« Voltar para Single Sign-on

CAS and LDAP in Liferay 5.2

Introduction #

This page details how to use CAS/LDAP integration in Liferay 5.2.2. In this document, changes were made to the Liferay code to make it work. This document does not apply for Liferay 6.0+.

Example Environment #

  • Liferay 5.2.2
  • Tomcat 6
  • Apache DS 1.5.4
  • CAS 3.3.1

This article assumes that you've got CAS and LDAP configured correctly. If you need some help with this there are some good Wiki articles, as well as the Portal Administrator's Guide.

Authentication Settings #

General

  • How do users authenticate: By Email Address

LDAP

  • Enabled: true
  • Required: true
  • Authentication Search Filter: (mail=@email_address@)

LDAP Mappings:

  • Sscreen Name: cn
  • Password: userPassword
  • Email Address: mail
  • First Nam: givenName
  • Last Name: sn
  • Job Title: title
  • Group: groupMembership

CAS settings:

  • Enabled: true
  • Import from LDAP: true

Problems to Overcome #

The problems encountered with getting LDAP and CAS up and running are:

Exporting to LDAP during LDAP import fails #

If a user exists in LDAP but not Liferay, when they first login Liferay tries to add them to the DB. During the user add, the LDAP export functionality kicks in. As the users Contact object hasn't been created yet, the export fails and things go pear shaped. See LEP-7360

Exporting to LDAP fails if the user doesn't have a Job Title #

When adding a new user that doesn't have a Job Title, the LDAP export fails on ApacheDS 1.5.4 as it doesn't like attributes with an empty string. Something about being syntactically incorrect.

Passwords exported to LDAP are plain text #

CAS authentication doesn't work #

CAS authentication doesn't work when authentication type is email address.

