Pentaho SSO Setup using CAS and LDAP

Hello.  Today I’m going to be discussing how to go about setting up SSO (Single Sign On) support for Pentaho using CAS (Central Authentication Service) and an LDAP (Lightweight Directory Access Protocol) Server.  By doing this, you can allow Pentaho to utilize the same usernames and passwords as your other programs requiring authentication, removing the burden of having to track and maintain multiple usernames per employee, as well as allow CAS compatible programs to share session data so that end users only have to log in once for all of them.  This guide is meant to focus more on setting up CAS than anything else, so if you need a test LDAP server (and are using Ubuntu) I would recommend following this tutorial for setting up OpenLDAP up to the section “Further Configuration”: https://help.ubuntu.com/11.04/serverguide/C/openldap-server.html

Special Thanks to Nilesh Patil, whose blog gave me a good basis to start from: http://pentahointegra.blogspot.com/2011/04/cas-with-pentaho-community-edition.html

Some security things to note before getting started.  Single Sign On does not mean Single Sign Off, due to the different ways web-based applications can retain session data.  Also note that this does not require an https connection, and thus is not ideal for things outside of a LAN or VPN connection.  It is possible to do this set up using https, but it requires more steps.  Addressing these sorts of issues will probably be the topic of a later post.

This guide is accurate for Pentaho CE 3.8 and CAS Server 3.4.8.

Installing CAS Server

  1. Download the most recent CAS Server from http://www.jasig.org/cas/download and unzip it.
  2. In the newly created directory, navigate to the modules directory, which will contain various jar and war files.
  3. Move the war file for the CAS server webapp (e.g. cas-server-webapp-3.4.8.war) to tomcat’s webapp directory.  If using the standard one that is part of Pentaho, this directory would be biserver-ce/tomcat/webapps.  Since war files automatically create a directory that shares the name of the war file, it might be a good idea to rename it to something more convenient before placing it in the webapps directory.
  4. If the tomcat server is not already running, start it.  The war file will be automatically inflated into its own directory.
  5. In a web browser, navigate to the subdirectory on the tomcat server, such as http://localhost:8080/cas-server-webapp-3.4.8/ which will bring you to the CAS main login screen if working correctly.  The default authentication that CAS uses merely requires the username and password entered to match for a successful authentication.

 

Connecting Pentaho to CAS

  1. Download spring-security-cas-client-2.0.5.RELEASE.jar from a site such as http://www.jarvana.com/jarvana/browse/org/springframework/security/spring-security-cas-client/2.0.5.RELEASE/.  Note that it needs to be this version of the jar file, as later versions do not contain the necessary classes.
  2. Download cas-client-core-3.1.10.jar from a site such as http://www.jarvana.com/jarvana/archive-details/org/jasig/cas/cas-client-core/3.1.10/cas-client-core-3.1.10.jar.  A later version should also be sufficient for this.
  3. Move both of these jar files to biserver-cetomcatwebappspentahoWEB-INFlib.
  4. Edit the file applicationContext-spring-security.xml in the directory biserver-cepentaho-solutionssystem.  Because of the widespread number of changes, the whole document will be reproduced here:

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN” “http://www.springsource.org/dtd/spring-beans.dtd”>

<!–+
| Application context containing FilterChainProxy.
+–>
<beans default-autowire=”no” default-dependency-check=”none” default-lazy-init=”false”>

<!– ======================== FILTER CHAIN ======================= –>
<!– overridden from applicationContext-spring-security.xml to enable CAS –>
<bean id=”filterChainProxy” class=”org.springframework.security.util.FilterChainProxy” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”filterInvocationDefinitionSource”>
<value>
<![CDATA[CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=securityContextHolderAwareRequestFilter,httpSessionContextIntegrationFilter,logoutFilter,casProcessingFilter,basicProcessingFilter,requestParameterProcessingFilter,anonymousProcessingFilter,pentahoSecurityStartupFilter,exceptionTranslationFilter,filterInvocationInterceptor,casSingleSignOutFilter]]>
</value>
</property>
</bean>
<!– ===================== HTTP REQUEST SECURITY ==================== –>
<bean id=”serviceProperties” class=”org.springframework.security.ui.cas.ServiceProperties” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”service” value=”http://localhost:8080/pentaho/j_spring_cas_security_check”/>
<property name=”sendRenew” value=”false”/>

