Taylor

Taylor Identity

From Taylor

Taylor Identity is a reusable component for user and group maintenance.

Contents

Model

Taylor Identity has a basic model made up of users and groups and a many2many association between them.

Group represents two concepts:

  • Roles - defined at design time
  • Organizational Units - defined at run time

Roles

Roles are defined at design time as you model your application and determine the actors and stories.

Two base roles are defined:

  • User - The root role used for all users
  • Admin - The role used for administrators

Two initial users are created if these roles have not already been created:

  • user/user
  • admin/admin

Use a Seam postInitialization listener to initialize your application's roles.

	@Observer("org.jboss.seam.postInitialization")
	@Transactional
	public void initRoles() {
		IdentityUtil.addGroup(identityEntityManager, 
			"Reviewer", "Reviewers",
			"The role for users who review tickets.");
	}

Profile

Several facelets are provided in addition to the User and Group maintenance screens.

  • Profile - For the current user to maintain their information
  • Register - For a new user to create a profile
  • Provisioning (future) - a business process triggered by registration to task administrators with assigning additional roles
  • Initial/Reset Password - set to changeme
    • Obviously there is room for improvement here...
    • However, you will typically use Ldap in production...

Jaas

  • Seam security is leveraged and pointed at the taylor jaas configuration
	<security:identity authenticate-method="#{authenticator.authenticate}"
                      jaas-config-name="taylor"/>
  • By default a Database Login Module is used and pointed at the taylor-identity tables
	<application-policy name="taylor">
		<authentication>
			<login-module
				code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
				flag="required">
				<module-option name="dsJndiName">java:/TaylorDS</module-option>
				<module-option name="principalsQuery">
					select password from TUser where id=?
				</module-option>
				<module-option name="rolesQuery">
					select membership_id,'Roles' from TGroup_TUser where members_id=?
				</module-option>
				<module-option name="hashAlgorithm">MD5</module-option>
				<module-option name="hashEncoding">base64</module-option>
			</login-module>
		</authentication>
	</application-policy>

Ldap

Using a database identity store is great for development and small applications. However, for enterprise applications you will want to use Ldap.

The following shows how to configure the Taylor Identity Ldap implementation.

Login Module

Of course we want to leverage the existing Ldap Login Module to perform authentication.

	<!-- http://jboss.org/community/docs/DOC-11251 -->
	<login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >
		<module-option name="password-stacking">useFirstPass</module-option>
		<module-option name="java.naming.provider.url">ldap://localhost:10389</module-option>
		<module-option name="bindDN">uid=admin,ou=system</module-option>
		<module-option name="bindCredential">secret</module-option>
		<module-option name="baseCtxDN">ou=People,ou=system</module-option>
		<module-option name="baseFilter">(uid={0})</module-option>
		<module-option name="allowEmptyPasswords">false</module-option>
		<module-option name="rolesCtxDN">ou=Groups,ou=system</module-option>
		<module-option name="roleFilter">(member={1})</module-option>
		<module-option name="roleAttributeID">cn</module-option>
		<module-option name="roleRecursion">-1</module-option>
		<module-option name="searchScope">SUBTREE_SCOPE</module-option>
	</login-module>

Application Specific Roles

It is often the case that you will not be allowed to manage all of your application specific roles in the enterprise Ldap store. For example, a typically approach is to have one role per application stored in Ldap to retrict overall authorization and then perform fine-grained authorization in each application.

If this is the case then we can leverage login module stacking to combine local roles into the Jaas Subject.

Here the taylor-identity database store is used just to access application specific roles.

	<login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >
	...
	</login-module>
	<login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="sufficient">
		<module-option name="password-stacking">useFirstPass</module-option>
		<module-option name="dsJndiName">java:/TaylorDS</module-option>
		<module-option name="rolesQuery">select membership_id,'Roles' from TGroup_TUser where members_id=?</module-option>
	</login-module>

Programmatic Ldap Access