The final part of the CAS authentication protocol is for the Liferay server to validate the CAS 'ticket'. (Check out the CAS documentation or for a good overview see http://static.springsource.org/spring-security/site/reference/html/cas.html#cas-how-it-works). The response from the CAS server is the username used in the authentication process. Liferay assumes that this is the users screen name.

Logging out when using CAS does not log out of Liferay #

See LPS-2551

Fix #

To fix the problems, we make changes to the following files:

  • /com/liferay/portal/model/impl/UserImpl.java
  • /com/liferay/portal/security/ldap/LDAPUser.java
  • /com/liferay/portal/security/ldap/PortalLDAPUtil.java
  • /com/liferay/portal/security/auth/CASAutoLogin.java
  • /com/liferay/portal/servlet/filters/sso/cas/CASFilter.java

The patch for these files is attached

Problem 1#

When a user is being added the User model object is saved before its Contact model. If the user already exists in LDAP, the PortalLDAPUtil.exportToLDAP(User) function, called as part of the saving of the User object, calls user.getContact(). Because there is no contact a blank instance is returned. Therefore the LDAP attributes exported are all blank which causes LDAP exception.

The fix for this problem is to instead of creating the new Contact object via

contact = new ContactImpl();
call the Contact service via
contact =  ContactLocalServiceUtil.createContact(getContactId());
This will ensure that the isNew() function of the Contact object will return true

Then in PortalLDAPUtil.exportToLDAP(User) don't export the user if the contact is new

if (binding == null || user.getContact().isNew() ) {

Problem 2#

Problem 2 is caused by setting the job title attribute even if the User value is null.

This was fixed by checking for null before setting the LDAP attribute.

Also see LPS-3786

Problem 3#

Exporting of plain text passwords is fixed by the following bit of code

if (user.isPasswordModified() &&
	Validator.isNotNull(user.getPassword())) {

	String ldapPassword = "{" + PwdEncryptor.PASSWORDS_ENCRYPTION_ALGORITHM + "}" + user.getPassword(); 
	mods.addItem(
		userMappings.getProperty("password"),
		ldapPassword);
}

This will set the password in LDAP to be the same as the encrypted password stored in the Liferay DB.

Problem 4#

This fix is in the CASAutoLogin.java class. The fix was two fold. 1. Use the COMPANY_SECURITY_AUTH_TYPE property to determine how to find an existing user

	String authType = PrefsPropsUtil.getString(
	                           				companyId, PropsKeys.COMPANY_SECURITY_AUTH_TYPE,
	                           				PropsValues.COMPANY_SECURITY_AUTH_TYPE);
				
	if (authType.equals(CompanyConstants.AUTH_TYPE_SN)) {
		
		user = UserLocalServiceUtil.getUserByScreenName(
			companyId, screenName);
	}
	else {
		user = UserLocalServiceUtil.getUserByEmailAddress(companyId, screenName);
	}

2. When adding a user from LDAP the search filter now works for both screen name and email address

String filter = PortalLDAPUtil.getAuthSearchFilter(companyId, screenName, screenName, "");

This one line of code replaces about 15 lines of code.

Problem 5#

When CAS is enabled, logging out redirects to the CAS logout URL. The problem is that logging out of Liferay is not performed.

CAS has some functionality that, by default, is turned off. When turned on it allows the CAS logout URL to have a service parameter, just like the login URL. If the service parameter exists, CAS will redirect to the service once it has completed logging out.

To turn ON this feature you need to edit the WEB-INF/cas-servlet.xml file of your CAS deployment. Look for the logoutController definition and add the line

p:followServiceRedirects="true"

To get use this feature from Liferay we need to do two things

  • Add the service parameter to the CAS logout redirect
  • Process that CAS redirect to do the actual logout of Liferay.

Point one is handled by the changes to CASFilter Point two needed a new Struts action mapping that is different from the normal logout, i.e. /portal/logout, but runs the same struts action. To do this I added the following to struts-config.xml

<action path="/portal/caslogout" type="com.liferay.portal.action.LogoutAction" />

We also add /portal/caslogout to the portal property auth.public.paths

auth.public.paths=\
       ...
        /portal/extend_session_confirm,\
        /portal/json_service,\
        /portal/logout,\
+       /portal/caslogout,\
        /portal/open_id_request,\
        /portal/open_id_response,\
1 Anexo
63929 Visualizações
Média (0 Votos)
A média da avaliação é 0.0 estrelas de 5.
Comentários
Respostas do tópico Autor Data
When a user changes his/her password, there is... Stef Heyenrath 18 de Maio de 2009 09:15
I'm facing with another problem, liferay... Nowhere Rosa 24 de Setembro de 2009 00:05
It seems that LifeRay SSO does not support CAS... John J 25 de Novembro de 2009 04:02
Tested with CAS3.2, same issue:-( Anyone can... John J 25 de Novembro de 2009 04:24
mine works with CAS 3.4 Van Hoai Pham 8 de Dezembro de 2009 02:52
what is the solutino for this prolem, ... Auditya manikanta Vadrevu 7 de Janeiro de 2010 22:05
hello, I am working with 5.2.3 and i have used... Mahmudur Rahman Manna 2 de Agosto de 2010 12:13
When you have liferay 5.2.2 and CAS configured... Eduardo Lopez Seijo 25 de Agosto de 2010 04:10

When a user changes his/her password, there is the following exception in the logging:

17:07:07,421 INFO 2009-05-18 17:07:07,411 [http-127.0.0.1-8080-2] ERROR portal-web.docroot.html.portal.render_portlet.jsp - com.liferay.portal.ModelListenerException: javax.naming.NameAlreadyBoundException: [LDAP: error code 68 - ENTRY_ALREADY_EXISTS: failed for Add Request :
ClientEntry
dn: 2.5.4.3=2000a,2.5.4.11=users,0.9.2342.19200300.100.1.25=example,0.9.2342.1920030­0.100.1.25=com
objectclass: top
objectclass: person
objectclass: inetOrgPerson
objectclass: organizationalPerson
mail: 2000a@x.nl
sn: 2000a
userpassword: '0x32 0x30 0x30 0x30 0x61 '
cn: 2000a
givenname: 2000a
: cn=2000a,ou=users,dc=example,dc=com already exists!]; remaining name 'cn=2000a,ou=users,dc=example,dc=com'
at com.liferay.portal.model.ContactListener.onAfterUpdate(ContactListener.java:56)
­at com.liferay.portal.service.persistence.ContactPersistenceImpl.update(ContactPers­istenceImpl.java:191)
at com.liferay.portal.service.impl.UserLocalServiceImpl.updateUser(UserLocalService­Impl.java:2331)
at com.liferay.portal.service.impl.UserServiceImpl.updateUser(UserServiceImpl.java:­602)
at com.liferay.portal.service.impl.UserServiceImpl.updateUser(UserServiceImpl.java:­633)
at com.liferay.portal.service.UserServiceUtil.updateUser(UserServiceUtil.java:387)
­at com.liferay.portlet.enterpriseadmin.action.EditUserAction.updateUser(EditUserAct­ion.java:449)
at com.liferay.portlet.enterpriseadmin.action.EditUserAction.processAction(EditUser­Action.java:118)
at com.liferay.portlet.myaccount.action.EditUserAction.processAction(EditUserAction­.java:58)
at com.liferay.portal.struts.PortletRequestProcessor.process(PortletRequestProcesso­r.java:180)
at com.liferay.portlet.StrutsPortlet.processAction(StrutsPortlet.java:197)
at com.sun.portal.portletcontainer.appengine.filter.FilterChainImpl.doFilter(Filter­ChainImpl.java:98)
at com.liferay.portal.kernel.portlet.PortletFilterUtil.doFilter(PortletFilterUtil.j­ava:57)
at com.liferay.portlet.InvokerPortletImpl.invoke(InvokerPortletImpl.java:630)
at com.liferay.portlet.InvokerPortletImpl.invokeAction(InvokerPortletImpl.java:662)­
at com.liferay.portlet.InvokerPortletImpl.processAction(InvokerPortletImpl.java:357­)
at com.liferay.portal.action.LayoutAction.processPortletRequest(LayoutAction.java:5­95)
at com.liferay.portal.action.LayoutAction.processLayout(LayoutAction.java:423)
at com.liferay.portal.action.LayoutAction.execute(LayoutAction.java:195)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.­java:431)
Caused by: javax.naming.NameAlreadyBoundException: [LDAP: error code 68 - ENTRY_ALREADY_EXISTS: failed for Add Request :


What's wrong?
Postado em 18/05/09 09:15.
I'm facing with another problem, liferay imports user from LDAP, but it does'n update password in DB when user log in successfully the first time. Without CAS it works fine. Is it a problem or expected behaviour? You can read more here: http://www.liferay.com/web/guest/community/forums/-/message_boards/message/40602­43

Any suggestion would be appreciated
Postado em 24/09/09 00:05.
It seems that LifeRay SSO does not support CAS 3.4 so you have to try with another CAS version...
Postado em 25/11/09 04:02 em resposta a Nowhere Rosa.
Tested with CAS3.2, same issue:-(

Anyone can help?


type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

javax.servlet.ServletException: edu.yale.its.tp.cas.client.CASAuthenticationException: Unable to validate ProxyTicketValidator [[edu.yale.its.tp.cas.client.ProxyTicketValidator proxyList= [edu.yale.its.tp.cas.client.ServiceTicketValidator casValidateUrl=[https://localhost:18009/cas-web/proxyValidate] ticket=[ST-1-bpKAQ4HRqzcvBbegNQI9-cas] service=[http%3A%2F%2Flocalhost%3A8080%2Fc%2Fportal%2Flogin] renew=false]]]
edu.yale.its.tp.cas.client.filter.CASFilter.doFilter(CASFilter.ja­va:381)
com.liferay.portal.servlet.filters.sso.cas.CASFilter.processFilter(CASFi­lter.java:141)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.­java:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.j­ava:165)
com.liferay.portal.sharepoint.SharepointFilter.processFilter(Sharepoint­Filter.java:192)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilte­r.java:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter­.java:165)
com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.proc­essFilter(VirtualHostFilter.java:189)
com.liferay.portal.kernel.servlet.BaseFilt­er.doFilter(BaseFilter.java:92)
com.liferay.portal.kernel.servlet.BaseFilter.pro­cessFilter(BaseFilter.java:165)
com.liferay.portal.servlet.filters.threadlocalca­che.ThreadLocalCacheFilter.processFilter(ThreadLocalCacheFilter.java:52)
com.lif­eray.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:92)
com.liferay.p­ortal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:165)
com.liferay.p­ortal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:95)
org.tuckey.web.filt­ers.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:738)


root cause

edu.yale.its.tp.cas.client.CASAuthenticationException: Unable to validate ProxyTicketValidator [[edu.yale.its.tp.cas.client.ProxyTicketValidator proxyList= [edu.yale.its.tp.cas.client.ServiceTicketValidator casValidateUrl=[https://localhost:18009/cas-web/proxyValidate] ticket=[ST-1-bpKAQ4HRqzcvBbegNQI9-cas] service=[http%3A%2F%2Flocalhost%3A8080%2Fc%2Fportal%2Flogin] renew=false]]]
edu.yale.its.tp.cas.client.CASReceipt.getReceipt(CASReceipt.java:­58)
edu.yale.its.tp.cas.client.filter.CASFilter.getAuthenticatedUser(CASFilter.j­ava:455)
edu.yale.its.tp.cas.client.filter.CASFilter.doFilter(CASFilter.java:378­)
com.liferay.portal.servlet.filters.sso.cas.CASFilter.processFilter(CASFilter.j­ava:141)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:9­2)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:16­5)
com.liferay.portal.sharepoint.SharepointFilter.processFilter(SharepointFilter­.java:192)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java­:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:­165)
com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.processFil­ter(VirtualHostFilter.java:189)
com.liferay.portal.kernel.servlet.BaseFilter.doF­ilter(BaseFilter.java:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFi­lter(BaseFilter.java:165)
com.liferay.portal.servlet.filters.threadlocalcache.Th­readLocalCacheFilter.processFilter(ThreadLocalCacheFilter.java:52)
com.liferay.p­ortal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:92)
com.liferay.portal.­kernel.servlet.BaseFilter.processFilter(BaseFilter.java:165)
com.liferay.portal.­kernel.servlet.BaseFilter.doFilter(BaseFilter.java:95)
org.tuckey.web.filters.ur­lrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:738)


root cause

java.net.SocketException: Unexpected end of file from server
sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:769)
sun.net.­www.http.HttpClient.parseHTTP(HttpClient.java:632)
sun.net.www.http.HttpClient.p­arseHTTPHeader(HttpClient.java:766)
sun.net.www.http.HttpClient.parseHTTP(HttpCl­ient.java:632)
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpUR­LConnection.java:1049)
edu.yale.its.tp.cas.util.SecureURL.retrieve(SecureURL.jav­a:86)
edu.yale.its.tp.cas.client.ServiceTicketValidator.validate(ServiceTicketVa­lidator.java:212)
edu.yale.its.tp.cas.client.CASReceipt.getReceipt(CASReceipt.ja­va:56)
edu.yale.its.tp.cas.client.filter.CASFilter.getAuthenticatedUser(CASFilte­r.java:455)
edu.yale.its.tp.cas.client.filter.CASFilter.doFilter(CASFilter.java:­378)
com.liferay.portal.servlet.filters.sso.cas.CASFilter.processFilter(CASFilte­r.java:141)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.jav­a:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java­:165)
com.liferay.portal.sharepoint.SharepointFilter.processFilter(SharepointFil­ter.java:192)
com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.j­ava:92)
com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.ja­va:165)
com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.process­Filter(VirtualHostFilter.java:189)
com.liferay.portal.kernel.servlet.BaseFilter.­doFilter(BaseFilter.java:92)
com.liferay.portal.kernel.servlet.BaseFilter.proces­sFilter(BaseFilter.java:165)
com.liferay.portal.servlet.filters.threadlocalcache­.ThreadLocalCacheFilter.processFilter(ThreadLocalCacheFilter.java:52)
com.lifera­y.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:92)
com.liferay.port­al.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:165)
com.liferay.port­al.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:95)
org.tuckey.web.filters­.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:738)