</bean>
<!– replaces authenticationProcessingFilter in filterChainProxy above –>
<bean id=”casProcessingFilter” class=”org.springframework.security.ui.cas.CasProcessingFilter” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”authenticationManager”>
<ref bean=”authenticationManager”/>
</property>
<property name=”authenticationFailureUrl” value=”/public/casFailed”/>
<property name=”defaultTargetUrl” value=”/”/>
<property name=”filterProcessesUrl” value=”/j_spring_cas_security_check”/>
</bean>

<bean id=”casSingleSignOutFilter” class=”org.jasig.cas.client.session.SingleSignOutFilter”>
</bean>
<bean id=”casSingleSignOutHttpSessionListener” class=”org.jasig.cas.client.session.SingleSignOutHttpSessionListener”>
</bean>
<!– overridden from applicationContext-spring-security.xml –>

<bean id=”exceptionTranslationFilter”  class=”org.springframework.security.ui.ExceptionTranslationFilter” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”authenticationEntryPoint”>
<ref local=”casProcessingFilterEntryPoint”/>
</property>
<property name=”accessDeniedHandler”>
<bean class=”org.springframework.security.ui.AccessDeniedHandlerImpl” />
</property>
</bean>

<bean id=”casProcessingFilterEntryPoint” class=”org.springframework.security.ui.cas.CasProcessingFilterEntryPoint” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”loginUrl” value=”http://localhost:8080/cas-server-webapp-3.4.8/login”/>
<property name=”serviceProperties”>
<ref local=”serviceProperties”/>
</property>
</bean>

<!– overridden from applicationContext-spring-security.xml –>
<bean id=”authenticationManager” class=”org.springframework.security.providers.ProviderManager” autowire=”default” dependency-check=”default” lazy-init=”default”>
<property name=”providers”>
<list>
<!–ref bean=”daoAuthenticationProvider” /–>
<ref bean=”anonymousAuthenticationProvider”/>
<ref bean=”casAuthenticationProvider”/>
</list>
</property>
</bean>

<bean id=”casAuthenticationProvider” class=”org.springframework.security.providers.cas.CasAuthenticationProvider”>
<property name=”userDetailsService”>
<ref bean=”userDetailsService”/>
</property>
<property name=”serviceProperties”>
<ref local=”serviceProperties”/>
</property>
<property name=”ticketValidator”>
<ref local=”ticketValidator”/>
</property>
<property name=”key” value=”my_password_for_this_auth_provider_only”/>
</bean>

<bean id=”ticketValidator” class=”org.jasig.cas.client.validation.Cas20ServiceTicketValidator” autowire=”default” dependency-check=”default” lazy-init=”default”>
<constructor-arg index=”0″ value=”http://localhost:8080/cas-server-webapp-3.4.8″/>
</bean>
<!– overridden from applicationContext-spring-security.xml to specify logoutSuccessUrl as CAS logout page –>

<bean id=”logoutFilter” class=”org.springframework.security.ui.logout.LogoutFilter” autowire=”default” dependency-check=”default” lazy-init=”default”>
<constructor-arg value=”http://localhost:8080/cas-server-webapp-3.4.8/logout”/>
<constructor-arg>
<list>
<bean class=”org.pentaho.platform.web.http.security.PentahoLogoutHandler”/>
<bean class=”org.springframework.security.ui.logout.SecurityContextLogoutHandler”/>
</list>
</constructor-arg>
<property name=”filterProcessesUrl” value=”/Logout”/>
</bean>

<!– ======================== AUTHENTICATION ======================= –>
<!–    <bean id=”authenticationManager” class=”org.springframework.security.providers.ProviderManager”>
<property name=”providers”>
<list>
<ref bean=”daoAuthenticationProvider” />
<ref local=”anonymousAuthenticationProvider” />
</list>
</property>
</bean>
–>
<!– Automatically receives AuthenticationEvent messages –>
<bean id=”loggerListener”
class=”org.springframework.security.event.authentication.LoggerListener” />
<bean id=”basicProcessingFilter”
class=”org.springframework.security.ui.basicauth.BasicProcessingFilter”>
<property name=”authenticationManager”>
<ref local=”authenticationManager” />
</property>
<property name=”authenticationEntryPoint”>
<ref local=”basicProcessingFilterEntryPoint” />
</property>
</bean>
<bean id=”basicProcessingFilterEntryPoint”
class=”org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint”>
<property name=”realmName” value=”Pentaho Realm” />
</bean>

