Thursday, March 22, 2012

RichFaces Error: filterStart

RichFaces has recently moved into version 4, providing compatibility with JSF 2.0.  Pretty cool.

The problem is that it can be a bit confusing when upgrading from version 3, or when following some of the slightly outdated documentation on the site.

If you've dutifully followed the getting started instructions for RichFaces when working with version 4, you probably added these lines to your web.xml file:


<filter>
     <display-name>RichFaces Filter</display-name>
     <filter-name>richfaces</filter-name>
     <filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
     <filter-name>richfaces</filter-name>
     <servlet-name>Faces Servlet</servlet-name>
     <dispatcher>REQUEST</dispatcher>
     <dispatcher>FORWARD</dispatcher>
     <dispatcher>INCLUDE</dispatcher>
</filter-mapping>

And yet you're still getting that horrible filterStart error, aren't you?  No problem, my friends.  Here's how to fix it:

Delete those lines from your web.xml.

Seriously.

The JSF 2.0 spec handles those configurations already.  By adding those lines into your web.xml you're just going to confuse the poor thing.

SEVERE: Error listenerStart Part IV

By now you've gone through the other possible solutions on this site to get your JSF Portlet to deploy and are still seeing this evil,  horrible, unholy error message and you're seriously contemplating changing careers...

It hit me this afternoon that there is yet another possible cause for this error that might catch the new developer off guard.

Which version of JSF are you using?

No, I don't mean the version number.  I mean which implementation?  Don't know?  Better find out, because there are two very popular ones.

The first is Mojarra, which is the JSF implementation developed by Sun.  It's the one I've used most often in my own JSF Portlets in Liferay.  The other is MyFaces from Apache.  They both comply with the JSF standard, and are usually interchangeable...

BUT

You have to make sure you know which one you're dealing with when you're editing your web.xml.  When you configure your listener, you need to know which implementation you're working with so that you can configure it to call the correct class.

If you're using Mojarra, then your listener should look like this:
   


<listener>
     <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>


If you're using Apache MyFaces, then it should look like this:


<listener> <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class> </listener>


Make sure you're configuring for the right implementation!

Tuesday, March 6, 2012

CILogon and Liferay Part 6: Signing Out


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

So by now you've got your Liferay signing you in using your CILogon provider and all is well.  Your boss is pleased, and says "Can I try?"  Of course, you say, and slide the demo laptop over to him.  He clicks the "Sign Out" link in the upper right corner of the page and...

...you're still signed in.  He slides it back over to you, smile fading as you start clicking that link over and over, but Liferay's got you in an iron grip and ain't letting go.

Time to go back to your extension.

See, Liferay has a Struts driven LogoutAction object that clears the session and kills all the Liferay cookies... But it's not killing your CILogon cookies because, well, you made them, not Liferay. We need to modify that class to include the cookies your CILogonAutoLogin module keeps using to log you back in.

Grab the following file from the Liferay source code: com.liferay.portal.action.LopgoutAction.java and put it into your ext plugin in the corresponding folder.

About halfway down the file is a series of lines of code adding cookie objects to CookieKeys.  Right before those lines is a good place to add this code:



Cookie cILogonEmailCookie = new Cookie(
"CILOGON-USER_EMAIL", StringPool.BLANK);


if (Validator.isNotNull(domain)) {
cILogonEmailCookie.setDomain(domain);
}


cILogonEmailCookie.setMaxAge(0);
cILogonEmailCookie.setPath(StringPool.SLASH);
cILogonEmailCookie.setSecure(false);

Cookie cILogonNameCookie = new Cookie(
"CILOGON-USER_NAME", StringPool.BLANK);


if (Validator.isNotNull(domain)) {
cILogonNameCookie.setDomain(domain);
}


cILogonNameCookie.setMaxAge(0);
cILogonNameCookie.setPath(StringPool.SLASH);
cILogonNameCookie.setSecure(false);


CookieKeys.addCookie(request, response, cILogonNameCookie, false);
CookieKeys.addCookie(request, response, cILogonEmailCookie, false);


What's happening here is that we're replacing the cookies we created in SuccessServlet with new ones that will expire immediately. That way when the user comes back around in the request the CILogonAutoLogin module won't see the cookies and log the user back in.

