Enterprise Integration Zone is brought to you in partnership with:

I am currently working as a Software Architect and a Senior Manager at WSO2. I have spoken in numerous conferences - OSCON 2009, ApacheCon 2009, WSO2Con 2010, WSO2 SOA Workshops and WSO2 Security Workshops. I am a graduate from University of Moratuwa, Sri Lanka and in 2008 I completed my Masters specialized in software architecture from the same University. I also gained professional qualifications in BCS and ACS as well as certifications in SCDJWS, SCJP, SCBCD, SCWCD, MCSD, OCA, and CCNA. Prabath is a DZone MVB and is not an employee of DZone and has posted 22 posts at DZone. You can read more from them at their website. View Full User Profile

Integrating WSO2 Identity Server with Liferay

10.11.2012
| 4341 views |
  • submit to reddit
Liferay has a highly extensible architecture. You decide what you want to override in Liferay - it has an extension somewhere. This blog post shows how to delegate Liferay's authentication and authorization functionality to WSO2 Identity Server.



One of the challenging parts we found in this integration is the LDAP Users/Groups import. You can connect an LDAP to Liferay, but to authenticate users in Liferay against the underlying LDAP it has to import all the users and groups to Liferay's underlying database, which is by default running on Hypersonic.

Since we only need to keep the user data in a single LDAP, we wanted to avoid this duplication. But it was not as easy as we thought. If you need to avoid that, then we need to write the complete persistence layer. To understand this better, I need to elaborate. Let's take a step back and see how Authentication and Authorization work in Liferay.

Liferay has a chain of authenticators. When you enter your username/password the chain of authenticators will get invoked. This is the place where we plugged in WSO2ISAuthenticator.
auth.pipeline.pre=org.wso2.liferay.is.authenticator.WSO2ISAuthenticator
auth.pipeline.enable.liferay.check=false 
wso2is.auth.service.endpoint.primary=https://localhost:9443/services/   
The above configuration [which should be in the liferay_home/tomcat/webapps/ROOT/WEB-INF/classes/portal-ext.properties] tells Liferay to load our custom authenticator. Also, the second entry says, once our authanticator is loaded, do not invoke rest in the chain. Otherwise, the default Liferay authenticator will also get invoked. The third entry points to the AuthenticationAdmin service running in WSO2 Identity Server.

Now, the username/password goes into WSO2ISAuthenticator and it will talk to WSO2 Identity Server over SOAP to authenticate the user. Once authentication is done, the control once again will be passed in to the Liferay container.

Now is the tricky part. Liferay has its own permission model: who should be able to see portlets, who should be able to add portlets, etc. For this it needs to find which Liferay roles are attached to the logged in user or which Liferay roles are attached to any group the logged in user belongs to. To get these details it needs to talk to the underlying persistence layer - which will load details from Liferay's underlying database. This is why we wanted to have users imported here from the LDAP.

Even-though it's possible, we decided not to write a persistence layer.  We only wanted to override authentication and authorization.

Even in the case of authorization - there are two types. The authorization model governed by Liferay to display/add portlets to the portal and the authorization model used within the Portlet itself to display content within the portlet.

The first type is done by assigning portlet management permissions to a given Liferay role and assigning members [groups/users] to that role from the underlying LDAP. We did not want to do that because that is very much on the portal administration side, and it's specific to Liferay. But the second model is the one that directly deals with the business functions. That is what we wanted to do in a fine-grained manner.

Let's dig deeper into this...

Even the second model can be done with Liferay's roles and permissions. Whenever you want to render something in the portlet that requires some restricted audience, then before rendering that you need to call req.isUserInRole("roleNme"). This is compliant with the JSR too, but the disadvantages are..

1. Our business functionalities in an SOA deployment should not be governed by Liferay roles. Liferay could only be a single channel to access the business functions.
2. We can achieve only the role based access control with this model.

Liferay,also has it's own way of permission checking within a portlet via PermissionChecker API. You may have a look at this for further details.

Our approach was to write a utility function called hasPermission(). If you extend your portlet from org.wso2.liferay.xacml.connector.SecuredGenericPortlet then this will be automatically available for you. Or else you can directly call it through AuthzChecker.hasPermission(). These functions are available from the org.wso2.liferay.xacml.connector.jar file.

You can copy all jar dependencies from here and copy those to liferay_home/tomcat/lib/ext.
The connection between XACML connector deployed in Liferay and WSO2 XACML engine is through Thrift. You need to add following properties to the portal-ext.properties file.
wso2is.auth.thrift.endpoint=localhost
wso2is.auth.thrift.port=10500
wso2is.auth.thrift.connection.timeout=10000
wso2is.auth.thrift.admin.user=admin
wso2is.auth.thrift.admin.user.password=admin
wso2is.auth.thrift.endpoint.login=https://localhost:9443/
Since by default Identity Server is using a self-signed certificate, either you have import its public certificate to the trust store of Liferay or set the following two properties in portal-ext.properties file pointing to the Identity Server's key store.
wso2is.auth.thrift.system.trusstore=/wso2is-3.2.3/repository/resources/security/wso2carbon.jks
wso2is.auth.thrift.system.trusstore.password=wso2carbon
Please note that above configuration is tested with Liferay 6.1.1 and WSO2 Identity 3.2.3/4.0.0.
Published at DZone with permission of Prabath Siriwardena, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)