Showing posts with label Security. Show all posts
Showing posts with label Security. Show all posts

Thursday, 21 May 2015

Hippo CMS with Spring Security

Hippo CMS already uses Spring for dependency injection as part of the HST framework used to develop websites. By default, it uses JAAS as its authentication mechanism and the CMS repository (via the HippoAuthenticationProvider) for authenticating login credentials and providing security roles for the authenticated user.

The existing website application already had in excess of 10 million users and there was no way that these users would be moved into the CMS repository. Fortunately Hippo can be configured with a custom AuthenticationProvider to use a database or LDAP service rather than the CMS repository for authenticating login credentials.

As part of the redevelopment of the website, the login UI was also redesigned. The default form based authentication uses the Hippo UI templates to render the login form. Again this was quite easy to replace with a custom login form template by following the online documentation [1].

At this point the development was looking good, the show and tell at the end of the first sprint demonstrated the new login story and the home page with integration to internal services for authentication and customer transactional data.

The next sprint required some changes to the login functionality based on insights gathered from user testing and UI/UX designers. Due to the business requirements, the login functionality was required to display a number of different messages depending on the cause of authentication failure. This may involve a simple error message, or asking the user that they need to activate their account or to contact the call centre if their account had been locked.

Using the default login mechanism proved to be difficult in propagating different error messages from the custom AuthenticationProvider back through the Hippo JAAS LoginModule and to the Hippo LoginServlet to render the login form. This was due to the Hippo LoginModule catching any exceptions from the AuthenticationProvider and throwing a new JAAS LoginException with a default error message thereby losing the error code and message from the original exception. There were also other issues with CSS when the login form was redisplayed after an authentication failure.

Furthermore, there was also a lurking requirement that the new mobile apps would be required to integrate with Hippo CMS for authentication and content. This would require developing a RESTful API to expose the content for the mobile apps to use. The mobile apps would not use http form based authentication for a RESTful API. Consequently the new website would need to provide a different authentication method depending on the access channel (web browser or mobile app).

The above issues and the complexity involved in customising the JAAS implementation were the main deciding factor to adopt Spring Security. Fortunately, we were not the first team that needed to do this and the Hippo community provides a project that details the integration of Spring Security with HST based website applications.[2]

The configuration is pretty simple and requires configuring the Spring Security filter within the web.xml file.

Spring Security Configuration in web.xml
This will now ensure all requests are passed through the Spring Security filter chain. The context files can be used to specify any security related Spring beans such as a custom authentication manager, authentication filters and success and failure handlers.

Thereafter, to integrate Spring Security within the HST based web application, it is simply a matter of adding the SpringSecurityValve into the HST request processing pipeline. As the HST pipeline employs the 'chain of responsibilty' pattern [3], by adding the SpringSecurityValve into the pipeline, HST will ensure the SpringSecurityValve has the opportunity to handle the request and establish a javax.security.auth.Subject if it finds a spring security Authentication instance.

The Hippo SpringSecurityValve allows securing the HST based website by configuring 'hst:sitemapitem' or 'hst:mount' nodes with security settings.

References
[1] http://www.onehippo.org/library/concepts/security/hst-2-authentication-and-authorization-support.html
[2] http://hst-springsec.forge.onehippo.org/
[3] http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

Wednesday, 10 October 2012

Custom Glassfish Security Realm

Following on from the last post, our client required an user management console that delegated authentication to the container to leverage the SSO capabilities that were available.

Glassfish allows creating a JDBC realm that authenticates users and retrieves their roles from a database. 
The above screenshot shows how a new JDBC realm can be created from the Glassfish administrative console. This JDBC realm however specifies that there needs to be an User table with username and password columns, and a Group table with group name column with a foreign key to the user table as shown below:

T_USERS
USERNAME (PK)
PASSWORD
T_GROUPS
USERNAME (FK)
GROUPNAME
This is fine if the tables do not exist, but what if the database already exists or you require a mapping table for the many-to-many relationship between users and groups? For example, as our application was using Spring Security we already had a database schema as follows:
T_USERS
USERNAME (PK)
PASSWORD
....
T_USER_AUTHORITIES
USERNAME (FK)
ROLE_ID (FK)
T_ROLES
ROLE_ID (PK)
ROLENAME
In such a scenario, the Glassfish provided JDBCRealm is insufficient and a custom realm needs to be created. The custom jdbc realm will allow Glassfish to authenticate using the same database as the application is using to manage the users.

