Chapter 7. Authentication and Authorization
Authentication in the portal is based on JAAS and by default it is a standard J2EE FORM based authentication.
7.1. Authentication Methods
The portal supports the following authentication methods:
- J2EE FORM based authentication
- The
RememberMeauthentication method (wherein the user checks the Remember my login check box on the log in form). - SSO server integration (CAS, JOSSO, OpenSSO).
- SPNEGO authentication with a Kerberos ticket.
- SAML2 based authentication.
- Cluster authentication with load balancer or with JBoss SSO valve.
See Also:
7.2. Authentication Workflow
Authentication workflow consists of HTTP requests and redirects which include handshakes. Currently only Servlet 3.0 containers are supported, so authentication is triggered programmatically from Servlet API.
In
JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/web.xml, authentication is triggered by accessing a secured URL /dologin:
<security-constraint> <web-resource-collection> <web-resource-name>user authentication</web-resource-name> <url-pattern>/dologin</url-pattern> <http-method>POST</http-method> <http-method>GET</http-method> </web-resource-collection> <auth-constraint> <role-name>users</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint>
This means that access to URLs (such as http://localhost:8080/portal/dologin) will directly trigger J2EE authentication in the case that the user is not already logged in.
Access to this URL means that the user needs to be in the JAAS group users, otherwise they can authenticate but will receive an HTTP error: 403 Forbidden.
In the next part of the file we can see that authentication is FORM based and it starts by redirection to /login URL, which is mapped to
LoginServlet.
<login-config> <auth-method>FORM</auth-method> <realm-name>gatein-domain</realm-name> <form-login-config> <form-login-page>/login</form-login-page> <form-error-page>/login</form-error-page> </form-login-config> </login-config>
LoginServlet redirects the user to the login page placed in JPP_DIST/gatein/gatein.ear/portal.war/login/jsp/login.jsp.
Changes to the appearance of the login page can be made in this JSP file. Alternatively you can create an extension and override this page via extension if you don't want to edit it directly. You can also change images or CSS placed in
JPP_DIST/gatein/gatein.ear/login/skin.
After a user submits the login form, the LoginServlet will store credentials and trigger WCI login, which delegates to Servlet API (method HttpServletRequest.login(String username, String password) available in Servlet 3.0) and additionally triggers WCI Authentication listeners. Login through Servlet API delegates to JAAS.
7.2.1. RememberMe Authentication
In the default login dialog is the Remember my login checkbox, which persist a user's login on their workstation. The default validity period of the
RememberMe cookie is one day, so a user can be logged for a whole day before needing to re-authenticate. The validity period is configurable.
The way the RememberMe authentication operates is described below:
- User checks the checkbox
RememberMeon the login screen of the portal, then submits the form. - Form data such as
username,passwordandremembermeare sent in an HTTP POST request to the/portal/loginURL, with theremembermeparameter set to true. - Request is processed by PortalLoginController servlet. The servlet obtains an instance of RemindPasswordTokenService and saves user credentials into JCR. It generates and returns special token (key) for later use, then creates a cookie called RememberMe and uses the returned token as the cookie's value.
7.2.2. Re-authentication
- After some time, the user wants to re-authenticate. It is assumed that his HTTP Session is already expired but his RememberMe cookie is still active.
- The user sends the HTTP request to some portal pages (for example, http://localhost:8080/portal/classic).
- There is a special HTTP Filter RememberMeFilter configured in
web.xml, which checks the RememberMe cookie and then it retrieves credentials of user from RemindPasswordTokenService. Now filter redirects request to PortalLoginController and authentication process goes in same way as for normal FORM based authentication.
7.2.3. RemindPasswordToken Service
This is a special service used during the RememberMe authentication workflow. It is configurable in the file
JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/conf/common/remindpwd-configuration.xml.
You can also encrypt passwords before storing them in JCR.
See Also:
7.3. Login Modules
From the WCI servlet API login, the user is redirected to JAAS authentication. The portal uses its own security domain (gatein-domain) with a set of predefined login modules. Login module configuration for gatein-domain is contained in the
JPP_HOME/standalone/configuration/standalone.xml file.
Below is the default 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>
New login modules can be added or the stack completely replaced with custom modules.
Authentication starts with the login method of each login module being invoked. After all login methods are invoked, the authentication is continued by invoking 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 turn invokes the abort method on each login module. By returning "false" from the login method, you can ensure that the login module is ignored. This is not specific to the portal, but it is generic to JAAS. See http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html for more information about login modules in general.
7.3.1. Types of Login Modules
The existing login modules include SSODelegateLoginModule, JBossAS7LoginModule, CustomMembershipLoginModule, InitSharedStateLoginModule,and SharedStateLoginModule.
Existing Login Modules
- SSODelegateLoginModule
- It is useful only if SSO authentication is enabled (disabled by default). SSO authentication can be enabled through properties in
configuration.propertiesfile and it delegates the work to another real login module for SSO integration. If SSO is disabled,SSODelegateLoginModuleis ignored. If SSO is used and SSO authentication succeeds, the special Identity object is created and saved into shared state map (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
- The most important login module, which is normally used to perform the whole authentication by itself. First it checks if an Identity object has been already created and saved into the sharedState map by previous login modules (like SSODelegateLoginModule, CustomMembershipLoginModule or SharedStateLoginModule). If not, it triggers real authentication of the user with usage of the Authenticator interface and it will use
Authentication.validateUser(Credential[] credentials)which performs real authentication of user names and passwords against OrganizationService and the portal identity database.In theJBossAS7LoginModule.commitmethod, the Identity object is registered to IdentityRegistry, which will be used later for authorization. Also some JAAS principals (UserPrincipal and RolesPrincipal) are assigned to our authenticated Subject. This is needed for JBoss Enterprise Application server, so that it can properly recognize the name of the logged user and its roles on an application server level. - InitSharedStateLoginModule
- It can read credentials from JAAS callback and add them into shared state. It's intended to be used in JAAS chain before SharedStateLoginModule
- SharedStateLoginModule
- It reads the username and password from sharedState map from attributes
javax.security.auth.login.nameandjavax.security.auth.login.password. Then it calls Authenticator.validateUser(Credential[] credentials), to perform authentication of username and password against OrganizationService and portal identity database. The result of successful authentication is an Identity object, which is saved to the sharedState map.
See Also:
7.3.2. About Custommembership Login Module
Custommembership login module is a special login module, that can be used to add a user to existing groups during a successful login of this user. The group name is configurable and by default is /platform/users group. This 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 an LDAP user are taken from the LDAP tree so that users may not be in the /platform/users group, which is needed for successful authorization.
Note
The CustomMembershipLoginModule cannot be the first login module in the LoginModule chain because it assumes that the Identity object is already available in the shared state. So there are two possible cases. For a non-SSO case, you may need to chain this login module with other login modules, which can be used to establish Identity and add it into shared state. Those login modules can be
InitSharedStateLoginModule and SharedStateLoginModule. For an SSO case, you can add CustomMembershipLoginModule between SSODelegateLoginModule and JBossAS7LoginModule.
7.3.3. Configuring Custommembership Login Module
To configure Customermembership Login module with SSO disabled and SSO enabled is shown as example in this topic.
Example 7.1. 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>
Example 7.2. Configuration with SSO Enabled
<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>
7.3.4. Creating a Login Module
Before creating your own login module, it is recommended that you study the source code of existing login modules to better understand the 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.
7.3.5. Levels of Authentication
There are two levels of authentication available. The overall result of JAAS authentication handles both cases:
- Authentication on Application Server Level
- The Application server must properly recognize that the user is successfully logged and it has their JAAS roles assigned. Unfortunately this part is not standardized and is specific for each application server. For example in Red Hat JBoss Enterprise Application Platform, you need to ensure that JAAS Subject has an assigned UserPrincipal with user name and also a RolesPrincipal, which contains a list of JAAS roles. This part is actually done in
JBossAS7LoginModule.commit().After successful authentication, the user must be at least in JAAS roleusersbecause this role is declared inweb.xmlas you saw above. JAAS roles are extracted RolesExtractor from portal memberships. - Authentication on the Portal Level
- The login process creates a special object
org.exoplatform.services.security.Identityand registers this object into the portal componentIdentityRegistry. The Identity object encapsulates the user name of the authenticated user, memberships of this user, and JAAS roles. The Identity object can be created with theAuthenticatorinterface.
7.3.6. Authenticator Interface
Authenticator is an important component in the authentication process. The interface org.exoplatform.services.security.Authenticator 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; }
Method validateUser is used to authenticate the given credentials (username and password). It returns a username if the credentials are correct, otherwise a
LoginException is thrown.
Method createIdentity is used to create an instance of object org.exoplatform.services.security.Identity, which encapsulates all important information about a single user:
- Username
- Set of Memberships (MembershipEntry objects) associated with the
user. Membership is an object, which contains information about membershipType (manager, member, validator, ... ) and about group (/platform/users, /platform/administrators, /partners, /organization/management/executiveBoard, ... ). - Set of Strings with JAAS roles of given user. JAAS roles are simple Strings, which are mapped from MembershipEntry objects. There is another special component org.exoplatform.services.security.RolesExtractor, which is used to map JAAS roles from MembershipEntry objects.
7.3.7. RolesExtractor Interface
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); }
Default implementation DefaultRolesExtractorImpl is based on a special algorithm, which uses the name of the role from the root of the group (for example for role "/organization/management/something" we have JAAS role "organization"). The only exception is the "platform" group where we use second level as the name of the group. For example from group "/platform/users" we have JAAS role "users".
For example, the user root has the following memberships:
- member:/platform/users
- manager:/platform/administrators
- validator:/platform/managers
- member:/partners
- member:/customers/acme
- member:/organization/management/board
In this case we will have JAAS roles: users, administrators, managers, partners, customers, organization.
The default implementation of Authenticator is OrganizationAuthenticatorImpl, which is implementation based on OrganizationService. See the API documentation in the Development Guide for further information.
You can override the default implementation of the mentioned
Authenticator and RolesExtractor interfaces if the default behavior is not suitable for your needs.
7.4. Authorization
Authorization in the portal happens on two levels:
- Servlet Container Authorization
- Portal Authorization
7.4.1. Servlet Container Authorization
The first round of authorization is through the servlet container authorization based on the secured URL from
web.xml. The web.xml code sample shows that secured URLs are accessible only for users with the users role.
<auth-constraint> <role-name>users</role-name> </auth-constraint>
This means a user must be in the portal role
/platform/users.
If the user is successfully authenticated, but is not in the
/platform/users group, the user will not be assigned a correct JAAS role users. This will result in an error 403 Forbidden thrown by the servlet container. For example in LDAP setup, users may not be in /platform/users by default, but CustomMembershipLoginModule can fix this problem.
Important
It is possible to change the behavior and add more
auth-constraint elements into web.xml, however this protection of resources based on web.xml is not a standard portal method and is mentioned here mainly for illustration purposes.
See Also:
7.4.2. Portal Authorization
The second round of authorization is based on the component UserACL. You can declare access, and edit permissions for portals, pages and/or portlets. UserACL is then used to check if our user has particular permissions to access or edit specified resources. An important object containing information about the roles of our user is the Identity object created during JAAS authentication.
Authorization on portal level looks like this:
- The user sends a HTTP request to some URLs in portal;
- The HTTP request is processed through
SetCurrentIdentityFilter, which is declared inJPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/web.xml. - SetCurrentIdentityFilter reads the user name of current user from HttpServletRequest.getRemoteUser(). Then it looks for Identity of this user in IdentityRegistry, where Identity has been saved during authentication. The Identity found is then encapsulated into a
ConversationStateobject and bound into the ThreadLocal variable. - UserACL is able to obtain Identity of current user from method UserACL.getIdentity(), which calls ConversationState.getCurrent().getIdentity() to find the current Identity bound to ThreadLocal. Now the UserACL has the identity of the user and can perform any security checks.