There is more to Ldap the just authentication and authorization. We also need access to Ldap with in applications to access user information such as email addresses. Two key components are provided to facilitate this:

  • LoginModuleOptions - provides access to the login module configuration to avoid duplicate configuration
  • ManagedLdapContext - manages the lifecycle of the ldap context
    • Note the assignment of the provider url
	<component name="ldapLoginModule" class="net.taylor.jaas.LoginModuleOptions" 
		appConfigurationEntryName="taylor" 
		loginModuleName="org.jboss.security.auth.spi.LdapExtLoginModule" />
	
	<ldap:ManagedLdapContext providerUrl = "#{ldapLoginModule['java.naming.provider.url']}"/>

Identity Service

Taylor provides an Identity Service that is used to access user and group information with in an application and encapsulates the source of the data.

Use the following to turn on the Ldap implementation.

	<ldap:LdapDao />

	<component class="net.taylor.identity.ldap.IdentityServiceLdapImpl" />		

Schema Mapping

Not all Ldap schema are created equal. To account for these variations a schema mapping feature is provided. The mapping uses a combination of annotations and xml configuration.

NOTE: You can use this feature to map your own entities as well, such as an Employee entity which contains address and contact information.

The User and Groups entities have already been implemented with the necessary annotations.

	@Entity
	public class User {
		@Id
		@Attribute
		public String getId() {...}

		@Attribute("cn")
		public String getName() {...}

		@Attribute("mail")
		public String getEmail() {...}
	}

	@Entity
	public class Group {

		@Id
		@Attribute("cn")
		public String getId() {...}

		@Attribute
		public String getName() {...}

		@Attribute
		public String getDescription() {...}

		@ManyToMany
		@Attribute
		public Set<User> getMembers() {...}
	}

These annotations are then merged with the metadata in the components.xml file.

	<ldap:Metadata name="net.taylor.identity.entity.UserLdapMetadata"
		dnAttribute="uid" 
		contextDN="#{ldapLoginModule['baseCtxDN']}" 
		filterObjectClass="person" 
		searchScope="SubTree">
		<attributes>
			<key>id</key><value>uid</value>
			<key>membership</key><value>businessCategory</value>
		</attributes>
	</ldap:Metadata>
	
	<ldap:Metadata name="net.taylor.identity.entity.GroupLdapMetadata"
		dnAttribute="cn" 
		contextDN="#{ldapLoginModule['rolesCtxDN']}" 
		filterObjectClass="groupOfNames">
		<attributes>
			<key>description</key><value>description</value>
			<key>members</key><value>member</value>
		</attributes>		
	</ldap:Metadata>
		

Synchronization

Synchronization is often necessary, because identity data will usually be spread across Ldap and the application database.

Users will need to be partially synchronized when Users are stored in Ldap and Roles are stored in the application, so that the roles can be assigned to users.

The LoginObserver will add new users to the database when they login the first time.

	<sync:LoginObserver />

The Synchronizer will sync users on startup that have the required role in Ldap.

	<sync:Synchronizer requiredRole="MyApp" />

Jpa Listeners can be used for real-time sync.

	<entity-listener class="net.taylor.ldap.jpa.LoadCallbackListener"/>
	<entity class="net.taylor.identity.entity.User">
		<entity-listeners>
			<entity-listener class="net.taylor.ldap.jpa.UpdateCallbackListener"/>
		</entity-listeners>
	</entity>

components.xml

These various configurations need to be placed in a components.xml. Any components.xml will do, but we need to consider different environment configurations.

For example, all of the connection information is stored outside the Ear in the server\all\conf\login-config.xml and accessed with in the Ear via the LoginModuleOptions component. This facilitates deploying the Ear to different ldap-enabled environments.

However, not all environments will use ldap.

One option is to use WEB-INF\classes\META-INF\components.xml.

But a better option is to create a separate jar with just a components.xml file for your ldap configuration. Then use a profile in your maven build to control the dependency on this jar.


Unit Testing

To facilitate unit testing with Ldap, taylor-identity provides a component to startup ApacheDS.

	<ldap:ApacheDS />
  • import.ldif - load data


Seam Security

The User and Group entities have the seam security annotations applied and the jpa-identity-store is configured in the taylor-identity/META-INF/components.xml file.

	<security:jpa-identity-store 
		user-class="net.taylor.identity.entity.User"
		role-class="net.taylor.identity.entity.Group"/>

Related