Creating a custom realm requires providing a custom JAAS login module class and custom realm class.
  • The custom login module must extend com.sun.appserv.security.AppservPasswordLoginModule.This class implements the javax.security.auth.spi.LoginModule.
  • The custom realm class must extend the abstract com.sun.appserv.security.AppservRealm class. You may find it easier to view the source code of the com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm class to provide a base from which to develop the custom realm class. Unfortunately, the JDBCRealm class is final and so cannot be extended. The realm class must also contain the HK2 @Service annotation to allow integration with the Glassfish OSGI module system.
  • Update the <GF_HOME>/domains/<DOMAIN-NAME>/config/login.conf to add an entry for the custom realm referring to the custom LoginModule.
  • Finally there should be a file named javax.security.auth.spi.LoginModule in the META-INF/services directory with the fully qualified name of the LoginModule class.
Once these classes have been written they need to be packaged as an OSGI bundle to be activated by Glassfish. For this purpose, we used the maven-bundle-plugin
 <dependencies>

<dependency>
            <groupId>org.glassfish.security</groupId>
            <artifactId>security</artifactId>
            <version>3.1.1-b11</version>
        </dependency>            
       
</dependencies>
<build>
       <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Export-Package>                            ${project.groupId}.glassfish.auth;version=${project.version}
                        </Export-Package>
                        <Import-Package>
                            java.util,
                            javax.security.auth,
                            javax.security.auth.login,
                            javax.sql,                                                   
                            org.glassfish.security.common,
                            org.jvnet.hk2.annotations,
                            org.jvnet.hk2.component,
                            com.sun.appserv.connectors.internal.api,
                            com.sun.appserv.security,
                            com.sun.enterprise.security.common,
                            com.sun.enterprise.util,
                            com.sun.enterprise.util.i18n,                           
                            com.sun.enterprise.security.auth.realm,
                            com.sun.enterprise.security.auth.realm.jdbc,
                            com.sun.enterprise.security.auth.login.common,
                            com.sun.enterprise.security.auth.digest.api,
                            com.sun.enterprise.universal                           
                        </Import-Package>
                    </instructions>
                </configuration>
              </plugin>
</plugins>   
</build>
Note the export-package element should contain the name of the packages of the custom LoginModule class and custom AppservRealm classes.
Once the OSGI bundle has been created it can be loaded by copying it to <GF_HOME>/domains/<DOMAIN-NAME>/autodeploy/bundles.

The realm can now be created from the Glassfish administrative console. When an application that utilizes this realm is accessed the LoginModule will be initialized.




Friday, 5 October 2012

Container Based Authentication with Spring Security

There are occasions where applications may be required to delegate authentication to a central identity access manager or Single Sign On (SSO) provider. This post describes how an application can use Spring Security for authorization but also delegate authentication to the application server.

These scenarios are referred to as "pre-authenticated" as the user has been reliably authenticated prior to accessing the application. Spring Security provides a number of classes to support pre-authentication such as the PreAuthenticatedAuthenticationProvider class. This post demonstrates how to convert a web application based on Spring Security authentication to one that uses Container based authentication.


    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
    </sec:authentication-manager>

    <bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
    </bean>

    <bean id="preAuthenticatedUserDetailsService"
            class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>



As Spring Security will no longer be authenticating the user, we need to modify our login page to target the standard j_security_check URI instead of the Spring j_spring_security_check.
We also need to specify the security constraints within the web.xml such as form-based login and the security role names.

Note that although we will be using Spring Security for specifying our access control rules, we still need to specify some access control within the web.xml so that the JEE container knows when to request authentication from the user.


    <filter>
        <!-- <filter-name>springSecurityFilterChain</filter-name> -->
        <filter-name>filterChainProxy</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <!-- <filter-name>springSecurityFilterChain</filter-name> -->
        <filter-name>filterChainProxy</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

       <!-- for container based authentication -->       
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>file</realm-name>
       <form-login-config>
              <form-login-page>/login</form-login-page>
              <form-error-page>/login</form-error-page>
       </form-login-config>       
    </login-config>

    <security-role>
        <role-name>ROLE_CONSUMER_SUPPORT</role-name>
    </security-role>
    <security-role>
        <role-name>ROLE_CONSUMER_SUPPORT_READ_ONLY</role-name>
    </security-role>
    
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>All areas</web-resource-name>
            <url-pattern>/users/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ROLE_CONSUMER_SUPPORT_READ_ONLY</role-name>
            <role-name>ROLE_CONSUMER_SUPPORT</role-name>
        </auth-constraint>
    </security-constraint>
 

