Dynamic LDAP group membership; increasing the flexibility of your security model
October 3rd, 2007 by Oscar HuseyinHow’s this for a problem; having users that can switch between security context in a J2EE construct and maintain a linear number of user to group associations in LDAP. What does this mean? To put it simply, if l’m user Foo and l can (from a security perspective) take on the exclusive roles of an administrator, accountant, auditor respectively, this must ultimately equate to group memberships; more specifically LDAP group memberships. This problem is not solved by simply assigning the user Foo into all the groups, because then Foo would have access to an auditor’s functions after he logged in as an accountant, and not all accountant’s are auditors. If we were to attempt a solution modeling the permissions using LDAP, we would have to create users Foo, Foo' and Foo'' where users are given group memberships to administrator, accountant and auditor respectively. From an LDAP perspective this solution is unmanageable, because as the requirement for users permissioning levels change, then the number of unique user to group combinations would exponentially increase, resulting in unmanageable LDAP system.
A simple solution to this problem is in the use of dynamic groups. One small problem though, a dynamic group is only a concept and, technically speaking, cannot be modeled in OpenLDAP, eTrust and IBM Directory Server. Therefore, dynamic groups functionality must be built into the application security architecture.
Firstly, all the defined groups must be defined a-priori and stored in an LDAP server, eg. cn=mycompany-group-banking, ou=groups,ou=policies,o=mycompany. All users in the system are not assigned any group memberships in LDAP itself which means, form an LDAP perspective, they dont have any group memberships. Now, to achieve dynamic groups, the “system access profile” needs to be read from a store (which is typically the application database as the user to profile relationships are highly normalised) and then used to build the users security credential. For example, the database is queried to read the user group memberships (which map one-to-one with the LDAP group) and are then used to construct the platform specific security credential; the JAAS Subject in a J2EE construct.
Confused? Perhaps it time for some concrete examples. WebSphere Application Server has an interface which can be configured to allow credential construction from a “trusted” source. This mechanism is called the Trust Association Interceptor (TAI). You could also use a custom Login Module but l find them a little cumbersome to work with. IBM Developerworks has a great article on this exact topic. If you want to assert the identify of a user who has not been authenticated using WebSphere, you can configure the TAI so that it fires when a client attempts to access a secured resource. At this time, the TAI has the ability to construct a security credential and assert the identity to WAS.
/*
* Trust Authentication Interceptor interface method that will called by WebSphere.
*/
public TAIResult negotiateValidateandEstablishTrust(
HttpServletRequest req,
HttpServletResponse resp) throws WebTrustAssociationFailedException {
String userid = req.getParameter("userid");
String password = req.getParameter("password");
try {
// Compute identity information
InitialContext ctx = new InitialContext();
UserRegistry reg = (UserRegistry) ctx.lookup("UserRegistry");
// Check the users identity in LDAP, eg. course grained authentication
boolean validUser = authenticateUsingLDAP(userid, password, reg);
// Load users group memberships from the database
if (validUser) {
Collection groups = readGroupsFromDatabase(userid);
} else {
throw new UnauthenticatedException("User could not be authenticated.");
}
// Having read the groups from the database for the authenticated user
Subject subject = constructSubject(groups);
// Now, assert the identity to WebSphere Application Server
return TAIResult.create(HttpServletResponse.SC_OK, "notused", subject);
} catch (SomeException e) {
Log.ingo("Exception caught whilst attempting to authenticate user");
}
}
Now that we have a security credential, for all invocations to secured EJB’s, Servlet’s, JSP’s, etc. WebSphere will automatically propagate the Subject from the authenticated caller to the callee. For example, when an EJB is invoked in another WAS instance in the Cell, the WebSphere EJB container (on the callee side) will intercept the EJB call, check the presence of the LTPA token for the call, check the local WAS Security Store for the (potentially) cached Subject, then make a call to retrieve the Subject (via JMX) if the Subject is not present in the Security Store to the caller WebSphere.
Constructing the subject is also simple. The key to the design, as mentioned before, is to ensure your user group memberships are in the application domain and not in LDAP. Here’s an example:
private Subject constructSubject(String userId, Collection groups) {
// Create the JAAS Subject
Subject subject = new Subject();
// Data structure to store credentials
Hashtable hashtable = new Hashtable();
hashtable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, userId + System.currentTimeInMillis());
hashtable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME, userId);
// This is the point which implements the Dynamic Groups. WebSphere reads the
// group memberships using the WSCREDENTIAL_GROUPS key.
hashtable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groups);
// Add all credential information to the Subject to complete Subject construction
subject.getPublicCredentials().add(hashtable);
return subject;
}
One point to be careful about. The groups that you insert into the public credential set of the Subject should exist and match the ones in LDAP. An example of a group is:
cn=mycompany-group-accounting, ou=groups,ou=policies,o=mycompany