note The full stack trace of the root cause is available in the Apache Tomcat/6.0.20 logs.
Postado em 25/11/09 04:24 em resposta a John J.
mine works with CAS 3.4
Postado em 08/12/09 02:52 em resposta a John J.
what is the solutino for this prolem,

15:53:11,784 ERROR [CASAutoLogin:172] Problem accessing LDAP server
2com.liferay.portal.NoSuchUserException: User thill99 was not found in the LDAP server
3 at com.liferay.portal.security.auth.CASAutoLogin.addUser(CASAutoLogin.java:167)
4............................................

more here,
http://www.liferay.com/web/guest/community/forums/-/message_boards/message/13806­76
Postado em 07/01/10 22:05.
hello,
I am working with 5.2.3 and i have used your patch in ext-impl but classloader is always loading the old CASAutoLogin before but not the ext-impl one .

( http://www.liferay.com/web/guest/community/forums/-/message_boards/message/14114­12 ) in this thread Mika suggested not to use the same name, I have tried it with new Name e.g. CASAutoLoginExt and also changed the auto.login.hooks property accordingly but it gives exception ClassNotFound.

Please advise me if you dont have any such trouble or if you had then how you solved that.

Thanks in advance
Postado em 02/08/10 12:13.
When you have liferay 5.2.2 and CAS configured without LDAP, does "Default password policy" works correctly??. I realized that Password expiration and Lockout rules don't work!
Postado em 25/08/10 04:10.