The JBossSX Architecture


The preceding discussion of the general JBoss security layer states that the JBossSX security extension framework is an implementation of the security layer interfaces. This is the primary purpose of the JBossSX framework. The details of the implementation are interesting in that the implementation offers a great deal of customization for integration into existing security infrastructures. A security infrastructure can be anything from a database or an LDAP server to a sophisticated security software suite. The integration flexibility is achieved by using the pluggable authentication model that is available in the JAAS framework.

The heart of the JBossSX framework is org.jboss.security.plugins.JaasSecurityManager. This is the default implementation of the AuthenticationManager and RealmMapping interfaces. Figure 8.11 shows how JaasSecurityManager integrates into the EJB and web container layers, based on the security-domain element of the corresponding component deployment descriptor.

Figure 8.11. The relationship between the security-domain component deployment descriptor value, the component container, and JaasSecurityManager.


Figure 8.11 depicts an enterprise application that contains both EJBs and web content, secured under the security domain jwdomain. The EJB and web containers have a request interceptor architecture that includes a security interceptor, which enforces the container security model. At deployment time, the security-domain element value in the jboss.xml and jboss-web.xml descriptors is used to obtain the security manager instance associated with the container. The security interceptor then uses the security manager to perform its role. When a secured component is requested, the security interceptor delegates security checks to the security manager instance associated with the container.

The JBossSX JaasSecurityManager implementation performs security checks based on the information associated with the Subject instance that results from executing the JAAS login modules configured under the name that matches the security-domain element value. The following section drills into the JaasSecurityManager implementation and its use of JAAS.

How JaasSecurityManager Uses JAAS

JaasSecurityManager uses the JAAS packages to implement the AuthenticationManager and RealmMapping interface behavior. In particular, its behavior derives from the execution of the login module instances that are configured under the name that matches the security domain to which JaasSecurityManager has been assigned. The login modules implement the security domain's principal authentication and role-mapping behavior. Thus, you can use the JaasSecurityManager across different security domains simply by plugging in different login module configurations for the domains.

To illustrate the details of JaasSecurityManager's usage of the JAAS authentication process, you will walk through a client invocation of an EJB home method invocation. The prerequisite setting is that the EJB has been deployed in the JBoss server, its home interface methods have been secured by using method-permission elements in the ejb-jar.xml descriptor, and it has been assigned a security domain named jwdomain, using the jboss.xml descriptor security-domain element.

Figure 8.12 provides a view of the client-to-server communication discussed in this section.

Figure 8.12. The steps involved in the authentication and authorization of a secured EJB home method invocation.


The following are the numbered steps shown in Figure 8.12:

1.

The client performs a JAAS login to establish the principal and credentials for authentication. This is how clients establish their login identities in JBoss. Support for presenting the login information via JNDI InitialContext properties is provided via an alternate configuration. A JAAS login entails creating a LoginContext instance and passing the name of the configuration to use. The configuration name is other. This one-time login associates the login principal and credentials with all subsequent EJB method invocations. Note that the process might not authenticate the user. The nature of the client-side login depends on the login module configuration that the client uses. In this example, the other client-side login configuration entry is set up to use the ClientLoginModule module (an org.jboss.security.ClientLoginModule). This is the default client-side module, and it simply binds the username and password to the JBoss EJB invocation layer for later authentication on the server. The identity of the client is not authenticated on the client.

2.

The client obtains the EJB home interface and attempts to create a bean. This results in a home interface method invocation being sent to the JBoss server. The invocation includes the method arguments passed by the client, along with the user identity and credentials from the client-side JAAS login performed in step 1.

3.

On the server side, the security interceptor first requires authentication of the user invoking the call. As on the client side, this involves a JAAS login.

4.

The security domain under which the EJB is secured determines the choice of login modules. The security domain name is used as the login configuration entry name passed to the LoginContext constructor. The EJB security domain is jwdomain. If the JAAS login authenticates the user, a JAAS Subject is created, and it contains the following in its PrincipalsSet:

  • A java.security.Principal that corresponds to the client identity, as known in the deployment security environment.

  • A java.security.acl.Group named Roles that contains the role names from the application domain to which the user is assigned. org.jboss.security.SimplePrincipal objects are used to represent the role names; SimplePrincipal is a simple string-based implementation of Principal. These roles are used to validate the roles assigned to methods in ejb-jar.xml and in the EJBContext.isCallerInRole(String) method implementation.

  • An optional java.security.acl.Group named CallerPrincipal, which contains a single org.jboss.security.SimplePrincipal that corresponds to the identity of the application domain's caller. The CallerPrincipal sole group member will be the value returned by the EJBContext.getCallerPrincipal() method. The purpose of this mapping is to allow a Principal, as known in the operational security environment, to map to a Principal with a name known to the application. In the absence of a CallerPrincipal mapping, the deployment security environment principal is used as the get-CallerPrincipal method value. That is, the operational principal is the same as the application domain principal.

The final step of the security interceptor check is to verify that the authenticated user has permission to invoke the requested method. Performing the authorization entails the following processes:

  • Obtain the names of the roles allowed to access the EJB method from the EJB container. The role names are determined by ejb-jar.xml descriptor role-name elements of all method-permission elements that contain the invoked method.

  • If no roles have been assigned, or if the method is specified in an exclude-list element, access to the method is denied. Otherwise, the doesUserHaveRole method is invoked on the security manager by the security interceptor to see if the caller has one of the assigned role names. This method iterates through the role names and checks whether the authenticated user's Subject Roles group contains a SimplePrincipal with the assigned role name. Access is allowed if any role name is a member of the Roles group. Access is denied if none of the role names are members.

  • If the EJB was configured with a custom security proxy, the method invocation is delegated to it. If the security proxy wants to deny access to the caller, it throws a java.lang.SecurityException. If no SecurityException is thrown, access to the EJB method is allowed, and the method invocation passes to the next container interceptor. Note that the SecurityProxyInterceptor handles this check, and this interceptor is not shown in Figure 8.12.

Every secured EJB method invocation or secured web content access requires the authentication and authorization of the caller because security information is handled as a stateless attribute of the request that must be presented and validated on each request. This can be an expensive operation if the JAAS login involves client-to-server communication. Therefore, JaasSecurityManager supports the notion of an authentication cache that is used to store principal and credential information from previous successful logins. You can specify the authentication cache instance to use as part of the JaasSecurityManager configuration, as you will see in the following section. In the absence of any user-defined cache, a default cache that maintains credential information for a configurable period of time is used.

The JaasSecurityManagerService MBean

The JaasSecurityManagerService MBean service manages security managers. Although its name begins with Jaas, the security managers it handles need not use JAAS in their implementation. The name arose from the fact that the default security manager implementation is JaasSecurityManager. The primary role of JaasSecurityManagerService is to externalize the security manager implementation. You can change the security manager implementation by providing an alternate implementation of the AuthenticationManager and RealmMapping interfaces.

The second fundamental role of JaasSecurityManagerService is to provide a JNDI javax.naming.spi.ObjectFactory implementation to allow for simple code-free management of the JNDI name-to-security manager implementation mapping. As mentioned previously, you enable security by specifying the JNDI name of the security manager implementation via the security-domain deployment descriptor element. When you specify a JNDI name, there has to be an object-binding to use. To simplify the setup of the JNDI name-to-security manager bindings, JaasSecurityManagerService manages the association of security manager instances to names by binding the next naming system reference with itself as the JNDI ObjectFactory under the name java:/jaas. This allows you to use a naming convention in the form java:/jaas/XYZ as the value for the security-domain element, and the security manager instance for the XYZ security domain will be created as needed for you. The security manager for the domain XYZ is created on the first lookup against the java:/jaas/XYZ binding by creating an instance of the class specified by the SecurityManagerClassName attribute, using a constructor that takes the name of the security domain. For example, consider the following container security configuration snippet:

 <jboss>     <!-- Configure all containers to be secured under the "hades" security domain -->     <security-domain>java:/jaas/hades</security-domain>     <!-- ... --> </jboss> 

