Legacy Authentication Service
1 Background
Version 1.x uses the security service that manages the authentication and the organization services as well as the role/membership extraction. In some places, our developers use the security service to check the access rights, in some other places, they use the organization service.
In version 2.x, a new authentication service is used to unify the permission management and the performance improvement . It was designed to satisfy the following requirements:
- The service should provide a list of the current login subject
- The service should broadcast an event when a subject is login or logout
- The service should load all the memberhips of a user when he is logged and cache in the memory for better performance
- The service should provide the subject(user) of the current action(or request)
- The service should provide the methods to check if the current subject has a certain role or memberships or in a group
2 JAAS Login Modules
The JAAS mechanism is used to forward security information from eXo to the Application Server in use.
The new JAAS Login Modules have been built in order to be used in most cases without the creation of your own ones. Hence we have two login modules now. The first one can be replaced to handle authentication and password manipulations. The second one just propagates user information to different listeners that are configured as usal eXo plugins as it will be explained later.
Responsible for the authentication, this is the only LoginModule that has access to the password. It handles authentication through eXo by calling the method
public boolean login(String userName, String password) throws Exception
of the AuthenticationService. The password is not stored anywhere but just taken from the OrganizationService that looks for it in the DB or LDAP. Once the authentication is done there is no password manipulation in eXo.
Once the authentication is done we set up the username inside the SharedMap to propagate the user info to the other LoginModules.
sharedState_.put("javax.security.auth.login.name", username);
- ExoBroadcastJAASLoginModule:
The broadcast login module is used to propagate the user identity to all the listeners that have subscribed to. The Identity does not contain any information on the password. It is propagated the commit() method of the LoginModule:
Identity identity = new Identity(username, username, subject_);
authService.broadcastAuthentication(identity);
The two login modules can be configured in jaas.conf in Tomcat and in similar files in other application servers:
exo-domain {
org.exoplatform.services.organization.auth.ExoLoginJAASLoginModule required;
org.exoplatform.services.organization.auth.ExoBroadcastJAASLoginModule required;
};
3 AuthenticationService API
The API is based on the Identity object which is a wrapper on top of the Subject object from the JAAS library. In the future it may also include cached user information like memberships for all the user session.
package org.exoplatform.services.organization.auth;
import javax.security.auth.Subject;
public class Identity {
private String sessionId_ ;
private String username_ ;
private Subject subject_ ;
public Identity(String sessionId, String username, String password) {
this(sessionId, username, new Subject()) ;
}
public Identity(String sessionId, String username, Subject subject) {
sessionId_ = sessionId ;
username_ = username ;
subject_ = subject ;
}
public String getSessionId() { return sessionId_ ; }
public String getUsername() { return username_ ; }
public Subject getSubject() { return subject_ ;}
}
The map of Identities is managed by the AuthenticationService class itself. It allows to retrieve an identity based on a session id (in the Portal context the sessionid is also the username).
During the lifetime of a request (in web mode it will be the HTTP request) the current identity can be bound to the current request thread thanks to a ThreadLocal located inside the AuthenticationService.
package org.exoplatform.services.organization.auth;
import java.util.HashMap;
import java.util.Map;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.organization.OrganizationService;
public class AuthenticationService {
private ThreadLocal <Identity> currentIdentity_ = new ThreadLocal <Identity>();
private Map<String, Identity> identities_ = new HashMap<String, Identity>() ;;
private ListenerService listenerService_ ;
private OrganizationService orgService_ ;
public AuthenticationService(ListenerService listenerService, OrganizationService orgService) {
listenerService_ = listenerService ;
orgService_ = orgService ;
}
public boolean login(String userName, String password) throws Exception {
if(orgService_.getUserHandler().authenticate(userName, password)) {
return true ;
} else {
return false ;
}
}
public void broadcastAuthentication(Identity identity) throws Exception {
identities_.put(identity.getSessionId(), identity) ;
listenerService_.broadcast("exo.service.authentication.login", this, identity) ;
}
public Identity getIdentityBySessionId(String sessionId) throws Exception {
return identities_.get(sessionId) ;
}
public Identity getCurrentIdentity() { return currentIdentity_.get() ; }
public void setCurrentIdentity(Identity identity) { currentIdentity_.set(identity) ; }
public void logout(String sessionId) throws Exception {
Identity identity = identities_.remove(sessionId) ;
if(identity == null) {
throw new Exception("Cannot find the subject for the " + sessionId) ;
} else {
listenerService_.broadcast("exo.service.authentication.logout", this, identity) ;
}
}
public OrganizationService getOrganizationService() { return orgService_ ; }
}
The code to setup the current identity is the following one if you know the session id of the identity to setup. That code has to be called at each request.
AuthenticationService authService =
(AuthenticationService) container.getComponentInstanceOfType(AuthenticationService.class) ;
if(remoteUser != null) {
Identity identity = authService.getIdentityBySessionId(remoteUser) ;
if(identity == null) {
throw new Exception("Cannot find the identity for user " + remoteUser) ;
}
authService.setCurrentIdentity(identity) ;
}