<!– custom Pentaho begin –>
<bean id=”requestParameterProcessingFilter”
class=”org.pentaho.platform.web.http.security.RequestParameterAuthenticationFilter”>
<property name=”authenticationManager”>
<ref local=”authenticationManager” />
</property>
<property name=”authenticationEntryPoint”>
<ref local=”requestParameterProcessingFilterEntryPoint” />
</property>
</bean>
<bean id=”requestParameterProcessingFilterEntryPoint”
class=”org.pentaho.platform.web.http.security.RequestParameterFilterEntryPoint” />
<bean id=”pentahoSecurityStartupFilter”
class=”org.pentaho.platform.web.http.security.SecurityStartupFilter”>
<property name=”injectAnonymous” value=”true” />
</bean>
<!– custom Pentaho end –>
<bean id=”anonymousProcessingFilter”
class=”org.springframework.security.providers.anonymous.AnonymousProcessingFilter”>
<property name=”key” value=”foobar” />
<property name=”userAttribute” value=”anonymousUser,Anonymous” />
</bean>
<bean id=”anonymousAuthenticationProvider”
class=”org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider”>
<property name=”key” value=”foobar” />
</bean>
<bean id=”httpSessionContextIntegrationFilter”
class=”org.springframework.security.context.HttpSessionContextIntegrationFilter” />
<!–
<bean id=”logoutFilter” class=”org.springframework.security.ui.logout.LogoutFilter”>
<constructor-arg value=”/index.jsp” />
<constructor-arg>
<list>
<bean class=”org.pentaho.platform.web.http.security.PentahoLogoutHandler” />
<bean
class=”org.springframework.security.ui.logout.SecurityContextLogoutHandler” />
</list>
</constructor-arg>
<property name=”filterProcessesUrl” value=”/Logout” />
</bean>
–>
<bean id=”securityContextHolderAwareRequestFilter”
class=”org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter” />
<bean id=”httpSessionReuseDetectionFilter”
class=”org.pentaho.platform.web.http.security.HttpSessionReuseDetectionFilter”>
<property name=”filterProcessesUrl” value=”/j_spring_security_check” />
<property name=”sessionReuseDetectedUrl” value=”/Login?login_error=2″ />
</bean>