Any lookup of the name java:/jaas/hades will return a security manager instance that has been associated with the security domain named hades. This security manager will implement the AuthenticationManager and RealmMapping security interfaces and will be of the type specified by the JaasSecurityManagerService SecurityManagerClassName attribute.

The JaasSecurityManagerService MBean is configured by default for use in the standard JBoss distribution, and you can often use the default configuration as is. The configurable attributes of the JaasSecurityManagerService include the following:

  • SecurityManagerClassName The name of the class that provides the security manager implementation. The implementation must support both the org.jboss.security.AuthenticationManager and org.jboss.security.RealmMapping interfaces. If not specified, this defaults to the JAAS-based org.jboss.security.plugins.JaasSecurityManager.

  • CallbackHandlerClassName The name of the class that provides the javax.security.auth.callback.CallbackHandler implementation used by JaasSecurityManager. You can override the handler used by JaasSecurityManager if the default implementation (org.jboss.security.auth.callback.SecurityAssociationHandler) does not meet your needs. This is a rather deep configuration that you generally should not set unless you know what you are doing.

  • SecurityProxyFactoryClassName The name of the class that provides the org.jboss.security.SecurityProxyFactory implementation. If not specified, this defaults to org.jboss.security.SubjectSecurityProxyFactory.

  • AuthenticationCacheJndiName The location of the security credential cache policy. This is first treated as an ObjectFactory location that is capable of returning CachePolicy instances on a per-security-domain basis. This is done by appending the name of the security domain to this name when looking up the CachePolicy for a domain. If that fails, the location is treated as a single CachePolicy for all security domains. As a default, a timed cache policy is used.

  • DefaultCacheTimeout The default timed cache policy timeout, in seconds. The default value is 1800 (30 minutes). The value you use for the timeout is a tradeoff between frequent authentication operations and how long credential information may be out of sync with respect to the security information store. If you want to disable caching of security credentials, you can set this to 0 to force authentication to occur every time. This has no effect if AuthenticationCacheJndiName has been changed from the default value.

  • DefaultCacheResolution The default timed cache policy resolution, in seconds. This controls the interval at which the cache current timestamp is updated and should be less than DefaultCacheTimeout in order for the timeout to be meaningful. The default resolution is 60 (1 minute). This has no effect if AuthenticationCacheJndiName has been changed from the default value.

  • DefaultUnauthenticatedPrincipal The principal to use for unauthenticated users. This setting makes it possible to set default permissions for users who have not been authenticated.

JaasSecurityManagerService also supports a number of useful operations. These include flushing any security domain authentication cache at runtime, getting the list of active users in a security domain authentication cache, and using any of the security manager interface methods.

You can flush a security domain authentication cache in order to drop all cached credentials when the underlying store has been updated and you want the store state to be used immediately. The MBean operation signature is public void flushAuthenticationCache(String securityDomain). The following code invokes this operation:

 MBeanServer server = ...; String jaasMgrName = "jboss.security:service=JaasSecurityManager"; ObjectName jaasMgr = new ObjectName(jaasMgrName); Object[] params = {domainName}; String[] signature = {"java.lang.String"}; server.invoke(jaasMgr, "flushAuthenticationCache", params, signature); 

Getting the list of active users provides a snapshot of the unexpired Principals keys in a security domain authentication cache. The MBean operation signature is public List getAuthenticationCachePrincipals(String securityDomain). The following code invokes this operation:

 MBeanServer server = ...; String jaasMgrName = "jboss.security:service=JaasSecurityManager"; ObjectName jaasMgr = new ObjectName(jaasMgrName); Object[] params = {domainName}; String[] signature = {"java.lang.String"}; server.invoke(jaasMgr, "flushAuthenticationCache", params, signature); 

The security manager has a few additional access methods:

 public boolean isValid(String securityDomain, Principal principal, Object credential); public Principal getPrincipal(String securityDomain, Principal principal); public boolean doesUserHaveRole(String securityDomain, Principal principal,                                 Object credential, Set roles); public Set getUserRoles(String securityDomain, Principal principal, Object credential); 

These access methods provide access to the corresponding AuthenticationManager and RealmMapping interface method of the associated security domain named by the securityDomain argument.

The JaasSecurityDomain MBean

org.jboss.security.plugins.JaasSecurityDomain is an extension of JaasSecurityManager that adds the notion of a KeyStore, a JSSE KeyManagerFactory, and a TRustManagerFactory for supporting SSL and other cryptographic use cases. The additional configurable attributes of the JaasSecurityDomain include the following:

  • KeyStoreType The type of the KeyStore implementation. This is the type argument that is passed to the java.security.KeyStore.getInstance(String type) factory method.The default is JKS.

  • KeyStoreURL A URL to the location of the KeyStore database. This is used to obtain an InputStream to initialize the KeyStore. If the string is not a value URL, it is treated as a file.

  • KeyStorePass The password associated with the KeyStore database contents. The KeyStorePass is also used in combination with the Salt and IterationCount attributes to create a PBE secret key that is used with the encode/decode operations. The KeyStorePass attribute value format is one of the following:

    • The plaintext password for the KeyStore The toCharArray() value of the string is used without any manipulation.

    • A command to execute to obtain the plaintext password The format is {EXT}... where the ... is the exact command line that will be passed to the Runtime.exec(String) method to execute a platform-specific command. The first line of the command output is used as the password.

    • A class to create to obtain the plaintext password The format is {CLASS}classname[:ctorarg] where [:ctorarg] is an optional string that will be passed to the constructor when instantiating classname. The password is obtained from classname by invoking a toCharArray() method, if found; otherwise, the toString() method is used.

  • Salt The PBEParameterSpec salt value.

  • IterationCount The PBEParameterSpec iteration count value.

  • trustStoreType The type of the trustStore implementation. This is the type argument that is passed to the java.security.KeyStore.getInstance(String type) factory method. The default is JKS.

  • trustStoreURL A URL to the location of the trustStore database. This is used to obtain an InputStream to initialize the KeyStore. If the string is not a value URL, it is treated as a file.

  • TRustStorePass The password associated with the trust store database contents. trustStorePass is a simple password and doesn't have the same configuration options as KeyStorePass.

  • ManagerServiceName The JMX object name string of the security manager service MBean. This is used to register the defaults to register the JaasSecurityDomain as the security manager under java:/jaas/<domain>, where <domain> is the name that is passed to the MBean constructor. The name defaults to jboss.security:service=JaasSecurityManager.

An XML JAAS Login Configuration MBean

XMLLoginConfig is a service that loads standard JAAS application configurations from a local configuration file. This MBean supports the following attributes:

  • ConfigURL This attribute specifies the URL of the XML login configuration file that this MBean should load on startup. This must be a valid URL string representation.

  • ConfigResource This attribute specifies the resource name of the XML login configuration file that this MBean should load on startup. The name is treated as a classpath resource for which a URL is located, using the thread context class loader.

  • ValidateDTD This flag indicates whether the XML configuration should be validated against its DTD. This defaults to true.

The XML configuration file conforms to the DTD shown in Figure 8.13. This DTD can be found in docs/dtd/security_config.dtd.

Figure 8.13. The XMLLoginConfig DTD.


