Thursday, March 1, 2012

CILogon and Liferay Part 5: The Liferay Extension

This is part 5 of a set of blog posts detailing a procedure for setting up CILogon to provide authentication for a Liferay Portal.

And now we build the Liferay Extension!

If you've never done so, you should review the steps here.

So you have your Liferay Extension now in your Eclipse and are ready to roll!  In the Shibboleth steps I described a way to configure Shibboleth inside the Liferay Portal using a hook plugin.  Well, in this project we're going to roll that into the Extension, so there will be no need for a separate hook.

Now, create a folder structure to match Liferay in your ext project. We're going after the WEB-INF/ext-impl/src/com/liferay/portal/security/auth folder. That'll be in the docroot folder of your project. Now, if you look at the corresponding folder in your Liferay source code you'll notice a series of auto login classes corresponding to the various Web SSO providers Liferay is designed to interact with out of the box. We're going to build one just like them for CILogon.

Your new CILogonAutoLogin class should be of package com.liferay.portal.security.auth and it should implement AutoLogin.

Here's the code:


public class CILogonAutoLogin implements AutoLogin {


private static Log _log = LogFactoryUtil.getLog(CILogonAutoLogin.class);
private long companyId; 


public String[] login(HttpServletRequest req, HttpServletResponse res)
throws AutoLoginException {

String[] credentials = null;
String userEmail = "";
String[] userName;
User user;

_log.info("CILogon Extension");

try {
companyId = PortalUtil.getCompanyId(req);


if (!PrefsPropsUtil.getBoolean(
companyId, PropsKeys.CILOGON_AUTH_ENABLED,
PropsValues.CILOGON_AUTH_ENABLED)) {
System.out.println("CILogon Enabled.");
return credentials;
}

userEmail = getEmailFromCookie(req.getCookies());


if (userEmail.equals("") || userEmail.length() < 1) {
_log.error("Invalid or missing user login information from CILogon");
return credentials;
}


credentials = new String[3];


try{
user = UserLocalServiceUtil.getUserByEmailAddress(PortalUtil.getCompany(req).getCompanyId(), userEmail);
}catch (NoSuchUserException e) {
_log.error("No user found to match " + userEmail);
}


credentials[0] = String.valueOf(user.getUserId());
credentials[1] = user.getPassword();
credentials[2] = Boolean.TRUE.toString();



} catch (Exception e) {
_log.error(StackTraceUtil.getStackTrace(e));
throw new AutoLoginException(e);
}


return credentials;
}

private String getEmailFromCookie(Cookie[] cookies){

String email = "";

if(cookies != null){
for (Cookie ck : cookies) {
if ("CILOGON-USER_EMAIL".equals(ck.getName()) && !ck.getValue().equals("")) {
_log.info("User Login received:" + ck.getValue());
email = ck.getValue();
}
  }
}

return email;
}

 }


This should look familiar if you read my post on creating the extension for Shibboleth.  It's very similar, only it gets the user data from a cookie instead of an attribute.  Note the name of the cookie is the same as what we set in the SuccessServlet.

 Be sure to enable this module in your portal-ext.properties file:


#CILogon Auto Login Setup
auto.login.hooks=com.liferay.portal.security.auth.CILogonAutoLogin, com.liferay.portal.security.auth.RememberMeAutoLogin
auth.pipeline.enable.liferay.check=false




In your Ext project, add a new set of folders under docroot/WEB-INF/ext-impl/src:
com/liferay/portal/kernel/util
And copy the PropsKeys.java class from the corresponding location in the Liferay source bundle into util.

What you'll see in this class is a VERY LONG list of public Strings being declared. Add these into it:

public static final String CILogon_AUTH_ENABLED = "cilogon.auth.enabled";

It doesn't matter where you put it, but since the variables are in alphabetical order I just kept to that approach.

Now you can save that class and add another folder set:

com/liferay/portal/util
also under /src.

From the corresponding folder in the source code, copy over the PropsValues.java file.
You'll see another long list of Strings, similar to PropsKeys. Add these lines:

public static final boolean CILOGON_AUTH_ENABLED = GetterUtil.getBoolean(PropsUtil.get(PropsKeys.CILOGON_AUTH_ENABLED));

And save the file.

Now, let's modify the Admin UI to enable or disable our CILogon module.  The path here will be similar to the Shibboleth hook path we did in the other post, only now it's in our extension:

docroot/WEB-INF/ext-web/docroot/html/portlet/enterprise_admin/settings

From the corresponding folders in your Liferay source, copy the file authentication.jsp over.

What you'll see when you open this file is the code that sets up the Authentication settings in Portal Settings. At the top is yet another long series of variable declarations. Add this to the list:

boolean cilogonAuthEnabled = ParamUtil.getBoolean(request, "settings--" + PropsKeys.CILOGON_AUTH_ENABLED + "--", PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys. CILOGON _AUTH_ENABLED, PropsValues. CILOGON _AUTH_ENABLED));

This declaration is referencing our friends, the value pair in Liferay. At last we'll be writing code to set them.
Near the top of the HTML you'll see a <liferay-ui> tag with an attribute called "names." That attribute sets up the list of tab links to choose from in the menu. You should see the names of all the existing Web SSO providers. Just add "CILogon" to the end of the list.
Next, we see a series of <liferay-ui:section> tags. Each of these sections corresponds to one of the Web SSO providers. Add a new section to the end that looks like this:

 <liferay-ui:section>
   < aui:fieldset>
    < aui:input inlineLabel="left" label="enabled" name=' < %= "settings--" + PropsKeys.CILOGON_AUTH_ENABLED + "--" %>' type="checkbox" value=" < %= cilogonAuthEnabled %>" />

And save the file. Now you can deploy your new extension and start Liferay!

When you go to set these values in the CILogon configuration in Liferay, they're pretty self explanatory. Just  make sure the "Enabled" box is checked. Save the value.


This is what mine looks like.  Yes, the Shibboleth module is still in there too!

Last, it's nice to get logging from this module so from in Liferay, logged in as an Admin, go to the Control Panel and then go into "Server Administration." Click "Log Levels."

Click "Add Category."

In the box, add com.liferay.portal.security.auth.CILogonAutoLogin and click "save."

I'll put this project up on the Github account once it's been refined a bit more, but you see the basics.
---
This project incorporates tools whose development was funded in part by the NIH through the NHLBI grant: The Cardiovascular Research Grid (R24HL085343)

2 comments: