In my last Alfresco project I had to implement Single Sign On (SSO) with a CAS server (http://www.jasig.org/cas). This turned out to be a bit trickier than I initially thought. I found some resources about the subject that looked promising:
- http://holisticsecurity.wordpress.com/2011/02/19/web-sso-between-liferay-and-alfresco-with-cas-and-penrose-part-22/
- http://akselsarchitecture.blogspot.com/2010/09/cas-sso-for-alfresco-33-and-share.html
- http://translate.google.com/translate?u=http://blog.atolcd.com/%3Fp%3D115&sl=fr&tl=en
I started off using the files provided by the http://akselsarchitecture.blogspot.com/2010/09/cas-sso-for-alfresco-33-and-share.html link and set about to test it with Alfresco 3.4. I pretty quickly got Alfresco Explorer to work with CAS Server 3.4.6. This was because CAS does not require you to use SSL in this scenario.
When it came to getting it to work with Alfresco Share (that authenticates via Alfresco Repo/Explorer – proxy authentication) it was a different story. I could not get it to work no matter what I did. If I would have looked closer at the URLs in some of the files that I downloaded I would have seen that https was used in a lot of places. This was the key to the problem. Because Share would be using the proxy authentication mechanism provided by CAS, it required both CAS and the Alfresco server to be configured to support SSL.
Another thing to note in a proxy authentication scenario is that CAS will call back to the Alfresco server so you need to make sure that the the CAS server has access to the Alfresco server (https port).
Further on, the request to the proxy callback URL is only made if it is protected by SSL with a valid
certificate that the server can verify, including any necessary certificate chain. If the server cannot verify the certificate the call to the proxy callback url is never attempted and this can only be noticed in the CAS server log files.
certificate that the server can verify, including any necessary certificate chain. If the server cannot verify the certificate the call to the proxy callback url is never attempted and this can only be noticed in the CAS server log files.
A link that I found useful in explaining proxy authentication scenarios was this one:
Now let’s walk through from scratch how to get this working. The best thing you can do is start locally, with Alfresco and CAS running in the same Tomcat instance, and get it to work there before moving on to a deployment with CAS and Alfresco servers on different hosts.
Configuring SSL for Alfresco Tomcat
As part of the proxy authentication scenario the CAS server will call back to the Alfresco server and this call need to be secure via SSL. To configure support for https in Alfresco Tomcat (Version 6.0.29 in the Alfresco 3.4 Enterprise that I am using) do as follows:
1) Enable the SSL HTTP/1.1 Connector on port 8443, this is done in file /alfresco/tomcat/conf/server.xml:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="C:/Users/mbergljung/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS" />
2) Create a certificate keystore (the keystoreFile points to it) for Tomcat to use and generate a self-signed certificate at the same time for localhost. The following command will generate a .keystore file in user home, make sure to use password changeit everywhere:
C:\>keytool -genkey -alias tomcat -keyalg RSA
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: Dev
What is the name of your organization?
[Unknown]: Ixxus
What is the name of your City or Locality?
[Unknown]: London
What is the name of your State or Province?
[Unknown]: London
What is the two-letter country code for this unit?
[Unknown]: UK
Is CN=localhost, OU=Dev, O=Ixxus, L=London, ST=London, C=UK correct?
[no]: Yes
Enter key password for <tomcat>
(RETURN if same as keystore password):
The first and last name need to be the domain of the Tomcat server, in our case it will be localhost as we will be running both Alfresco and CAS locally in the same Tomcat instance, this means that we later on have to specify all URLs as http(s)://localhost:....
If you have specified an incorrect first and last name (i.e. domain) then you will see an error message such as the following:
java.security.cert.CertificateException: No name matching localhost found
SSL uses asymmetric encryption (i.e. public key cryptography) and because we are using a self-signed certificate we will get a warning message in the browser to accept/trust or ignore the certificate.
On the other hand, a program with no human interaction, like CAS calling Alfresco, will not give us this possibility but instead just throw an exception like the following:
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This error message means that the Public Key Infrastructure (PKI) cannot find a path from the self-signed certificate for our local Tomcat to one of the Certificate Authorities (CA) certificates. Meaning we could be talking to any local Tomcat server, really.
We will see in the next section how we can import the self-signed certificate into any Java runtime environment that wants to talk to Tomcat on localhost (in this case it would be only local JREs).
3) Disable the APR library loader in the tomcat configuration file /alfresco/tomcat/conf/server.xml:
<!--APR library loader. Documentation at /docs/apr.html
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />-->
This is because a bug in Tomcat 6.0.2x where it starts up the Apache Portable Runtime (APR)
engine to listen on 8443 when that port is already taken by the standard blocking IO connector that we defined in step 1, an error like the following can be seen:
java.lang.Exception: Socket bind failed: [730048] Only one usage of each socket address (protocol/network address/port) is normally permitted.
at org.apache.tomcat.util.net.AprEndpoint.init(AprEndpoint.java:647)
at org.apache.tomcat.util.net.AprEndpoint.start(AprEndpoint.java:754)
at org.apache.coyote.http11.Http11AprProtocol.start(Http11AprProtocol.java:137)
Ok that should do it; Alfresco Tomcat is now enabled for secure connections.
Tell Alfresco JRE about the Self-sign certificate
The self-signed certificate used by Tomcat needs to be imported into the Java Runtime environment that Alfresco uses. In my case I installed the full Alfresco package with JRE, MySQL, Tomcat etc. So I needed to import the self-signed cert into the Alfresco JRE. If we do not do this then we will keep getting the following errors and no luck logging into Alfresco Share:
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
To import the cert do as follows:
1) Export it from the keystore we generated for Tomcat
C:\>keytool -export -rfc -alias tomcat -file tomcat.crt –keystore c:/Users/mbergljung/.keystore -storepass changeit
Certificate stored in file <tomcat.crt>
2) Then import it into the Alfresco JRE certificate store:
C:\>keytool -import -alias tomcat -file tomcat.crt -keystore X:/Alfresco3.4E/java/jre/lib/security/cacerts -storepass changeit
Owner: CN=localhost, OU=Dev, O=Ixxus, L=London, ST=London, C=UK
Issuer: CN=localhost, OU=Dev, O=Ixxus, L=London, ST=London, C=UK
Serial number: 4df48ecd
Valid from: Sun Jun 12 11:02:53 BST 2011 until: Sat Sep 10 11:02:53 BST 2011
Certificate fingerprints:
MD5: F1:69:DA:CE:0D:F7:D3:9F:9F:9C:12:9B:8B:54:3F:B8
SHA1: 82:7A:0D:83:DC:99:07:4E:3A:98:95:13:52:EB:B1:20:EB:C9:89:4B
Signature algorithm name: SHA1withRSA
Version: 3
Trust this certificate? [no]: yes
Certificate was added to keystore
Note. In a deployment where the CAS server runs on one Tomcat instance on one host, and the Alfresco server runs on one Tomcat instance on another host, you would need to import the Alfresco Tomcat self-signed cert into the JRE that runs CAS.
Note 2. In a production scenario you would not need to worry about this as you would have properly signed certificates from a Certificate Authority (CA), and these will be automatically recognized as trusted by the JRE you are using.
Installing CAS in the same Tomcat instance as Alfresco
We will use a local CAS running in the same Tomcat instance as Alfresco to test the complete configuration. Download CAS version 3.4.x from http://www.jasig.org/cas and unzip in some folder. Then grab the .../cas-server-3.4.6/modules/cas-server-webapp-3.4.6.war web application and drop it into the alfresco/tomcat/webapps directory.
This is the only thing we need to do to start using CAS as it provides a testing mode out of the box where you can just supply the same username and password for a successful authentication.
To test CAS go to https://localhost:8443/cas-server-webapp-3.4.6/login and test logging in with the same username and password. You will also test your SSL configuration at the same time.
Logout afterwards with https://localhost:8443/cas-server-webapp-3.4.6/logout
Configuring CAS SSO for Alfresco Explorer
To setup SSO for Alfresco Explorer with CAS I will use the files/code available from the following link:
I have modified these files a bit as they have hard coded URLs. You can get all my files from my download page; a link to it is available at the right side of the blog page.
The following steps are needed to configure SSO for Alfresco Explorer:
1) Enable external authentication for Alfresco Explorer/Repo (this turns off the standard alfrescoNtlm1 authenticator). Open up the alfresco/tomcat/shared/classes/alfresco-global.properties file and add the following lines:
### Enable CAS SSO Authentication via EXTERNAL subsystem
authentication.chain=cas:external
Note. This does not turn on any extra CAS SSO authentication functionality or anything; we are just telling Alfresco that authentication will be handled via an external authentication mechanism and Alfresco should not worry about it.
2) Update the tomcat/webapps/alfresco/WEB-INF/web.xml file with some new filters that will talk to the CAS server and comment out Alfresco’s default authentication filter (an example web.xml can be found in the alfresco_war\WEB-INF directory of the source code package for this blog):
<!-- Commented out Alfresco's default authentication filter in order to be able to configure CAS filters
<filter>
<filter-name>Authentication Filter</filter-name>
<description>Authentication filter mapped only to faces URLs. Other URLs generally use proprietary means to talk to the AuthenticationComponent</description>
<filter-class>org.alfresco.repo.web.filter.beans.BeanProxyFilter</filter-class>
<init-param>
<param-name>beanName</param-name>
<param-value>AuthenticationFilter</param-value>
</init-param>
</filter>
-->
<!-- Adding CAS Authentication filters and replacing Alfresco's default one -->
<filter>
<filter-name>Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://localhost:8443/cas-server-webapp-3.4.6/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://localhost:8443/cas-server-webapp-3.4.6</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<filter-class>org.mycompany.cms.authentication.CasAuthenticationFilter</filter-class>
</filter>
<!-- End adding CAS authentication filters -->
The first two filters are provided by the CAS client library and the third one we have to make some changes to and provide in a library. Notice that the CAS server is accessed via a secure connection (https) but the Alfresco Explorer application is accessed via a plane connection (http).
Now configure the filter mappings as follows:
<!-- New CAS filter mappings starts here -->
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/faces/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/faces/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/navigate/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/navigate/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/navigate/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/command/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/command/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/command/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/download/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/download/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/download/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/template/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/template/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/template/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/n/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/n/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/n/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/c/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/c/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/c/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/t/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/t/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/t/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<url-pattern>/d/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/d/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/d/*</url-pattern>
</filter-mapping>
<!-- New CAS filter mappings end here -->
3) Make sure the URLs in the org.mycompany.cms.authentication.LoginCas Web Script controller class are correct:
public class LoginCas extends DeclarativeWebScript {
private final static String CAS_WEBAPP_URL = "https://localhost:8443/cas-server-webapp-3.4.6";
private final static String ALFRESCO_WEBAPP_URL = "http://localhost:8080/alfresco";
4) Configure the LoginCas web script controller in a Spring context file:
The declarative web script needs to be loaded and setup from a Spring bean configuration. If you are creating a new AMP file for this then add the following Spring bean configuration to the module-context.xml:
<bean id="webscript.org.mycompany.authentication.logincas.get" class="org.mycompany.cms.authentication.LoginCas" parent="webscript">
<property name="authenticationService" ref="authenticationService" />
<property name="authenticationComponent" ref="authenticationComponent" />
</bean>
5) Lookup how to setup an AMP build project and use it to package together the web script controller, authentication filter, CAS library, and Spring context file
The following two classes from the alfresco_war package source code need to be compiled against the Alfresco 3.4 SDK and CAS library:
org.mycompany.cms.authentication.LoginCas
org.mycompany.cms.authentication.CasAuthenticationFilter
To compile these classes you need the CAS Client library (e.g. cas-client-core-3.1.12.jar), and it need to be packaged in the AMP as well.
6) Stop Tomcat if it is running, and delete alfresco/tomcat/webapps/alfresco
7) Now apply the Alfresco WAR extension AMP to the alfresco/tomcat/webapps/alfresco.war
8) Start Tomcat, and wait for the alfresco.war to be deployed, then stop Tomcat
9) Copy the modified web.xml to /alfresco/tomcat/webapps/alfresco/WEB-INF directory (the web.xml cannot be updated via AMP files)
10) Start Tomcat
11) Now test the SSO integration by going to http://localhost:8080/alfresco, this should take you to the CAS login page
12) If you hit logout in Alfresco Explorer it will not log you out from CAS, you can fix this by updating the alfresco/tomcat/webapps/alfresco/jsp/org/relogin.jsp (this can be included in the AMP as well) and adding a redirect as follows:
. . .
authCookie.setMaxAge(0);
response.addCookie(authCookie);
}
}
// Logout from CAS
response.sendRedirect("http https://localhost:8443/cas-server-webapp-3.4.6/logout");
%>
<body bgcolor=
Configuring CAS SSO for Alfresco Share
For this I also use files/code available from the following link:
I have modified these files a bit as they have hard coded URLs and I also added a more descriptive error message in one place. You can get all my files from my download page; a link to it is available at the right side of the blog page.
The following steps are needed to configure SSO for Alfresco Share:
1) Update the tomcat/webapps/share/WEB-INF/web.xml file with some new filters that will talk to the CAS server and comment out Alfresco’s default authentication filter (an example web.xml can be found in the share_war\WEB-INF directory of the source code package for this blog):
<!-- Adding CAS Authentication filters -->
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://localhost:8443/cas-server-webapp-3.4.6/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://localhost:8443/cas-server-webapp-3.4.6</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
<init-param>
<param-name>allowAnyProxy</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>proxyCallbackUrl</param-name>
<param-value>https://localhost:8443/share/proxyCallback</param-value>
</init-param>
<init-param>
<param-name>proxyReceptorUrl</param-name>
<param-value>/proxyCallback</param-value>
</init-param>
</filter>
<filter>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<filter-class>org.mycompany.cms.authentication.CasAuthenticationFilter</filter-class>
</filter>
<!-- End adding CAS authentication filters -->
The first two filters are provided by the CAS client library and the third one we have to make some changes to and provide in a library. Notice that the CAS server is accessed via a secure connection (https) but the Alfresco Share application is accessed via a plane connection (http). There is one exception though; the proxyCallbackUrl callback URL requires a secure connection to Alfresco. (One thing to note, if you access Alfresco Share via https the Multi File upload Flash component might not work).
Now configure the filter mappings as follows:
<!-- Adding CAS Authentication filter mappings -->
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Alfresco CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- End adding CAS Authentication filter mappings -->
2) Make sure the URL in the org.mycompany.cms.authentication.CasAuthenticationFilter class are correct:
public class CasAuthenticationFilter implements Filter {
private final static String ALFRESCO_WEBAPP_URL = "http://localhost:8080/alfresco";
3) Update the share-config-custom.xml with the new authenticator to use, the one that will call the LoginCas Web Script with a CAS ticket:
<!--Overriding endpoints to reference a remote Alfresco server –>-->
<config evaluator="string-compare" condition="Remote">
<remote>
<!-- Authenticator implementation used in CAS authentication scenario,
overrides the default alfresco-ticket authenticator
<class>org.alfresco.connector.AlfrescoAuthenticator</class> -->
<authenticator>
<id>alfresco-ticket</id>
<name>Alfresco Authenticator</name>
<description>Alfresco Authenticator</description>
<class>org.mycompany.cms.authentication.CasAlfrescoAuthenticator
</class>
</authenticator>
<!-- Connects to an Alfresco instance using ticket-based authentication,
overrides the default alfresco connector to use CAS ticket
authenticator-->
<connector>
<id>alfresco</id>
<name>Alfresco Connector</name>
<description>Connects to an Alfresco instance using ticket-based
authentication</description>
<class> org.springframework.extensions.webscripts.connector.AlfrescoConnector
</class>
<authenticator-id>alfresco-ticket</authenticator-id>
</connector>
<!-- Endpoint using external authentication via CAS-->
<endpoint>
<id>alfresco</id>
<name>Alfresco - user access</name>
<description>Access to Alfresco Repository WebScripts that require
external user authentication
</description>
<connector-id>alfresco</connector-id>
<endpoint-url>http://localhost:8080/alfresco/s</endpoint-url>
<identity>user</identity>
<external-auth>true</external-auth>
</endpoint>
</remote>
</config>
4) Lookup how to setup a JAR extension project for Alfresco Share and use it to package together the authentication filter, authenticator, and share-config-custom.xml
The following two classes from the share_war package source code need to be compiled against the Alfresco 3.4 SDK and CAS library:
org.mycompany.cms.authentication.CasAlfrescoAuthenticator
org.mycompany.cms.authentication.CasAuthenticationFilter
To compile these classes you need the CAS Client library (e.g. cas-client-core-3.1.12.jar).
5) Stop Tomcat if it is running
6) Copy the Share extension JAR plus the CAS library JAR into the alfresco/tomcat/webapps/share/WEB-INF/lib directory
7) Copy the modified web.xml to /alfresco/tomcat/webapps/share/WEB-INF directory
8) Start Tomcat
9) Now test the SSO integration by going to http://localhost:8080/share, this should take you to the CAS login page, unless you already have a session from login into Alfresco Explorer, then you should be automatically logged in
Putting the CAS server on a separate host
Deploying
the CAS web application and the Alfresco web applications into the same Apache
Tomcat server simplifies the configuration quite a lot. It is not a simple task
to move the CAS server to a separate host. Deploying the CAS server on its own
Apache Tomcat server will require some more re-configuration and more network
related issues to think about.
I am
assuming you have followed the article so far and have now got the solution
working with everything installed in the Alfresco Tomcat server, with a "localhost"
certificate for Tomcat.
The
following is a picture of the configuration I am setting up to demonstrate
having CAS on a separate host:
Both of the
Apache Tomcat servers have a keystore with a self-signed certificate. These
certificates are also imported into the Java environments that are used to run
the Tomcat servers. The CAS host is not connected to a domain so the hosts file
contains an entry for the Alfresco host (i.e. IXDV1210). The Alfresco host also
contains an entry in the hosts file for the CAS host (i.e. cashost).
It is
important that the 8443 port is open in the Windows Firewall for this to work
and this can easily be tested via a web browser call from each host when the
Tomcat servers have been configured for secure access.
Setting up the new CAS server
This section goes through how to setup the CAS server on a
separate host.
Installing CAS on a separate host
I am using a
second laptop running Windows Vista for the CAS server. I have installed Apache
Tomcat version 5.5 and copied the cas-server-webapp-3.4.6.war
file into the /webapps directory.
The Tomcat server configuration looks like this (from /conf/server.xml), see previous configuration information in this
article for more information:
<Server port="8005"
shutdown="SHUTDOWN">
<!-- <Listener
className="org.apache.catalina.core.AprLifecycleListener" />-->
<Listener
className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener
className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
/>
<Listener
className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
. . .
<Service
name="Catalina">
<Connector
port="8080" address="0.0.0.0"
maxHttpHeaderSize="8192"
maxThreads="150"
minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443"
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true" />
<Connector
port="8443" address="0.0.0.0"
maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https"
secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="C:/Users/martin/.keystore"
keystorePass="changeit"/>
. . .
One thing is
different here from the Tomcat 6.0 configuration mentioned previously in this
article. The address="0.0.0.0" need to be specified otherwise Tomcat 5.5
will not bind to all interfaces and we will not be able to access the server
remotely.
Generating a self-signed certificate for the Tomcat server that runs CAS
As the CAS server
is accessed securely (which is mandatory) we need to create the keystore we are
referencing in the server.xml file configuration
above, and add a certificate to it. The certificate needs to have the alias and
CN properties set to the hostname of the CAS server. The Vista laptop that I am
using is not hooked up to a domain so I am just going to generate the
certificate with a hostname of cashost
(the Alfresco Tomcat server will use cashost to access CAS). I used the
following command to generate the self-signed certificate and keystore in my
user home directory:
C:\>keytool -genkey -alias cashost
-keyalg RSA
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: cashost
What is the name of your organizational
unit?
[Unknown]: Dev
What is the name of your organization?
[Unknown]: Ixxus
What is the name of your City or
Locality?
[Unknown]: London
What is the name of your State or
Province?
[Unknown]: London
What is the two-letter country code for
this unit?
[Unknown]: UK
Is CN=cashost, OU=Dev, O=Ixxus,
L=London, ST=London, C=UK correct?
[no]: Yes
Enter key password for <tomcat>
(RETURN if same as keystore password):
Testing the CAS installation
Before
moving on we can test the CAS installation and make sure it works as expected.
Access the
CAS application as follows:
https://localhost:8443/cas-server-webapp-3.4.6/login
We should
get a login prompt where we can use same username and password to test logging
in. We will not access via cashost (it will not work...), that hostname will
only be used from the Alfresco server.
Letting the CAS host know about the Alfresco host
The CAS
server will call back to the Alfresco Share web application as part of the
proxy authentication scenario. This call needs to be secured and the Tomcat
instance that runs Alfresco will have a new certificate generated with alias
and CN set to IXDV1210.ixxus.co.uk. This hostname and domain is however not
known by the Windows Vista laptop running Tomcat and the CAS web application,
so we need to add it to the C:\Windows\System32\drivers\etc\hosts
file as follows:
192.168.0.2 IXDV1210.ixxus.co.uk
Note. If both
your hosts are connected to a domain then this step is not needed.
Updating the Alfresco installation and removing the CAS server
This section goes through how to update the Alfresco configuration
to point to the new CAS server and how to remove currently installed CAS server.
Now stop Alfresco Tomcat if it is running!
Letting the Alfresco host know about the CAS host
The Alfresco
server will call the CAS server via the cashost
hostname. This is not a hostname that is known to the Alfresco host so we need
to update the hosts file with an IP for that hostname. Open up the C:\Windows\System32\drivers\etc\hosts
file and add the following line:
192.168.0.10 cashost
Note. If both
your hosts are connected to a domain then this step is not needed.
Remove CAS from the Tomcat server that runs Alfresco
As we are
running CAS from a separate host now we can remove it from the Alfresco Tomcat
webapps directory. Remove both the c:\Alfresco3.4\tomcat\webapps\cas-server-webapp-3.4.6.war
file and the c:\Alfresco3.4\tomcat\webapps\cas-server-webapp-3.4.6
directory.
Make sure the Alfresco host can get through to the CAS host on port 8443
The Alfresco
host and the CAS host needs to have port 8443 open in their firewalls,
otherwise it will not work. A quick way to test if it works is to use the
browser on each host and try and access the other host on port 8443.
If it is not
working open up "Allow program
through Windows Firewall -> Windows Firewall settings" and in the Exceptions tab click the Add port... button to let
through traffic on port 8443.
Updating the Login CAS Web Script
The login
CAS web script has the URL for the CAS server hard coded so we need to update
it as follows:
public class LoginCas extends DeclarativeWebScript {
private final static
String CAS_WEBAPP_URL =
"https://cashost:8443/cas-server-webapp-3.4.6";
And then we
need to rebuild the Alfresco Explorer extension AMP by running the deploy-alfresco-amp ant target. Then
start Alfresco and stop it again so the alfresco.war
is deployed, we will be updating the web.xml
for it in the next step.
Updating the web.xml for the Alfresco Explorer and the Alfresco Share web applications
The CAS web application
is now running on a separate host so we need to update the web.xml files for
the Alfresco Explorer and the Alfresco Share web applications, so that the URLs
point to the new CAS host (web.xml cannot be updated via the build project as
neither AMP or JAR extensions can update this file, you would need a Maven
project for that). First open up the tomcat/webapps/alfresco/WEB-INF/web.xml
file and update it so the following section looks like this:
<filter>
<filter-name>Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://cashost:8443/cas-server-webapp-3.4.6/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://cashost:8443/cas-server-webapp-3.4.6</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>Alfresco CAS Authentication
Filter</filter-name>
<filter-class>org.wwarn.cms.authentication.CasAuthenticationFilter</filter-class>
</filter>
Note that
the hostname for the CAS server is specified as cashost, which is resolved via the hosts file configuration. As mentioned before the CAS server
requests need to be secure (i.e. https).
Now, the
Alfresco Share web.xml also needs to
be updated, it’s located in the tomcat/webapps/share/WEB-INF
directory and needs to be updated so the following section looks like this:
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://cashost:8443/cas-server-webapp-3.4.6/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://cashost:8443/cas-server-webapp-3.4.6</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
<init-param>
<param-name>allowAnyProxy</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>proxyCallbackUrl</param-name>
<param-value>https://IXDV1210.ixxus.co.uk:8443/share/proxyCallback</param-value>
</init-param>
<init-param>
<param-name>proxyReceptorUrl</param-name>
<param-value>/proxyCallback</param-value>
</init-param>
</filter>
<filter>
<filter-name>Alfresco CAS Authentication
Filter</filter-name>
<filter-class>org.wwarn.cms.authentication.CasAuthenticationFilter</filter-class>
</filter>
Here we can
see also that all URLs need to be secure for the proxy authentication scenario
to work. Note the proxyCallbackUrl
that is used by CAS to call back to the service (i.e. Alfresco) during a proxy authentication
scenario.
Generating a new self-signed certificate for the Tomcat server that runs Alfresco
The Apache
Tomcat that currently runs the Alfresco web applications and the CAS web
application needs to have a new certificate generated as it currently has
"localhost" as alias and CN, which would not work when it is access
remotely (via the https://.../share/proxyCallback request made by the CAS
server).
The laptop
that I am running Alfresco Tomcat on has a hostname of IXDV1210 and is part of
the ixxus.co.uk domain. So I generated the new self-signed certificate in the
Tomcat keystore as follows:
C:\>keytool -genkey -alias
IXDV1210.ixxus.co.uk -keyalg RSA
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: IXDV1210.ixxus.co.uk
What is the name of your organizational
unit?
[Unknown]: Dev
What is the name of your organization?
[Unknown]: Ixxus
What is the name of your City or
Locality?
[Unknown]: London
What is the name of your State or
Province?
[Unknown]: London
What is the two-letter country code for
this unit?
[Unknown]: UK
Is CN=IXDV1210.ixxus.co.uk, OU=Dev, O=Ixxus,
L=London, ST=London, C=UK correct?
[no]: Yes
Enter key password for <tomcat>
(RETURN if same as keystore password):
I then also
removed the old “localhost” certificate so it is not picked up by mistake by
Tomcat:
C:\Alfresco3.4\java\bin>keytool
-delete -alias tomcat -keystore C:/Users/mbergljung/.keystore -storepass
changeit
Importing the CAS Host certificate into the Alfresco Host Java runtime environment
For the
Alfresco secure calls to the CAS server to work we need to import the CAS host
certificate into the Java runtime environment that runs the Tomcat with the
Alfresco server. This was done as follows; first export the certificate from
the keystore that Tomcat uses on the CAS Host:
C:\Program
Files\Java\jdk1.6.0_14\bin>keytool -export -rfc -alias cashost -file
c:/Users/martin/cashost.crt -keystore c:/Users/martin/.keystore -storepass
changeit
Certificate stored in
file <c:/Users/martin/cashost.crt>
Then import
it into the Java runtime environment on the Alfresco host (you need to first
copy the certificate file to the Alfresco host):
X:\Alfresco3.4\java\bin>keytool
-import -alias cashost -file Z:/temp/cashost.crt -keystore
X:/Alfresco3.4/java/jre/lib/security/cacerts -storepass changeit
The CAS host
is now trusted by the Java environment running the Alfresco server.
Note. if
you have a properly CA signed certificate then this import step is not needed
as the Java runtime environment will have a trust chain already setup to verify
the certificate.
Importing the Alfresco Host certificate into the CAS Host Java runtime environment
For the CAS
secure callback to Alfresco to work we need to import the Alfresco host
certificate into the Java runtime environment that runs the Tomcat with the CAS
server. This was done as follows; first export the certificate from the
keystore that Tomcat uses on the Alfresco Host:
X:\Alfresco3.4\java\bin>keytool
-export -rfc -alias IXDV1210.ixxus.co.uk -file alfrescohost.crt -keystore
c:/Users/mbergljung/.keystore -storepass changeit
Certificate stored in
file <alfrescohost.crt>
Then import
it into the Java runtime environment on the CAS host (you need to first copy
the certificate file to the CAS host):
C:\>keytool
-import -alias IXDV1210.ixxus.co.uk -file z:/temp/alfrescohost.crt -keystore
"C:/Program Files/Java/jdk1.6.0_14/jre/lib/security/cacerts"
-storepass changeit
The Alfresco
host is now trusted by the Java environment running the CAS server.
Note. if
you have a properly CA signed certificate then this import step is not needed
as the Java runtime environment will have a trust chain already setup to verify
the certificate.
Thanks for this, Martin--INCREDIBLY useful. One small question, if you don't mind:
ReplyDeleteYou write "The following two classes .... need to be compiled against the Alfresco 3.4 SDK and CAS library." Can you point me towards more specifics on how to do that?
Cheers,
Ian Crew
University of California, Berkeley
Nevermind--figured that out myself. For others, see http://wiki.alfresco.com/wiki/Developing_an_Alfresco_Module for the details you need. Sorry for the false alarm, Martin!
ReplyDeleteIan Crew
UC Berkeley
Hi Ian,
ReplyDeleteGlad that you find the article useful.
See also http://ecmstuff.blogspot.com/2011/06/setting-up-build-project-for-alfresco.html for information about how to setup a build project for Alfresco Repo, Alfresco Share, and WQS extensions.
Cheers,
Martin
Hi Martin,
ReplyDeleteI've written a short post about configuring logging out from Share/CAS at http://aelfric.co.uk/random/2011/11/share-cas-logout/
Hope it's useful.
Ian
Hi Martin, I have a problem. The SSO in Share ends with and error "A server error has occured". I have the next log:
ReplyDelete18:33:33,335 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,335 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: null
18:33:33,469 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,469 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: null
18:33:33,477 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,477 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: {cleartextUsername=admin, cleartextPassword=null}
18:33:33,478 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] Authenticating user: admin
18:33:33,485 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,485 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: {cleartextUsername=admin, cleartextPassword=null}
18:33:33,485 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] Authenticating user: admin
18:33:33,519 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,519 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: null
18:33:33,591 INFO [org.alfresco.web.site.EditionInterceptor] Successfully retrieved license information from Alfresco.
18:33:33,610 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] endpoint: http://alfrescoserver:8090/alfresco/s
18:33:33,610 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] credentials: {cleartextUsername=admin, cleartextPassword=null}
18:33:33,610 ERROR [org.springframework.extensions.webscripts.connector.AlfrescoAuthenticator] Authenticating user: admin
18:33:33,706 ERROR [org.alfresco.web.site] javax.servlet.ServletException: Could not resolve view with name 'site-index' in servlet with name 'Spring Surf Dispatcher Servlet'
Any idea? Whats wrong?
The endpoint url looks weird. Is the URL correct?
I think the problem is with the webscript, Class "LoginCas" is not executed. Why?? How can I solve that?
ReplyDeleteOther error is when I tried to execute logincas webscript:
ReplyDelete18:04:54,552 ERROR [freemarker.runtime] Template processing error: "Expression ticket is undefined on line 2, column 11 in api/logincas.get.xml.ftl."
Expression ticket is undefined on line 2, column 11 in api/logincas.get.xml.ftl.
The problematic instruction:
----------
==> ${ticket} [on line 2, column 9 in api/logincas.get.xml.ftl]
----------
Java backtrace for programmers:
----------
freemarker.core.InvalidReferenceException: Expression ticket is undefined on line 2, column 11 in api/logincas.get.xml.ftl.
at freemarker.core.TemplateObject.assertNonNull(TemplateObject.java:125)
My file 'logincas.get.xml.ftl' :
<?xml version="1.0" encoding="UTF-8"?>
<ticket>${ticket}</ticket>
Hi Core,
ReplyDeleteWould need more information about your configuration to be able to track down the problem.
Can you provide the configuration for the config evaluator="string-compare" condition="Remote" section in share-config-custom.xml
Also, can you turn on debug so we can see some output from CasAlfrescoAuthenticator, basically just verifying that it is called. And show us the debug logs.
Martin
Hi Martin Bergljung
ReplyDeleteI have the same problem with Core, username is available and proxyticket = null. I system.out in CasFilter on Share.
Please help me
Hi Martin,
ReplyDeleteYour CAS SSO solution runs perfectly in localhost. But I got the same problem as Core said when I put Alfresco and CAS into separate tomcat instances (in separate servers).
I receive proxyticket=null too but I have put the self-signed certs of both servers to the trusted keystore in the other sides.
Thx
Martin, this is my share-config-custom.xml :
ReplyDelete<alfresco-config>
<!-- Repository Library config section -->
<config evaluator="string-compare" condition="RepositoryLibrary" replace="true">
<!--
Whether the link to the Repository Library appears in the header component or not.
-->
<visible>true</visible>
</config>
<!--Overriding endpoints to reference a remote Alfresco server-->
<config evaluator="string-compare" condition="Remote">
<remote>
<!-- Authenticator implementation used in CAS authentication scenario,
overrides the default alfresco-ticket authenticator
<class>org.alfresco.connector.AlfrescoAuthenticator</class> -->
<authenticator>
<id>alfresco-ticket</id>
<name>Alfresco Authenticator</name>
<description>Alfresco Authenticator</description>
<class>com.edos.alfresco.sso.CasAlfrescoAuthenticator</class>
</authenticator>
<!-- Connects to an Alfresco instance using ticket-based authentication,
overrides the default alfresco connector to use CAS ticket authenticator-->
<connector>
<id>alfresco</id>
<name>Alfresco Connector</name>
<description>Connects to an Alfresco instance using ticket-based authentication</description>
<class> org.springframework.extensions.webscripts.connector.AlfrescoConnector</class>
<authenticator-id>alfresco-ticket</authenticator-id>
</connector>
<!-- Endpoint using external authentication via CAS-->
<endpoint>
<id>alfresco</id>
<name>Alfresco - user access</name>
<description>Access to Alfresco Repository WebScripts that require external user authentication</description>
<connector-id>alfresco</connector-id>
<endpoint-url>http://alfrescoserver:8090/alfresco/s</endpoint-url>
<identity>user</identity>
<external-auth>true</external-auth>
</endpoint>
</remote>
</config>
</alfresco-config>
And what logs can I put on in debug mode? How can I do that? Same as Barry, my CAS server is diferent server than my Alfresco server. Thanks for your help.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHi Martin,
ReplyDeleteI got error :
ERROR [extensions.webscripts.AbstractRuntime] Exception from executeScript - redirecting to status template error: 11190006 Script url /api/logincas does not map to a Web Script.
The cause is :
In your code, it's not mentioned about put "logincas.get.desc.xml" and "logincas.get.xml.ftl" in shared\classes\alfresco\extension\templates\webscripts\org\mycompany\authentication
Maybe this could help the others.
Thanks
This comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteSSO between Alfresco and CAS is now working for me but I can no longer connect to the IMAP server in Alfresco via Outlook. Is IMAP disabled after SSO change?
Thanks
Hi,
ReplyDeleteI have added a new section called " Putting the CAS server on a separate host" to the blog entry.
Hope it will solve most of your problems. When it comes to using other protocols/interfaces, such as WebDAV and IMAP,
with the CAS SSO solution it will not work.
You would have to code a custom authenticator for this.
In one project where CAS was backed by a Drupal user directory I coded a drupal authenticator
so we could use WebDAV.
The alfresco-public.properties would then have an authenticator chain such as the following:
authentication.chain=cas:external,localDrupal:drupal
I might write a blog in the future about how to implement a custom authenticator and synchronizer
Hi Martin,
ReplyDeleteI've briefly written up your custom Drupal authenticator at http://aelfric.co.uk/random/2011/08/cas-alfresco-and-webdav/
Hi Martin:
ReplyDeleteI always got error message using share:
The remote server may be unavailable or your authentication details have not been recognized.
FYI:
I synced user adn group from Active Directory
Used MSSQL Server
Hi togum,
ReplyDeleteCan you check that SSO works when logging into Alfresco Explorer (http://:8080/alfresco. If that works then check the Remote section in share-config-custom.xml, it has the details of how to connect to the Alfresco repository:
...config evaluator="string-compare" condition="Remote"...
Another place that has the Alfresco repo URL is the CAS Authentication filter for the Share webapp, check that it is correct:
public class CasAuthenticationFilter implements Filter {
private final static String ALFRESCO_WEBAPP_URL = "http://localhost:8080/alfresco";
The web.xml also has filters with the serverName parameter pointing to the Alfresco web app, check that it is correctly specified.
Thanks Martin,
ReplyDeleteSame with Core, I got this:
12:55:38,179 ERROR [freemarker.runtime] Expression ticket is undefined on line 2, column 11 in org/mycompany/authentication/logincas.get.xml.ftl.
Expression ticket is undefined on line 2, column 11 in org/mycompany/authentication/logincas.get.xml.ftl.
The problematic instruction:
----------
==> ${ticket} [on line 2, column 9 in org/mycompany/authentication/logincas.get.xml.ftl]
----------
Java backtrace for programmers:
----------
freemarker.core.InvalidReferenceException: Expression ticket is undefined on line 2, column 11 in org/mycompany/authentication/logincas.get.xml.ftl.
Can you help me please.
Thanks