The name attribute of application-policy is the login configuration name. This corresponds to the portion of the jboss.xml and jboss-web.xml security-domain element value after the java:/jaas/ prefix. The code attribute of the login-module element specifies the classname of the login module implementation. The flag attribute controls the overall behavior of the authentication stack. The allowed values and meanings are as follows:

  • required The LoginModule is required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.

  • requisite The LoginModule is required to succeed. If it succeeds, authentication continues down the LoginModule list. If it fails, control immediately returns to the application (authentication does not proceed down the LoginModule list).

  • sufficient The LoginModule is not required to succeed. If it does succeed, control immediately returns to the application (authentication does not proceed down the LoginModule list). If it fails, authentication continues down the LoginModule list.

  • optional The LoginModule is not required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.

Zero or more module-option elements may be specified as child elements of a loginmodule. These define name/value string pairs that are made available to the login module during initialization. The name attribute specifies the option name, and the module-option body provides the value. An example of a login configuration is shown in Listing 8.10.

Listing 8.10. A Sample Login Module Configuration Suitable for Use with XMLLoginConfig

[View full width]

 <policy>     <application-policy name="srp-test">         <authentication>             <login-module code="org.jboss.security.srp.jaas.SRPCacheLoginModule"                           flag="required">                 <module-option name="cacheJndiName">srp-test/AuthenticationCache< /module-option>             </login-module>             <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"                              flag="required">                 <module-option name="password-stacking">useFirstPass</module-option>             </login-module>         </authentication>     </application-policy> </policy> 

The MBean also supports the following operations that allow you to dynamically extend the login configurations at runtime. Note that any operation that attempts to alter the login configuration requires a javax.security.auth.AuthPermission ("refreshLoginConfiguration") when running with a security manager. The org.jboss.chap8.service.SecurityConfig service demonstrates how you can use this to add/remove a deployment-specific security configuration dynamically:

  • void addAppConfig(String appName,AppConfigurationEntry[] entries) This adds the given login module configuration stack to the current configuration, under the given appName. This replaces any existing entry under that name.

  • void removeAppConfig(String appName) This removes the login module configuration registered under the given appName.

  • String[] loadConfig(URL configURL) throws Exception This loads one or more login configurations from a URL that represents either an XML or a legacy Sun login configuration file. Note that all login configurations must be added, or none will be added. It returns the names of the login configurations that were added.

  • void removeConfigs(String[] appNames) This removes the login configuration's specified appNames array.

  • String displayAppConfig(String appName) This operation displays a simple string format of the named configuration, if it exists.

The JAAS Login Configuration Management MBean

The installation of the custom javax.security.auth.login.Configuration is managed by the org.jboss.security.plugins.SecurityConfig MBean. There is one configurable attribute:

  • LoginConfig This attribute specifies the JMX ObjectName string of the MBean that provides the default JAAS login configuration. When the SecurityConfig is started, you query this MBean for its javax.security.auth.login.Configuration by calling its getConfiguration(Configuration currentConfig) operation. If the LoginConfig attribute is not specified, the default Sun Configuration implementation that is described in the Configuration class JavaDocs is used.

In addition to allowing for a custom JAAS login configuration implementation, this service allows configurations to be chained together in a stack at runtime. This enables you to push a login configuration onto the stack and later pop it. The security unit tests use this feature to install custom login configurations into a default JBoss installation. Pushing a new configuration is done using the following:

 public void pushLoginConfig(String objectName)      throws JMException,             MalformedObjectNameException; 

The objectName parameter specifies an MBean similar to the LoginConfig attribute. You can remove the current login configuration by using the following:

 public void popLoginConfig()     throws JMException; 

Using and Writing JBossSX Login Modules

The JaasSecurityManager implementation allows complete customization of the authentication mechanism, using JAAS login module configurations. By defining the login module configuration entry that corresponds to the security domain name you have used to secure access to your J2EE components, you define the authentication mechanism and integration implementation.

The JBossSX framework includes a number of bundled login modules that are suitable for integration with standard security infrastructure store protocols, such as LDAP and JDBC. It also includes standard base class implementations that help enforce the expected LoginModule-to-Subject usage pattern that is described later in this chapter, in the section "Writing Custom Login Modules." These implementations allow for easy integration of your own authentication protocol if none of the bundled login modules prove suitable. The following sections first describe the useful bundled login modules and their configuration, and then discuss how to create your own custom LoginModule implementations for use with JBoss.

org.jboss.security.auth.spi.IdentityLoginModule IdentityLoginModule is a simple login module that associates the principal specified in the module options with any subject authenticated against the module. It creates a SimplePrincipal instance, using the name specified by the principal option. Although this is certainly not an appropriate login module for production-strength authentication, it can be of use in development environments when you want to test the security associated with a given principal and associated roles.

The supported login module configuration options include the following:

  • principal This is the name to use for the SimplePrincipal that all users are authenticated as. The principal name defaults to guest if no principal option is specified.

  • roles This is the names of the roles that will be assigned to the user principal. The value is a comma-delimited list of role names.

  • password-stacking When the password-stacking option is set to useFirstPass, this module first looks for a shared username under the property name javax.security.auth.login.name in the login module's shared state map. If that is found, this is used as the principal name. If it is not found, the principal name set by this login module is stored under the property name javax.security.auth.login.name.

The following is a sample XMLLoginConfig configuration entry that would authenticate all users as the principal named jduke and assign the role names TheDuke and AnimatedCharacter:

 <policy>     <application-policy name="testIdentity">         <authentication>             <login-module code="org.jboss.security.auth.spi.IdentityLoginModule"                          flag="required">                 <module-option name="principal">jduke</module-option>                 <module-option name="roles">TheDuke,AnimatedCharater </module-option>             </login-module>         </authentication>     </application-policy> </policy> 

org.jboss.security.auth.spi.UsersRolesLoginModule UsersRolesLoginModule is a simple login module that supports multiple users and user roles loaded from Java properties files. The username-to-password mapping file is called users.properties, and the username-to-roles mapping file is called roles.properties. The properties files are loaded during initialization, using the initialize method thread context class loader. This means that these files can be placed into the J2EE deployment JAR, the JBoss configuration directory, or any directory on the JBoss server or system classpath. The primary purpose of this login module is to easily test the security settings of multiple users and roles, using properties files deployed with the application.

The users.properties file uses a username=password format with each user entry on a separate line, as shown here:

 username1=password1 username2=password2 ... 

The roles.properties file uses the username=role1,role2,... format, with an optional group name value. Here's an example:

 username1=role1,role2,... username1.RoleGroup1=role3,role4,... username2=role1,role3,... 

The username.XXX form of property name is used to assign the username roles to a particular named group of roles, where the XXX portion of the property name is the group name. The username=... form is an abbreviation for username.Roles=..., where the Roles group name is the standard name the JaasSecurityManager expects to contain the roles that define the user's permissions.

The following would be equivalent definitions for the jduke username:

 jduke=TheDuke,AnimatedCharacter jduke.Roles=TheDuke,AnimatedCharacter 

The supported login module configuration options include the following:

  • unauthenticatedIdentity This option defines the principal name that should be assigned to requests that contain no authentication information. You can use this option to allow unprotected servlets to invoke methods on EJBs that do not require a specific role. Such a principal has no associated roles and so can only access either unsecured EJBs or EJB methods that are associated with the unchecked permission constraint.

  • password-stacking When this option is set to useFirstPass, this module first looks for a shared username and password under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively, in the login module shared state map. If these are found, they are used as the principal name and password. If they are not found, the principal name and password are set by this login module and stored under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively.

  • hashAlgorithm This option specifies the name of the java.security.MessageDigest algorithm to use to hash the password. There is no default, so this option must be specified to enable hashing. When hashAlgorithm is specified, the clear-text password obtained from callbackhandler is hashed before it is passed to UsernamePasswordLoginModule.validatePassword as the inputPassword argument. The expectedPassword, as stored in the users.properties file, must be comparably hashed.

  • hashEncoding The string format for the hashed pass and must be either base64 or hex. base64 is the default.

  • hashCharset This option specifies the encoding used to convert the clear-text password to a byte array. The platform default encoding is the default.

  • usersProperties This option specifies the name of the properties resource that contains the username-to-password mappings. The default is users.properties.

  • rolesProperties This option specifies the name of the properties resource that contains the username-to-roles mappings. The default is roles.properties.