Here's an important note... We're using the domain String object from the line

String domain = CookieKeys.getDomain(request);

earlier in the class.  This is not the full string you may expect.  For example, if this code is running on machine.mydomain.com, the domain that's returned here will be ".mydomain.com" so when you're setting this string in your SuccessServlet (back in part 2 of this series we called that variable TARGET_DOMAIN) make sure it matches!


I'm serious.  It took me a day and a half to figure out why Liferay was able to find the cookie, and yet wasn't overwriting the old cookie with the new one.  For this to work, the cookie domains must match exactly!

One quick note about setting that cookie back in SuccessServlet... I didn't go into specific detail but remember we NEVER hard code values!  If your domain is hard coded in that servlet for testing purposes fine... But as soon as possible move that string value into a config file or database entry or something.  Don't leave it in the compiled code.

--------------------------------------------
This project incorporates tools whose development was funded in part by the NIH through the NHLBI grant: The Cardiovascular Research Grid (R24HL085343)

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)

CILogon and Liferay Part 4: The Liferay Theme

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

Technically, this step is optional.  What we're doing here is changing the Sign In link in the upper right corner of the Liferay page to point to your CILogon servlets.  If you intend to come at the portal from a different site then you can skip this step, although it would still be good to have this link either changed or disabled so that it's consistent with your logon approach.

First, you need to create a Liferay Theme plugin.  If your Liferay SDK isn't already set up, take a few minutes and follow the procedure here.

It's a LOT easier to modify an existing theme than to build one from scratch.  If you already have a  theme you're using, modify that.  If you don't, you can import the default Liferay theme and use it.  (It can be found in [liferay-portal-home]/[tomcat-home]/webapps/ROOT/html/themes/classic).  If you have the Liferay plugin for Eclipse, it can handle the details for you.

If you need to do it by hand, you can run the following ant command in the themes folder of your SDK:

create myAwesomeTheme "My Awesome Theme"

Ant will create an empty theme project for you.  Then, you can copy the files from the /classic themes folder described above into the /docroot folder of your theme project.  Do NOT delete the /_diffs folder!

The way we make changes to a them in Liferay is to override the files, not edit them directly.  This helps you to keep your changes straight and easy to keep track of.

In the _diffs folder, create a folder called templates.  Now, from the templates folder under docroot, copy the portal_normal.vm and init_custom.vm files.

Open init_custom.vm.  It should be blank.  Add the following line:

#set( $custom_sign_in_url = "[MyAwesomeDomain]/portal/startRequest" )


Save it and close it.

Now, in portal_normal.vm, find the line



#if(!$is_signed_in)
<a href="$sign_in_url" id="sign-in" rel="nofollow">$sign_in_text</a>
#end


And change it to



#if(!$is_signed_in)
<a href="$custom_sign_in_url" id="sign-in" rel="nofollow">$sign_in_text</a>
#end



So it uses your custom velocity variable that points to the CILogon servlets.

Now you can deploy your theme to Liferay and apply it!

CILogon and Liferay Part 3: Installing the Servlets

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

So your Servlets build successfully in Maven, right?  if not, go back and make sure they do before proceeding. Also, by now you should have either your official X509 certificate form your CA or gotten a test cert from CILogon, along with instructions on how to install it.  If not, get these before going any further.

Let's configure Tomcat:

Open up the server.xml file in Tomcat's conf folder.  Find the tag for the secure Connector port.  It may be commented out by default.  Uncomment it and edit it to look like this:


    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
keystoreFile="[The path to your local keystore]"
keystoreType="PKCS12"
keystorePass="[Your keystore password]" />

You should have the path and password for your keystore from the installation steps when you received the certificate.  This isn't an optional step, even for testing.  The CILogon establishes a trust before sending you to your IdP, and if it can't do that you'll go no further.

All done?  Good.  Now restart your Tomcat.

Once Tomcat is back up and running, and there are no errors related to your keystore, you can install your CILogon servlet app.  Take the war file and copy it to the /webapps folder in your Tomcat.  It will automatically hot deploy.  Watch the catalina.out file and make sure no errors arise from the installation.

Done?  Good.  On to the next step...