<bean id=”httpRequestAccessDecisionManager” class=”org.springframework.security.vote.AffirmativeBased”>
<property name=”allowIfAllAbstainDecisions” value=”false” />
<property name=”decisionVoters”>
<list>
<ref bean=”roleVoter” />
</list>
</property>
</bean>
<!–
Note the order that entries are placed against the
objectDefinitionSource is critical. The FilterSecurityInterceptor will
work from the top of the list down to the FIRST pattern that matches
the request URL. Accordingly, you should place MOST SPECIFIC (ie
a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*)
expressions last
–>
<bean id=”filterInvocationInterceptor”
class=”org.springframework.security.intercept.web.FilterSecurityInterceptor”>
<property name=”authenticationManager”>
<ref local=”authenticationManager” />
</property>
<property name=”accessDecisionManager”>
<ref local=”httpRequestAccessDecisionManager” />
</property>
<property name=”objectDefinitionSource”>
<value>
<!–
Note – the “=Nobody” below is saying that resource URLs with those
patterns not be available through a web call.
–>
<![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
A/docs/.*Z=Anonymous,Authenticated,ea_admin
A/mantlelogin/.*Z=Anonymous,Authenticated,ea_admin
A/mantle/mantleloginservice/*Z=Anonymous,Authenticated,ea_admin
A/mantle/.*Z=Authenticated,ea_admin
A/welcome/.*Z=Anonymous,Authenticated,ea_admin
A/public/.*Z=Anonymous,Authenticated,ea_admin
A/login.*Z=Anonymous,Authenticated,ea_admin
A/ping/alive.gif.*Z=Anonymous,Authenticated,ea_admin
A/j_spring_security_check.*Z=Anonymous,Authenticated,ea_admin
A/getimage.*Z=Anonymous,Authenticated,ea_admin
A/getresource.*Z=Anonymous,Authenticated,ea_admin
A/admin.*Z=Admin,uidai_admin
A/auditreport.*Z=Admin,uidai_admin
A/auditreportlist.*Z=Admin,uidai_admin
A/versioncontrol.*Z=Admin,uidai_admin
A/propertieseditor.*Z=Admin,uidai_admin
A/propertiespanel.*Z=Admin,uidai_admin
A/subscriptionadmin.*Z=Admin,uidai_admin
A/resetrepository.*Z=Admin,uidai_admin
A/viewaction.*solution.admin.*Z=Admin,uidai_admin
A/scheduleradmin.*Z=Admin,uidai_admin
A/publish.*Z=Admin,uidai_admin
A/logout.*Z=Anonymous
A/solutionrepositoryservice.*component=delete.*solution=system.*Z=Nobody
A/solutionrepositoryservice.*solution=system.*component=delete.*Z=Nobody
.*system.*pentaho.xml.*=Nobody
.*system.*applicationcontext.*.xml.*=Nobody
.*system.*pentahoobjects.spring.xml.*=Nobody
.*system.*pentahosystemconfig.xml.*=Nobody
.*system.*adminplugins.xml.*=Nobody
.*system.*plugin.properties.*=Nobody
.*system.*publisher_config.xml.*=Nobody
.*system.*sessionstartupactions.xml.*=Nobody
.*system.*systemlisteners.xml.*=Nobody
.*system.*hibernate.*=Nobody
.*system.*birt/.*=Nobody
.*system.*dialects/.*=Nobody
.*system.*google/.*=Nobody
.*system.*jasperreports/.*=Nobody
.*system.*jfree/.*=Nobody
.*system.*kettle/.*=Nobody
.*system.*logs/.*=Nobody
.*system.*metadata/.*=Nobody
.*system.*mondrian/.*=Nobody
.*system.*olap/.*=Nobody
.*system.*quartz/.*=Nobody
.*system.*simple-jndi/.*=Nobody
.*system.*smtp-email/.*=Nobody
.*system.*ui/.*=Nobody
.*system.*analysistemplate.tpl.*=Nobody
.*system.*../.*=Nobody
A/.*Z=Authenticated,ea_admin
]]>
</value>
</property>
</bean>
</beans>

  1. Restart the server and navigate to the Pentaho home page in a web browser (http://localhost:8080/pentaho). If successful, it will redirect you to the CAS login page, and once authenticated redirect back to the main body of pentaho.

 

Connecting CAS to LDAP

  1. Download Spring-ldap-1.3.1.jar from a site such as http://www.jarvana.com/jarvana/archive-details/org/springframework/ldap/spring-ldap/1.3.1.RELEASE/spring-ldap-1.3.1.RELEASE-all.jar or similar version.
  2. Download cas-server-ldap-3.0.5.jar or similar from a site such as http://developer.jasig.org/repo/content/repositories/m1/cas/jars/
  3. Download LdapTemplate-1.0.2.jar or similar version from a site such as http://mvnrepository.com/artifact/net.sf.ldaptemplate/ldaptemplate/1.0.2
  4. Place all of these jar files in the CAS Server’s WEB-INF/lib directory (In this case, biserver-ce/tomcat/webapps/cas-server-webapp-3.4.8/WEB-INF/lib).
  5. Edit the deployerConfigContext.xml file in CAS Server’s WEB-INF directory (biserver-ce/tomcat/webapps/cas-server-webapp-3.4.8/WEB-INF/) and add to the end before the final </beans>  tag:

<bean id=”contextSource” class=”org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapContextSource”>
<property name=”pooled” value=”false”/>
<property name=”urls”>
<list>
<value>ldap://localhost:389</value>
</list>
</property>
<property name=”password” value=”secret”/>
<property name=”baseEnvironmentProperties”>
<map>
<entry key=”java.naming.security.authentication” value=”simple” />
</map>
</property>
</bean>

  1. In the same file, replace the Bean named SimpleTestUsernamePasswordAuthenticationHandler with this:
  2. <bean class=”org.jasig.cas.adaptors.ldap.BindLdapAuthenticationHandler”>
    <property name=”filter” value=”uid=%u” />
    <property name=”searchBase” value=”ou=people,dc=datamensional-Virtualbox” />
    <property name=”contextSource” ref=”contextSource” />
    </bean>
  3. In a web browser, navigate to the CAS login page or any app integrated with it.  You should be able to log in using the credentials provided on the connected ldap server.
  1. Download the most recent CAS Server from http://www.jasig.org/cas/download and unzip it.

  2. In the newly created directory, navigate to the modules directory, which will contain various jar and war files.

  3. Move the war file for the CAS server webapp (e.g. cas-server-webapp-3.4.8.war) to tomcat’s webapp directory.  If using the standard one that is part of Pentaho, this directory would be biserver-ce/tomcat/webapps.  Since war files automatically create a directory that shares the name of the war file, it might be a good idea to rename it to something more convenient before placing it in the webapps directory.

  4. If the tomcat server is not already running, start it.  The war file will be automatically inflated into its own directory.

  5. In a web browser, navigate to the subdirectory on the tomcat server, such as http://localhost:8080/cas-server-webapp-3.4.8/ which will bring you to the CAS main login screen if working correctly.  The default authentication that CAS uses merely requires the username and password entered to match for a successful authentication.

42 thoughts on “Pentaho SSO Setup using CAS and LDAP”

Comments are closed.