The following is a sample legacy XMLLoginConfig configuration entry that assigns unauthenticated users the principal name nobody and contains Base 64encoded MD5 hashes of the passwords in a usersb64.properties file:

 <policy>     <application-policy name="testUsersRoles">         <authentication>             <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"                           flag="required">                 <module-option name="usersProperties">usersb64.properties</module-option>                 <module-option name="hashAlgorithm">MD5</module-option>                 <module-option name="hashEncoding">base64</module-option>                 <module-option name="unauthenticatedIdentity">nobody </module-option>             </login-module>         </authentication>     </application-policy> </policy> 

org.jboss.security.auth.spi.LdapLoginModule LdapLoginModule is a LoginModule implementation that authenticates against an LDAP server, using JNDI login, using the login module configuration options. You would use LdapLoginModule if your username and credential information are stored in an LDAP server that is accessible via a JNDI LDAP provider.

The LDAP connectivity information is provided as configuration options that are passed through to the environment object used to create the JNDI initial context. The standard LDAP JNDI properties used include the following:

  • java.naming.factory.initial The classname of the InitialContextFactory implementation. This defaults to the Sun LDAP provider implementation com.sun.jndi.ldap.LdapCtxFactory.

  • java.naming.provider.url The LDAP URL for the LDAP server.

  • java.naming.security.authentication The security level to use. This defaults to simple.

  • java.naming.security.protocol The transport protocol to use for secure access, such as ssl.

  • java.naming.security.principal The principal for authenticating the caller to the service. This is built from other properties, as described later in this section.

  • java.naming.security.credentials The value of the property, which depends on the authentication scheme. For example, it could be a hashed password, clear-text password, key, certificate, and so on.

The supported login module configuration options include the following:

  • principalDNPrefix This option specifies a prefix to add to the username to form the user-distinguished name. See the following bullet for more information.

  • principalDNSuffix This option specifies a suffix to add to the username when forming the user-distinguished name. This is useful if you prompt a user for a username and you don't want the user to have to enter the fully distinguished name. By using this property and principalDNSuffix, the userDN will be formed as principalDNPrefix + username + principalDNSuffix.

  • useObjectCredential This option specifies a true/false value which indicates that the credential should be obtained as an opaque Object, using the org.jboss.security.auth.callback.ObjectCallback type of Callback rather than as a char[] password, using a JAAS PasswordCallback. This allows for passing non-char[] credential information to the LDAP server.

  • rolesCtxDN This option specifies the fixed distinguished name to the context to search for user roles.

  • userRolesCtxDNAttributeName This option specifies the name of an attribute in the user object that contains the distinguished name of the context to search for user roles. This differs from rolesCtxDN in that the context to search for a user's roles can be unique for each user.

  • roleAttributeID This option specifies the name of the attribute that contains the user roles. If not specified, this defaults to roles.

  • roleAttributeIsDN This option specifies a flag that indicates whether roleAttributeID contains the fully distinguished name of a role object, or the role name. If false, the role name is taken from the value of roleAttributeID. If true, the role attribute represents the distinguished name of a role object. The role name is taken from the value of the roleNameAttributeId attribute of the context name specified by the roleattributeId distinguished name. In certain directory schemas (for example, Microsoft Active Directory), role attributes in the user object are stored as DNs to role objects instead of as simple names, and in such a case, this property should be set to TRue. The default is false.

  • roleNameAttributeID This option specifies the name of the attribute of the context pointed to by the roleCtxDN distinguished name value, which contains the role name. If the roleAttributeIsDN property is set to TRue, this property is used to find the role object's name attribute. The default is group.

  • uidAttributeID This option specifies the name of the attribute in the object that contains the user roles that correspond to the user ID. This is used to locate the user roles. If not specified, it defaults to uid.

  • matchOnUserDN This option specifies a true/false flag that indicates whether the search for user roles should match the user's fully distinguished name. If false, just the username is used as the match value against the uidAttributeName attribute. If TRue, the full userDN is used as the match value.

  • unauthenticatedIdentity This option specifies the principal name that should be assigned to requests that contain no authentication information. This behavior is inherited from the UsernamePasswordLoginModule superclass.

  • password-stacking When the password-stacking option is set to useFirstPass, this module first looks for a shared username and password under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively, in the login module shared state map. If found, these are used as the principal name and password. If not found, the principal name and password are set by this login module and stored under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively.

  • allowEmptyPasswords This option specifies a flag that indicates whether if empty (length 0) passwords should be passed to the LDAP server. Some LDAP servers treat an empty password as an anonymous login, and this may not be a desirable feature. You set this to false to reject empty passwords or true to have the LDAP server validate the empty password. The default is TRue.

The authentication of a user is performed by connecting to the LDAP server, based on the login module configuration options. Connecting to the LDAP server is done by creating an InitialLdapContext with an environment composed of the LDAP JNDI properties described previously in this section. The Context.SECURITY_PRINCIPAL is set to the distinguished name of the user, as obtained by the callback handler in combination with the principalDNPrefix and principalDNSuffix option values, and the Context. SECURITY_CREDENTIALS property is either set to the String password or the Object credential, depending on the useObjectCredential option.

When authentication has succeeded by virtue of being able to create an InitialLdapContext instance, the user's roles are queried by performing a search on the rolesCtxDN location with search attributes set to the roleAttributeName and uidAttributeName option values. The role names are obtained by invoking the toString method on the role attributes in the search result set.

The following is a sample login-config.xml entry:

 <policy>     <application-policy name="testLDAP">         <authentication>             <login-module code="org.jboss.security.auth.spi.LdapLoginModule"                           flag="required">                 <module-option name="java.naming.factory.initial">                     com.sun.jndi.ldap.LdapCtxFactory                     </module-option>                 <module-option name="java.naming.provider.url">                     ldap://ldaphost.jboss.org:1389/                 </module-option>                 <module-option name="java.naming.security.authentication">                     simple                 </module-option>                 <module-option name="principalDNPrefix">uid=</module-option>                 <module-option name="principalDNSuffix">                     ,ou=People,dc=jboss,dc=org                 </module-option>                 <module-option name="rolesCtxDN">                     ou=Roles,dc=jboss,dc=org                 </module-option>                 <module-option name="uidAttributeID">member</module-option>                 <module-option name="matchOnUserDN">true</module-option>                 <module-option name="roleAttributeID">cn</module-option>                 <module-option name="roleAttributeIsDN">false </module-option>             </login-module>         </authentication>     </application-policy> </policy> 

An LDIF file representing the structure of the directory this data operates against is shown here:

 dn: dc=jboss,dc=org objectclass: top objectclass: dcObject objectclass: organization dc: jboss o: JBoss dn: ou=People,dc=jboss,dc=org objectclass: top objectclass: organizationalUnit ou: People dn: uid=jduke,ou=People,dc=jboss,dc=org objectclass: top objectclass: uidObject objectclass: person uid: jduke cn: Java Duke sn: Duke userPassword: theduke dn: ou=Roles,dc=jboss,dc=org objectclass: top objectclass: organizationalUnit ou: Roles dn: cn=JBossAdmin,ou=Roles,dc=jboss,dc=org objectclass: top objectclass: groupOfNames cn: JBossAdmin member: uid=jduke,ou=People,dc=jboss,dc=org description: the JBossAdmin group 

If you look back at the testLDAP login module configuration, you see that the java.naming.factory.initial, java.naming.factory.url, and java.naming.security options indicate that the Sun LDAP JNDI provider implementation will be used, the LDAP server is located on host ldaphost.jboss.org on port 1389, and the LDAP simple authentication method will be use to connect to the LDAP server.

