So from WCI servlet API login, you are redirected to the JAAS authentication. GateIn Portal is using its own security domain named gatein-domain with a set of predefined login modules. Login module configuration for gatein-domain is in:
For JBoss: $PLATFORM_JBOSS_HOME/standalone/configuration/standalone.xml
.
For Tomcat: $PLATFORM_TOMCAT_HOME/conf/jaas.conf
.
By default, you can see this login modules stack:
<security-domain name="gatein-domain" cache-type="default">
<authentication>
<login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
<module-option name="enabled" value="${gatein.sso.login.module.enabled}" />
<module-option name="delegateClassName" value="${gatein.sso.login.module.class}" />
<module-option name="portalContainerName" value="portal" />
<module-option name="realmName" value="gatein-domain" />
<module-option name="password-stacking" value="useFirstPass" />
</login-module>
<login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
</login-module>
</authentication>
</security-domain>
You are free to add some new login modules or completely replace existing login modules with your own ones.
Authentication starts with invoke of the login
method on each login module. After all login methods are invoked, the authentication is continued by invoke of the commit method on each login module. Both login
and commit
methods can throw LoginException
.
If it happens, then the whole authentication ends unsuccessfully, which in next turn invokes the abort method on each login module. By returning "false" from method login, you can ensure that login module is ignored.
This is not specific to GateIn Portal but it is generic to JAAS. See here for more information about login modules.
Here is brief description of existing login modules:
SSODelegateLoginModule
: It is useful only if the SSO authentication is enabled (disabled by default).
It can be enabled through properties in the exo.properties
file
(see Configuration overview for this file) and in this case it delegates the work to another real login module for the SSO integration.
If SSO is disabled, SSODelegateLoginModule is simply ignored.
If SSO is used and SSO authentication is successful, the special Identity object will be created and saved into the shared state map (which is shared between all login modules), so that this Identity object can be used by JBossAS7LoginModule
or other login modules in the JAAS chain.
JBossAS7LoginModule
: This is the most important login module which is normally used to perform the whole authentication. First it checks if the Identity
object has been already created and saved into sharedState
map by previous login modules (for example, SSODelegateLoginModule, CustomMembershipLoginModule or SharedStateLoginModule
).
If not, it triggers the real authentication of user with usage of Authenticator interface and it will use Authentication.validateUser(Credential[] credentials)
which performs the real authentication of username and password against OrganizationService and portal identity database. In the JbossAS7LoginModule.commit
method, the Identity object is registered to IdentityRegistry, which will be used later for authorization. Also, some JAAS principals (UserPrincipal and RolesPrincipal) are assigned to the authenticated Subject.
This is needed for JBoss AS server, so that it can properly recognize name of logged user and his roles on JBoss AS level.
There is couple of other login modules, which are not active by default, but you can add them if you find them useful.
CustomMembershipLoginModule - special login module, which can be used to add user to some existing groups during the successful login of this user. The group name is configurable and by default is /platform/users group. The login module is not used because in normal environment, users are already in the /platform/users group. It is useful only for some special setups like read-only LDAP, where groups of ldap user are taken from ldap tree so that users may not be in the /platform/users group, which is needed for successful authorization.
Note that CustomMembershipLoginModule cannot be first login module in LM chain because it assumes that Identity object is already available in shared state. So there are those possible cases:
For the Non-SSO case, you may need to chain this LM with other login modules, which can be used to establish Identity and add it into the shared state. Those LM can be InitSharedStateLoginModule and SharedStateLoginModule.
For the SSO case, you can simply add CustomMembershipLoginModule between SSODelegateLoginModule and JBossAS7LoginModule.
Configuration example with CustomMembershipLoginModule and disabled SSO:
<login-module code="org.exoplatform.web.security.InitSharedStateLoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
</login-module>
<login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
</login-module>
<login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
<module-option name="membershipType" value="member" />
<module-option name="groupId" value="/platform/users" />
</login-module>
<login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
</login-module>
And now configuration example with enabled SSO:
<login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
<module-option name="enabled" value="${gatein.sso.login.module.enabled}" />
<module-option name="delegateClassName" value="${gatein.sso.login.module.class}" />
<module-option name="portalContainerName" value="portal" />
<module-option name="realmName" value="gatein-domain" />
<module-option name="password-stacking" value="useFirstPass" />
</login-module>
<login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
<module-option name="membershipType" value="member" />
<module-option name="groupId" value="/platform/users" />
</login-module>
<login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
<module-option name="portalContainerName" value="portal"/>
<module-option name="realmName" value="gatein-domain"/>
</login-module>
InitSharedStateLoginModule
: It can read credentials from the JAAS callback and add them into shared state. It is intended to be used in the JAAS chain before SharedStateLoginModule.
SharedStateLoginModule
: It reads username and password from the sharedState map from attributes: javax.security.auth.login.name
and javax.security.auth.login.password
. Then it calls Authenticator.validateUser(Credential[] credentials)
to perform authentication of username and password against OrganizationService and portal identity database. Result of successful authentication is object Identity, which is saved to sharedState map.
Creating your own login module
Before creating your own login module, it is recommended you study source code of existing login modules to better understand the whole JAAS authentication process. You need to have good knowledge so that you can properly decide where your login module should be placed and if you need to replace some existing login modules or simply attach your own module to the existing chain.
There are actually two levels of authentication and the overall result of JAAS authentication should properly handle both these cases:
Authentication at application server level
Application server needs to properly recognize that the user is successfully logged and it has assigned his JAAS roles. Unfortunately this part is not standardized and is specific for each AS. For example in JBoss AS, you need to ensure that JAAS Subject has assigned principal with username (UserPrincipal) and also RolesPrincipal, which has name "Roles" and it contains a list of JAAS roles. This part is actually done in JbossLoginModule.commit()
.
In Tomcat, this flow is little different, which means Tomcat has it is own TomcatLoginModule
.
After successful authentication, the user needs to be at least in JAAS role users because this role is declared in web.xml
as you saw above. The JAAS roles are extracted by the special algorithm from GateIn Portal memberships. See below in section with RolesExtractor.
Authentication at Portal level
The login process needs to create a special object: org.exoplatform.services.security.Identity
and register this object into IdentityRegistry component of GateIn Portal. This Identity object should encapsulate username of authenticated user, memberships of this user and JAAS roles.
Identity object can be easily created with the Authenticator interface as shown below.
Authenticator and RolesExtractor
Authenticator is an important component in the authentication process. Actually the org.exoplatform.services.security.Authenticator
interface looks like this:
public interface Authenticator { /** * Authenticate user and return userId. * * @param credentials - list of users credentials (such as name/password, X509 * certificate etc) * @return userId */ String validateUser(Credential[] credentials) throws LoginException, Exception; /** * @param userId. * @return Identity */ Identity createIdentity(String userId) throws Exception; }
The validateUser
method is used to check whether given credentials (username and password) are really valid. So it performs real authentication. It returns the username if credentials are correct. Otherwise, LoginException
is thrown.
The createIdentity
method is used to create instance of the org.exoplatform.services.security.Identity
object, which encapsulates all important information about single user like:
Username
Set of Memberships (MembershipEntry
objects) to which the user belongs. Membership
is object, which contains information about membershipType
(manager, member, validator, and more) and about group (/platform/users, /platform/administrators, /partners, /organization/management/executiveBoard, and more).
Set of Strings with JAAS roles of a given user. The JAAS roles are simple strings, which are mapped from the MembershipEntry objects. There is another special component named org.exoplatform.services.security.RolesExtractor
, which is used to map JAAS roles from MembershipEntry objects. The RolesExtractor interface looks like this:
public interface RolesExtractor { /** * Extracts J2EE roles from userId and|or groups the user belongs to both * parameters may be null * * @param userId * @param memberships */ Set<String> extractRoles(String userId, Set<MembershipEntry> memberships); }
The default implementation named DefaultRolesExtractorImpl
is based on special algorithm, which uses name of role from the root of the group (for example you have JAAS role "organization" for the "/organization/management/something" role). The only exception is "platform" group where the second level is used as the group name. For example, from "/platform/users" group, you have the JAAS role "users".
Assuming that you have user root, which has memberships: member:/platform/users, manager:/platform/administrators, validator:/platform/managers, member:/partners, member:/customers/acme, member:/organization/management/board. In this case, you will have the JAAS roles: users, administrators, managers, partners, customers, organization.
Default implementation of Authenticator is OrganizationAuthenticatorImpl
, which is implementation based on OrganizationService
.
You can override the default implementation of mentioned interfaces (Authenticator
and RolesExtractor
) if the default behavior is not suitable for your needs.