
CAS and LDAP in Liferay 5.2
Table of Contents [-]
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,\