The login module attempts to connect to the LDAP server by using a DN that represents the user it is trying to authenticate. This DN is constructed from the principalDNPrefix, passed in the username of the user, and the principalDNSuffix, as described earlier in this chapter. In this example, the username jduke would map to uid=jduke, ou=People,dc=jboss,dc=org. Here we assume that the LDAP server authenticates users by using the user-Password attribute of the user's entry (theduke, in this example). This is the way most LDAP servers work; however, if your LDAP server handles authentication differently, you need to set the authentication credentials in a way that makes sense for your server.

When authentication succeeds, you retrieve the roles on which authorization will be based by performing a subtree search of the rolesCtxDN for entries whose uidAttributeID matches the user. If matchOnUserDN is true, the search is based on the full DN of the user. Otherwise, the search is based on the actual username entered. In this example, the search is under ou=Roles,dc=jboss,dc=org for any entries that have a member attribute equal to uid=jduke,ou=People,dc=jboss,dc=org. The search would locate cn=JBossAdmin under the roles entry.

The search returns the attribute specified in the roleAttributeID option. In this example, the attribute is cn. The value returned would be JBossAdmin, so the jduke user is assigned to the JBossAdmin role.

It's often the case that a local LDAP server provides identity and authentication services but is unable to use the authorization services. This is because application roles don't always map well to LDAP groups, and LDAP administrators are often hesitant to allow external application-specific data in central LDAP servers. For this reason, the LDAP authentication module is often paired with another login module, such as the database login module, that can provide roles more suitable to the application being developed.

org.jboss.security.auth.spi.DatabaseServerLoginModule DatabaseServerLoginModule is a JDBC-based login module that supports authentication and role mapping. You would use this login module if you have your username, password, and role information in a relational database. DatabaseServerLoginModule is based on two logical tables:

 Table Principals(PrincipalID text, Password text) Table Roles(PrincipalID text, Role text, RoleGroup text) 

The Principals table associates the user PrincipalID with the valid password, and the Roles table associates the user PrincipalID with its role sets. The roles used for user permissions must be contained in rows, with a RoleGroup column value of Roles. The tables are logical in that you can specify the SQL query that the login module uses. All that is required is that the java.sql.ResultSet has the same logical structure as the Principals and Roles tables described previously. The actual names of the tables and columns are not relevant because the results are accessed based on the column index. To clarify this notion, consider a database with two tables, Principals and Roles, as already declared. The following statements build the tables to contain a PrincipalID java with the Password of echoman in the Principals table, a PrincipalID java with a role named Echo in the Roles RoleGroup in the Roles table, and a PrincipalID java with a role named caller_java in the CallerPrincipal RoleGroup in the Roles table:

 INSERT INTO Principals VALUES('java', 'echoman') INSERT INTO Roles VALUES('java', 'Echo', 'Roles') INSERT INTO Roles VALUES('java', 'caller_java', 'CallerPrincipal') 

The supported login module configuration options include the following:

  • dsJndiName This option specifies the JNDI name for the DataSource of the database that contains the logical Principals and Roles tables. If not specified, this defaults to java:/DefaultDS.

  • principalsQuery This option specifies the prepared statement query equivalent to select Password from Principals where PrincipalID=?. If not specified, this is exactly the prepared statement that will be used.

  • rolesQuery This option specifies the prepared statement query equivalent to select Role, RoleGroup from Roles where PrincipalID=?. If not specified, this is exactly the prepared statement that will be used.

  • unauthenticatedIdentity This option specifies the principal name that should be assigned to requests that contain no authentication information.

  • password-stacking When this option is set to useFirstPass, this module first looks for a shared username and password under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively, in the login module shared state map. If found, these are used as the principal name and password. If not found, the principal name and password are set by this login module and stored under the property names javax.security.auth.login.name and javax.security.auth.login.password, respectively.

  • hashAlgorithm This option specifies the name of the java.security.MessageDigest algorithm to use to hash the password. There is no default, so this option must be specified to enable hashing. When hashAlgorithm is specified, the clear-text password obtained from callbackhandler is hashed before it is passed to UsernamePasswordLoginModule.validatePassword as the inputPassword argument. The expectedPassword, as obtained from the database, must be comparably hashed.

  • hashEncoding This option specifies the string format for the hashed pass and must be either base64 or hex. base64 is the default.

  • hashCharset This option specifies the encoding used to convert the clear-text password to a byte array. The platform default encoding is the default.

  • ignorePasswordCase This option specifies a Boolean flag that indicates whether the password comparison should ignore case. This can be useful for hashed password encoding where the case of the hashed password is not significant.

  • principalClass This option specifies a Principal implementation class. This must support a constructor that takes a string argument for the principal name.

As an example of a DatabaseServerLoginModule configuration, consider a custom table schema like the following:

 CREATE TABLE Users(username VARCHAR(64) PRIMARY KEY, passwd VARCHAR(64)) CREATE TABLE UserRoles(username VARCHAR(64), userRoles VARCHAR(32)) 

A corresponding login-config.xml entry would look like this:

 <policy>     <application-policy name="testDB">         <authentication>             <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"                              flag="required">                 <module-option name="dsJndiName">java:/MyDatabaseDS</module-option>                 <module-option name="principalsQuery">                     select passwd from Users username where username=?</module-option>                 <module-option name="rolesQuery">                     select userRoles, 'Roles' from UserRoles where username=?</module-option>             </login-module>         </authentication>     </application-policy> </policy> 

BaseCertLoginModule BaseCertLoginModule is a login module that authenticates users based on X509 certificates. A typical use case for this login module is CLIENT-CERT authentication in the web tier. This login module only performs authentication. You need to combine it with another login module that is capable of acquiring the authorization roles to completely define access to a secured web or EJB component. Two subclasses of this login module, CertRolesLoginModule and Database-CertLoginModule, extend its behavior to obtain the authorization roles from either a properties file or database.

BaseCertLoginModule needs a KeyStore to perform user validation. This is obtained through an org.jboss.security.SecurityDomain implementation. Typically, the SecurityDomain implementation is configured using the org.jboss.security.plugins.JaasSecurityDomain MBean, as shown in this jboss-service.xml configuration fragment:

 <mbean code="org.jboss.security.plugins.JaasSecurityDomain"           name="jboss.web:service=SecurityDomain">      <constructor>           <arg type="java.lang.String" value="jmx-console"/>      </constructor>      <attribute name="KeyStoreURL">resource:localhost.keystore</attribute>      <attribute name="KeyStorePass">unit-tests-server</attribute> </mbean> 

This creates a security domain with the name jmx-console whose SecurityDomain implementation is available via JNDI under the name java:/jaas/jmx-console, following the JBossSX security domain naming pattern. To secure a web application such as the jmx-console.war by using client certificates and role-based authorization, you would first modify web.xml to declare the resources to be secured, along with the allowed roles and security domain to be used for authentication and authorization, as shown here:

 <?xml version="1.0"?> <!DOCTYPE web-app PUBLIC                   "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"                   "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app>     ...     <security-constraint>         <web-resource-collection>             <web-resource-name>HtmlAdaptor</web-resource-name>             <description>An example security config that only allows users with                 the role JBossAdmin to access the HTML JMX console web                 application </description>             <url-pattern>/*</url-pattern>             <http-method>GET</http-method>             <http-method>POST</http-method>         </web-resource-collection>         <auth-constraint>             <role-name>JBossAdmin</role-name>         </auth-constraint>     </security-constraint>     <login-config>         <auth-method>CLIENT-CERT</auth-method>         <realm-name>JBoss JMX Console</realm-name>     </login-config>     <security-role>         <role-name>JBossAdmin</role-name>     </security-role> </web-app> 