We have also replaced the springSecurityFilterChain bean with the filterChainProxy bean.This allows specifying (via the Spring context file) which Spring bean filters will be involved in the security process.

Note
See the source code of Spring Security Config's HttpSecurityBeanDefinitionParser#registerFilterChainProxyIfNecessary method to understand how springSecurityFilterChain is used as the default bean id for the FilterChainProxy when using the http security namespace.

So which filters are required to configure the FilterChainProxy for this container based pre-authenticated scenario?
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">

    <sec:filter-chain-map path-type="ant">
        <sec:filter-chain pattern="/**" filters="sif,j2eePreAuthFilter,logoutFilter,etf,fsi"/>
    </sec:filter-chain-map>
</bean>
The above shows five filters that have been specified in the Spring Security pre-auth sample. Lets see what each one is responsible for.


<bean id="sif" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

<bean id="j2eePreAuthFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationDetailsSource">
            <bean class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
                <property name="mappableRolesRetriever">
                    <bean class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever" />
                </property>
                <property name="userRoles2GrantedAuthoritiesMapper">
                    <bean class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
                        <property name="convertAttributeToUpperCase" value="true"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
The SecurityContextPersistenceFilter is responsible for populating the SecurityContextHolder and is required to be the first filter to execute.
The J2eePreAuthFilter is what retrieves the JEE user principle name and associated roles for the pre-authenticated principle. As the container has performed the authentication, the user principle is available with associated roles and these are mapped to Spring's GrantedAuthority instances.

<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">

    <constructor-arg value="/"/>
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </list>
    </constructor-arg>
</bean>
The LogoutFilter will invoke the configured list of LogoutHandler implementations and then direct the user to the url specified in the first argument. It is also possible to provide a LogoutSuccessHandler to implement any custom logic after a successful logout. The LogoutHandler's are responsible for implementing the actual logout behaviour such as invalidating the session.

<bean id="preAuthenticatedProcessingFilterEntryPoint"            class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>


<bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint" ref="preAuthenticatedProcessingFilterEntryPoint"/>
</bean>
The ExceptionTranslationFilter is responsible for handling any AccessDeniedException and AuthenticationException's thrown within the filter chain and mapping them to HTTP responses. It will delegate to the authenticationEntryPoint on an AuthenticationException. In the pre-authenticated scenario, the authenticationEntryPoint will simply return the HTTP error code 403 rather than commence the re-authentication process.

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>


<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="allowIfAllAbstainDecisions" value="false"/>
    <property name="decisionVoters">
        <list>
            <ref bean="roleVoter"/>
        </list>
    </property>
</bean>

<bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
    <property name="securityMetadataSource">
        <sec:filter-invocation-definition-source>
            <sec:intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
            <sec:intercept-url pattern="/secure/**" access="ROLE_USER"/>
            <sec:intercept-url pattern="/**" access="ROLE_USER"/>
        </sec:filter-invocation-definition-source>
    </property>
</bean>
The final filter in the chain is the FilterSecurityInterceptor that implements the access control rules for HTTP resources similar to the ones defined in the HTTP security namespace. The FilterSecurityInterceptor will first check if authentication is required, and then ask the accessDecisionManager to decide whether to grant access to a request resource. The AccessDecisionManager collaborates with the configured list of AccessDecisionVoters to resolve the authorization request.

These filters and some changes to the web.xml and login, logout forms enable transitioning a web application based on Spring Security authentication to one that uses container based authentication.

Finally, depending on the application server used, there will be some configuration required for mapping the JEE security roles.
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>

       <context-root>preauth</context-root>
       <security-role-mapping>
              <role-name>ROLE_USER</role-name>
              <group-name>users</group-name>
       </security-role-mapping>
       <security-role-mapping>
              <role-name>ROLE_SUPERVISOR</role-name>
              <group-name>managers</group-name>
       </security-role-mapping>  
</glassfish-web-app>

glassfish-web.xml