Next, you need to specify the JBoss security domain in jboss-web.xml:

 <jboss-web>     <security-domain>java:/jaas/jmx-console</security-domain> </jboss-web> 

Finally, you need to define the login module configuration for the jmx-console security domain you just specified. You do this in the conf/login-config.xml file:

 <application-policy name="jmx-console">     <authentication>         <login-module code="org.jboss.security.auth.spi.BaseCertLoginModule"                       flag="required">             <module-option name="password-stacking">useFirstPass</module-option>             <module-option name="securityDomain">java:/jaas/jmx-console</module-option>         </login-module>         <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"                       flag="required">             <module-option name="password-stacking">useFirstPass</module-option>             <module-option name="usersProperties">jmx-console-users.properties</module-option>             <module-option name="rolesProperties">jmx-console-roles.properties</module-option>         </login-module>     </authentication> </application-policy> 

Here BaseCertLoginModule is used for authentication of the client certificate, and UsersRolesLoginModule is only used for authorization due to the password-stacking=useFirstPass option. Both localhost.keystore and jmx-console-roles.properties need an entry that maps to the principal associated with the client certificate. By default, the principal is created using the client certificate distinguished name. Consider the following certificate:

 [starksm@banshee9100 conf]$ keytool -printcert -file unit-tests-client.export Owner: CN=unit-tests-client, OU=JBoss Inc., O=JBoss Inc., ST=Washington, C=US Issuer: CN=jboss.com, C=US, ST=Washington, L=Snoqualmie Pass, EMAILADDRESS=admin @jboss.com, OU=QA, O=JBoss Inc. Serial number: 100103 Valid from: Wed May 26 07:34:34 PDT 2004 until: Thu May 26 07:34:34 PDT 2005 Certificate fingerprints:          MD5:  4A:9C:2B:CD:1B:50:AA:85:DD:89:F6:1D:F5:AF:9E:AB          SHA1: DE:DE:86:59:05:6C:00:E8:CC:C0:16:D3:C2:68:BF:95:B8:83:E9:58 

localhost.keystore would need this certificate to be stored with an alias of CN=unit-tests-client,OU=JBoss Inc.,O=JBoss Inc.,ST=Washington,C=US, and jmx-console-roles.properties would also need an entry for the same entry. Because the DN contains many characters that are normally treated as delimiters, you need to escape the problem characters with a backslash (\), as shown here:

 # A sample roles.properties file for use with the UsersRolesLoginModule CN\=unit-tests-client,\ OU\=JBoss\ Inc.,\ O\=JBoss\ Inc.,\ ST\=Washington,\ C\=US=JBossAdmin admin=JBossAdmin 

org.jboss.security.auth.spi.RunAsLoginModule JBoss has a helper login module called RunAsLoginModule that pushes a run-as role for the duration of the login phase of authentication, and it pops the run-as role in either the commit or abort phase. The purpose of this login module is to provide a role for other login modules that need to access secured resources in order to perform their authentication. An example is a login module that accesses a secured EJB; this login module must be configured ahead of the login module(s) that needs a run-as role established.

The only login module configuration option is roleName, which is the name of the role to use as the run-as role during the login phase. If not specified, the default of nobody is used.

org.jboss.security.ClientLoginModule ClientLoginModule is an implementation of LoginModule for use by JBoss clients for the establishment of the caller identity and credentials. This simply sets org.jboss.security.SecurityAssociation.principal to the value of the NameCallback filled in by the callbackhandler, and it sets org.jboss.security.SecurityAssociation.credential to the value of the PasswordCallback filled in by the callbackhandler. This is the only supported mechanism for a client to establish the current thread's caller. Both standalone client applications and server environments, acting as JBoss EJB clients where the security environment has not been configured to use JBossSX transparently, need to use ClientLoginModule. Of course, you could always set the org.jboss.security.SecurityAssociation information directly, but this is considered an internal API that is subject to change without notice.

Note that this login module does not perform any authentication. It merely copies the login information provided to it into the JBoss server EJB invocation layer for subsequent authentication on the server. If you need to perform client-side authentication of users, you need to configure another login module in addition to ClientLoginModule.

The supported login module configuration options include the following:

  • multi-threaded When this option is set to true, each login thread has its own principal and credential storage. This is useful in client environments where multiple user identities are active in separate threads. When it is TRue, each separate thread must perform its own login. When it is set to false, the login identity and credentials are global variables that apply to all threads in the VM. The default for this option is false.

  • password-stacking When this option is set to useFirstPass, this module first looks for a shared username and password, using javax.security.auth.login.name and javax.security.auth.login.password, respectively, in the login module shared state map. This allows a module configured prior to this one to establish a valid username and password that should be passed to JBoss. You use this option if you want to perform client-side authentication of clients by using some other login module, such as LdapLoginModule.

  • restore-login-identity When this option is TRue, the SecurityAssociation principal and credential seen on entry to the login() method are saved and restored on either abort or logout. When it is false (the default), the abort and logout simply clear the SecurityAssociation. A restore-login-identity of true is needed if you need to change identities and then restore the original caller identity.

A sample login configuration for ClientLoginModule is the default configuration entry found in the JBoss distribution client/auth.conf file, which is as follows:

 other {     // Put your login modules that work without jBoss here     // jBoss LoginModule     org.jboss.security.ClientLoginModule required;     // Put your login modules that need jBoss here }; 

Writing Custom Login Modules

If the login modules bundled with the JBossSX framework do not work with your security environment, you can write your own custom login module implementation that does.

Recall from the section "The JBossSX Architecture," earlier in this chapter, that the JaasSecurityManager expects a particular usage pattern of the Subject principals set. You need to understand the JAAS Subject class's information storage features and the expected usage of these features to be able to write a login module that works with the JaasSecurityManager. This section examines this requirement and introduces two abstract base LoginModule implementations that can help you implement your own custom login modules.

You can obtain security information associated with a Subject in six ways in JBoss, using the following methods:

 java.util.Set getPrincipals() java.util.Set getPrincipals(java.lang.Class c) java.util.Set getPrivateCredentials() java.util.Set getPrivateCredentials(java.lang.Class c) java.util.Set getPublicCredentials() java.util.Set getPublicCredentials(java.lang.Class c) 

For Subject identities and roles, JBossSX has selected the most natural choice: the principals' sets obtained via getPrincipals() and getPrincipals(java.lang.Class). The usage pattern is as follows:

  • User identities (username, Social Security number, employee ID, and so on) are stored as java.security.Principal objects in the Subject Principals set. The Principal implementation that represents the user identity must base comparisons and equality on the name of the principal. A suitable implementation is available as the org.jboss.security.SimplePrincipal class. You can add other Principal instances to the Subject Principals set as needed.

  • The assigned user roles are also stored in the Principals set, but they are grouped in named role sets, using java.security.acl.Group instances. The Group interface defines a collection of Principals and/or Groups, and it is a subinterface of java.security.Principal. Any number of role sets can be assigned to a Subject. Currently, the JBossSX framework uses two well-known role sets with the names Roles and CallerPrincipal. The Roles group is the collection of Principals for the named roles, as known in the application domain under which the Subject has been authenticated. This role set is used by methods such as EJBContext.isCallerInRole (String), which EJBs can use to see whether the current caller belongs to the named application domain role. The security interceptor logic that performs method permission checks also uses this role set. The CallerPrincipal Group consists of the single Principal identity assigned to the user in the application domain. The EJBContext.getCallerPrincipal() method uses the CallerPrincipal to allow the application domain to map from the operation environment identity to a user identity suitable for the application. If a Subject does not have a CallerPrincipal Group, the application identity is the same as the operational environment identity.

Support for the Subject Usage Pattern

To simplify correct implementation of the Subject usage patterns described in the preceding section, JBossSX includes two abstract login modules that handle the population of the authenticated Subject with a template pattern that enforces correct Subject usage. The most generic of the two is the org.jboss.security.auth.spi.AbstractServerLoginModule class. It provides a concrete implementation of the javax.security.auth.spi.LoginModule interface and offers abstract methods for the key tasks that are specific to an operation environment security infrastructure. The key details of the class are shown in the following class fragment, and the JavaDoc comments detail the responsibilities of subclasses:

 package org.jboss.security.auth.spi; /**  *  This class implements the common functionality required for a JAAS  *  server-side LoginModule and implements the JBossSX standard  *  Subject usage pattern of storing identities and roles. Subclass  *  this module to create your own custom LoginModule and override the  *  login(), getRoleSets(), and getIdentity() methods.  */ public abstract class AbstractServerLoginModule     implements javax.security.auth.spi.LoginModule {     protected Subject subject;     protected CallbackHandler callbackHandler;     protected Map sharedState;     protected Map options;     protected Logger log;     /** Flag indicating if the shared credential should be used */     protected boolean useFirstPass;     /**      * Flag indicating if the login phase succeeded. Subclasses that      * override the login method must set this to true on successful      * completion of login      */     protected boolean loginOk;     // ...     /**      * Initialize the login module. This stores the subject,      * callbackHandler and sharedState and options for the login      * session. Subclasses should override if they need to process      * their own options. A call to super.initialize(...) must be      * made in the case of an override.      *      * <p>      * The options are checked for the <em>password-stacking</em> parameter.      * If this is set to "useFirstPass", the login identity will be taken from the      * <code>javax.security.auth.login.name</code> value of the sharedState map,      * and the proof of identity from the      * <code>javax.security.auth.login.password</code> value of the sharedState map.      *      * @param subject the Subject to update after a successful login.      * @param callbackHandler the CallbackHandler that will be used to obtain      * the user identity and credentials.      * @param sharedState a Map shared between all configured login module instances      * @param options the parameters passed to the login module.      */     public void initialize(Subject subject,                            CallbackHandler callbackHandler,                            Map sharedState,                            Map options)     {         // ...     }     /**      *  Looks for javax.security.auth.login.name and      *  javax.security.auth.login.password values in the sharedState      *  map if the useFirstPass option was true and returns true if      *  they exist. If they do not or are null this method returns      *  false.      *  Note that subclasses that override the login method      *  must set the loginOk ivar to true if the login succeeds in      *  order for the commit phase to populate the Subject. This      *  implementation sets loginOk to true if the login() method      *  returns true, otherwise, it sets loginOk to false.      */     public boolean login()         throws LoginException     {         // ...     }     /**      *  Overridden by subclasses to return the Principal that      *  corresponds to the user primary identity.      */     abstract protected Principal getIdentity();     /**      *  Overridden by subclasses to return the Groups that correspond      *  to the role sets assigned to the user. Subclasses should      *  create at least a Group named "Roles" that contains the roles      *  assigned to the user.  A second common group is      *  "CallerPrincipal," which provides the application identity of      *  the user rather than the security domain identity.      *      *  @return Group[] containing the sets of roles      */     abstract protected Group[] getRoleSets() throws LoginException; } 

You need to pay attention to the loginOk instance variable. Any subclasses that override the login method must set this to true if the login succeeds and false otherwise. Failure to set this variable correctly results in the commit method either not updating the subject when it should or updating the subject when it should not. Tracking the outcome of the login phase was added to allow login modules to be chained together with control flags that do not require that the login module succeed in order for the overall login to succeed.

The second abstract base login module suitable for custom login modules is org.jboss.security.auth.spi.UsernamePasswordLoginModule. This login module further simplifies custom login module implementation by enforcing a string-based username as the user identity and a char[] password as the authentication credentials. It also supports the mapping of anonymous users (indicated by null username and password) to a principal with no roles. The key details of the class are shown in the following class fragment, and the JavaDoc comments detail the responsibilities of subclasses:

 package org.jboss.security.auth.spi; /**  *  An abstract subclass of AbstractServerLoginModule that imposes a  *  an identity == String username, credentials == String password  *  view on the login process. Subclasses override the  *  getUsersPassword() and getUsersRoles() methods to return the  *  expected password and roles for the user.  */ public abstract class UsernamePasswordLoginModule     extends AbstractServerLoginModule {     /** The login identity */     private Principal identity;     /** The proof of login identity */     private char[] credential;     /** The principal to use when a null username and password are seen */     private Principal unauthenticatedIdentity;     /**      * The message digest algorithm used to hash passwords. If null then      * plain passwords will be used. */     private String hashAlgorithm = null;     /**      *  The name of the charset/encoding to use when converting the      * password String to a byte array. Default is the platform's      * default encoding.      */      private String hashCharset = null;     /** The string encoding format to use. Defaults to base64. */     private String hashEncoding = null;     // ...     /**      *  Override the superclass method to look for an      *  unauthenticatedIdentity property. This method first invokes      *  the super version.      *      *  @param options,      *  @option unauthenticatedIdentity: the name of the principal to      *  assign and authenticate when a null username and password are      *  seen.      */     public void initialize(Subject subject,                            CallbackHandler callbackHandler,                            Map sharedState,                            Map options)     {         super.initialize(subject, callbackHandler, sharedState,                          options);         // Check for unauthenticatedIdentity option.         Object option = options.get("unauthenticatedIdentity");         String name = (String) option;         if (name != null) {             unauthenticatedIdentity = new SimplePrincipal(name);         }     }     // ...     /**      *  A hook that allows subclasses to change the validation of the      *  input password against the expected password. This version      *  checks that neither inputPassword or expectedPassword are null      *  and that inputPassword.equals(expectedPassword) is true;      *      *  @return true if the inputPassword is valid, false otherwise.      */     protected boolean validatePassword(String inputPassword,                                        String expectedPassword)     {         if (inputPassword == null || expectedPassword == null) {             return false;         }         return inputPassword.equals(expectedPassword);     }     /**      *  Get the expected password for the current username available      * via the getUsername() method. This is called from within the      * login() method after the CallbackHandler has returned the      * username and candidate password.      *      * @return the valid password String      */     abstract protected String getUsersPassword()         throws LoginException; } 

The choice of subclassing the AbstractServerLoginModule versus UsernamePasswordLoginModule is simply based on whether a string-based username and credentials are usable for the authentication technology you are writing the login module for. If the string-based semantic is valid, then you should subclass UsernamePasswordLoginModule; otherwise, you should subclass AbstractServerLoginModule.

The steps you are required to perform when writing a custom login module depend on which base login module class you choose. When writing a custom login module that integrates with your security infrastructure, you should start by subclassing AbstractServerLoginModule or UsernamePassword-LoginModule to ensure that your login module provides the authenticated Principal information in the form that the JBossSX security manager expects.

When subclassing AbstractServerLoginModule, you need to override the following:

  • void initialize(Subject,CallbackHandler,Map,Map) You override this if you have custom options to parse.

  • boolean login() You override this to perform the authentication activity. You need to be sure to set the loginOk instance variable to true if login succeeds and false if it fails.

  • Principal getIdentity() You override this to return the Principal object for the user authenticated by the log() step.

  • Group[]getRoleSets() You override this to return at least one Group named Roles that contains the roles assigned to the Principal authenticated during login(). A second common Group is named CallerPrincipal and provides the user's application identity rather than the security domain identity.

When subclassing the UsernamePasswordLoginModule, you need to override the following:

  • void initialize (Subject,CallbackHandler,Map,Map) You override this if you have custom options to parse.

  • Group[] getRoleSets() You override this to return at least one Group named Roles that contains the roles assigned to the Principal authenticated during login(). A second common Group is named CallerPrincipal and provides the user's application identity rather than the security domain identity.

  • String getUsersPassword() You override this to return the expected password for the current username available via the getUsername() method. The getUsersPassword() method is called from within login() after the callbackhandler returns the username and candidate password.

A Custom LoginModule Example

In this section, you will develop a custom login module example that extends the UsernamePasswordLoginModule and obtains a user's password and role names from a JNDI lookup. The idea is that there is a JNDI context that returns a user's password if you perform a lookup on the context by using a name in the form password/<username>, where <username> is the current user being authenticated. Similarly, a lookup in the form roles/<username> returns the requested user's roles.

The source code for the example is located in the src/main/org/jboss/chap8/ex2 directory of the book examples. Listing 8.11 shows the source code for the JndiUserAndPass custom login module. Note that because this extends the JBoss UsernamePasswordLoginModule, all the JndiUserAndPass does is obtain the user's password and roles from the JNDI store. The JndiUserAndPass does not concern itself with the JAAS LoginModule operations.

Listing 8.11. A JndiUserAndPass Custom Login Module
 package org.jboss.chap8.ex2; import java.security.acl.Group; import java.util.Map; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import org.jboss.security.SimpleGroup; import org.jboss.security.SimplePrincipal; import org.jboss.security.auth.spi.UsernamePasswordLoginModule; /**  *  An example custom login module that obtains passwords and roles  *  for a user from a JNDI lookup.  *  *  @author Scott.Stark@jboss.org  *  @version $Revision: 1.5 $ */ public class JndiUserAndPass     extends UsernamePasswordLoginModule {     /** The JNDI name to the context that handles the password/username lookup */     private String userPathPrefix;     /** The JNDI name to the context that handles the roles/ username lookup */     private String rolesPathPrefix;     /**      * Override to obtain the userPathPrefix and rolesPathPrefix options.      */     public void initialize(Subject subject, CallbackHandler callbackHandler,                            Map sharedState, Map options)     {         super.initialize(subject, callbackHandler, sharedState, options);         userPathPrefix = (String) options.get("userPathPrefix");         rolesPathPrefix = (String) options.get("rolesPathPrefix");     }     /**      *  Get the roles the current user belongs to by querying the      * rolesPathPrefix + '/' + super.getUsername() JNDI location.      */     protected Group[] getRoleSets() throws LoginException     {         try {             InitialContext ctx = new InitialContext();             String rolesPath = rolesPathPrefix + '/' + super.getUsername();             String[] roles = (String[]) ctx.lookup(rolesPath);             Group[] groups = {new SimpleGroup("Roles")};             log.info("Getting roles for user="+super.getUsername());             for(int r = 0; r < roles.length; r ++) {                 SimplePrincipal role = new SimplePrincipal(roles[r]);                 log.info("Found role="+roles[r]);                 groups[0].addMember(role);             }             return groups;         } catch(NamingException e) {             log.error("Failed to obtain groups for                         user="+super.getUsername(), e);             throw new LoginException(e.toString(true));         }     }     /**      * Get the password of the current user by querying the      * userPathPrefix + '/' + super.getUsername() JNDI location.      */     protected String getUsersPassword()         throws LoginException     {         try {             InitialContext ctx = new InitialContext();             String userPath = userPathPrefix + '/' + super.getUsername();             log.info("Getting password for user="+super.getUsername());             String passwd = (String) ctx.lookup(userPath);             log.info("Found password="+passwd);             return passwd;         } catch(NamingException e) {             log.error("Failed to obtain password for                         user="+super.getUsername(), e);             throw new LoginException(e.toString(true));         }     } } 

You can find the details of the JNDI store in the org.jboss.chap8.ex2.service.JndiStore MBean. This service binds an ObjectFactory that returns a javax.naming.Context proxy into JNDI. The proxy handles lookup by checking the prefix of the lookup name against password and roles. When the name begins with password, a user's password is being requested. When the name begins with roles, the user's roles are being requested. The sample implementation always returns the password theduke and an array of role names equal to {"TheDuke","Echo"}, regardless of the username. You can experiment with other implementations as you wish.

The example's code includes a simple session bean for testing the custom login module. To build, deploy, and run the example, you execute the following command in the examples directory:

 [examples]$ ant -Dchap=chap8 -Dex=2 run-example ... run-example2:      [copy] Copying 1 file to /tmp/jboss-4.0.1/server/default/deploy      [echo] Waiting for 5 seconds for deploy...      [java] [INFO,ExClient] Login with username=jduke, password=theduke      [java] [INFO,ExClient] Looking up EchoBean2      [java] [INFO,ExClient] Created Echo      [java] [INFO,ExClient] Echo.echo('Hello') = Hello 19:06:13,266 INFO  [EjbModule] Deploying EchoBean2 19:06:13,482 INFO  [JndiStore] Start, bound security/store 19:06:13,486 INFO  [SecurityConfig] Using JAAS AuthConfig: jar:file: /private/tmp/jboss-4.0.1/server/default/tmp/deploy/ tmp23012chap8-ex2.jar-contents/chap8-ex2.sar!/META-INF/login-config.xml 19:06:13,654 INFO  [EJBDeployer] Deployed: file:/private/tmp/jboss-4.0.1/ server/default/deploy/chap8-ex2.jar 

Whether to use the JndiUserAndPass custom login module for the server-side authentication of the user is determined based on the login configuration for the sample security domain. The EJB JAR META-INF/jboss.xml descriptor sets the security domain as follows:

 <?xml version="1.0"?> <jboss>     <security-domain>java:/jaas/chap8-ex2</security-domain> </jboss> 

The SAR META-INF/login-config.xml descriptor defines the login module configuration as follows:

 <application-policy name = "chap8-ex2">     <authentication>         <login-module code="org.jboss.chap8.ex2.JndiUserAndPass"                       flag="required">             <module-option name = "userPathPrefix">/security/store/password </module-option>             <module-option name = "rolesPathPrefix">/security/store/roles </module-option>         </login-module>     </authentication> </application-policy> 

The DynamicLoginConfig Service

Security domains defined in the login-config.xml file are essentially static. They are read when JBoss starts up, but there is no easy way to add a new security domain or change the definition for an existing one. The DynamicLoginConfig service allows you to dynamically deploy security domains. This allows you to specify JAAS login configuration as part of a deployment (or just as a standalone service) rather than having to edit the static login-config.xml file.

The service supports the following attributes:

  • AuthConfig The resource path to the JAAS login configuration file to use. This defaults to login-config.xml.

  • LoginConfigService The XMLLoginConfig service name to use for loading. This service must support a String loadConfig(URL) operation to load the configurations.

  • SecurityManagerService The SecurityManagerService name used to flush the registered security domains. This service must support a flushAuthenticationCache(String) operation to flush the case for the argument security domain. Setting this triggers the flush of the authentication caches when the service is stopped.

Here is an example of an MBean definition that uses the DynamicLoginConfig service:

 <server>     <mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="...">         <attribute name="AuthConfig">login-config.xml</attribute>         <!-- The service which supports dynamic processing of login-config.xml          configurations.         -->         <depends optional-attribute-name="LoginConfigService">             jboss.security:service=XMLLoginConfig </depends>         <!-- Optionally specify the security mgr service to use when          this service is stopped to flush the auth caches of the domains          registered by this service.         -->         <depends optional-attribute-name="SecurityManagerService">             jboss.security:service=JaasSecurityManager </depends>     </mbean> </server> 

This loads the specified AuthConfig resource, using the specified LoginConfigService MBean by invoking loadConfig with the appropriate resource URL. When the service is stopped, the configurations are removed. The resource that is specified may be either an XML file or a Sun JAAS login configuration.



JBoss 4. 0(c) The Official Guide
JBoss 4.0 - The Official Guide
ISBN: B003D7JU58
EAN: N/A
Year: 2006
Pages: 137

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net