Tuesday, August 23, 2011

Shibboleth and Liferay Part 2: Configuring Apache

This is part 2 of a set of blog posts detailing the procedure for setting up Shibboleth to provide Web SSO functionality to Liferay.

This is the part that sounds a LOT more complicated than it is.

Apache's configuration is handled in a file called httpd.conf. This file is usually found in the/etc/httpd/conf directory.

There are a few settings in this file we need to pay attention to, but keep in mind that often additional configuration items are kept in separate files and imported into httpd.conf. This allows the user to more easily keep track of related configuration items without having to navigate through one, massive file. if that's the case on your server, then somewhere in your httpd.conf file (usually near the beginning) there should be a statement that looks like this:

Include conf.d/*.conf

That statement is telling Apache to include all the files in the subdirectory conf.d that end in .conf. Yours should be similar. It may also include specific individual files.

General Settings

Open up your httpd.conf file and find your way to the ServerName setting. Set this to the name of the server. That's NAME not IP ADDRESS. The next setting below that should be UseCanonicalName. Set that to "On" in order to get the server to use the ServerName you just set. So if our server is named test.awesome.server.dude then your configuration should look like this:

ServerName test.awesome.server.dude
UseCanonicalName On

And leave the rest alone... Maybe. If your configuration is spread out over multiple separate configuration files as described above, then the rest of this will probably be located in those files. Otherwise stay in httpd.conf to do the rest.

Shibboleth Settings

Next, you should have a file called shib.conf in the directory where all of your other Include files are. It will have been pre-generated for you in some cases by the Shibboleth installation, otherwise these settings should appear in your httpd.conf file. Most of these can be left alone, but the Location tag is the one we're interested in. When Liferay is bundled with Tomcat it is the ROOT application, so for the location in the following setting, we just use / alone.

<Location />
AuthType shibboleth
ShibRequestSetting requireSession 1
require valid-user
</Location>

If your Liferay were installed in Tomcat as some other context, then the name of the folder in [Tomcat Home]/webapps/ would be the correct Location setting.

And that's it for the Shibboleth configurations in Apache.

To be continued...

Tuesday, August 16, 2011

Shibboleth and Liferay Part 1: Installing Shibboleth

So here we go... A topic everybody seems to search on and nobody has a truly complete source of information for. Plenty of guides exist out there for setting up Apache, installing Shibboleth, setting up mod_proxy...

Some of those sites are great, some of them not so much... But the problem is they often assume a LOT about your knowledge of the subject or how your server is configured, and leave out many things important to new people.

That's the sort of problem that led to this blog. So often, blog posts, articles and forum posts are more about showing off than about actually helping people out. Figuring this thing out has been downright PAINFUL, and I want to spare the next guy by sharing lessons learned.

Oh, and many of the links out there referencing the Shibboleth Wiki are broken. They still link to a site that gives you a new URL for the Shibboleth Wiki, but you lose the specific item you were trying to read.

The project we'll be doing here will be an effort to set up Liferay to be protected by Shibboleth as a Web SSO service provider. This will be a series of blog posts detailing each step. Some of these steps aren't unique to Liferay or Tomcat so I'll break them up so if, for example, someone is looking for ways to connect Apache to Tomcat but isn't using Liferay, they can still benefit from this.

If you've been working with Liferay in just Tomcat (Or Oracle or Glassfish or whatever) you may not have had to interact with Apache. Apache is the quiet little web server that comes bundled with Linux and usually stays out of Tomcat's way. Well, now Apache is going to OWN Tomcat. Seriously. When we configure this thing Apache will decide what requests get sent to Tomcat and which ones don't. Tomcat's just going to have to learn to like it.

Here's how it works:

The user makes a request to the server. Shibboleth, having been configured to watch for this request, intercepts it and redirects the user to a separate authentication site. The user logs in using their credentials. Once they're authenticated, the request returns to the original server and Shibboleth then allows the request to proceed. Normally, with a site running under Apache, the request is turned over to Apache and the requested page is transmitted to the client. In the case of a request made to a servlet, more needs to happen. Apache will forward the request along to Tomcat, which will then handle it normally.

Technically, there are two halves of Shibboleth... The Service Provider (SP) and the Identity Provider (IdP). We're only going to worry about the SP here because that's the part that you, the person who's responsible for building this thing, are responsible for. Typically you'll be setting up a site that authenticates through a pre-existing IdP anyway so that part will already be done.

So our software today:
Operating System: (Linux) CentOS 5.6
Web Server: Apache 2.2.3
Web SSO Provider: Shibboleth 2.0
Servlet Container: Tomcat 6.0.29
Portal: Liferay 6.0.6

Step 1: Install Shibboleth

First thing's first... Install Shibboleth. If you're using the same OS I am, you can run this command:

sudo wget http://download.opensuse.org/repositories/security://shibboleth/CentOS_5/security:shibboleth.repo -P /etc/yum.repos.d

then navigate to /etc/yum.repos.d and run this command:

sudo yum install shibboleth.x86_64

If you're not, here's a WORKING link to the Shibboleth Wiki Linux install page.

Now, there are a couple of folders where log files will be written, and you need to make sure that the directory permissions are set such that Shibboleth can use them. These directories are

/var/log/httpd and
/var/log/shibboleth

The files that will be created are /var/log/httpd/native.log and /var/log/shibboleth/shibd.log. Don't create those files... Shibboleth will handle that. This is just so you know.

There is a file, located in /etc/shibboleth called shibboleth2.xml. This file contains the settings to allow Shibboleth to find and use the specific data items that control your authentication. In most cases, if you're working with a pre-existing IdP this file may be provided to you by the team that is responsible for that IdP. If not, a sample file is created for you that you can modify to your needs.

The first important part of that file is the RequestMapper section. It will look like this, assuming Liferay is installed in the ROOT and using the server name we came up with above.

<RequestMapper type="Native">
<RequestMap applicationId="default">
<Host name="test.awesome.server.dude" port="443" scheme="https">
<Path name="/" authType="shibboleth" requireSession="true"/>
</Host>
</RequestMap>
</RequestMapper>

Now, if this file wasn't provided to you by your IdP personnel then you're going to have to configure that part yourself. After RequestMapper is the ApplicationDefaults section. The opening tag for that section should look like this:

<ApplicationDefaults
entityID="https://test.awesome.server.dude/shibboleth"
REMOTE_USER="eppn persistent-id targeted-id"
attributePrefix="AJP_">

Inside this Element is another tag, <Sessions> which has yet another inside that, which we need to configure:

<SSO entityID="https://youramazingidpprovider/idp/shibboleth"
discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF">
SAML2 SAML1
</SSO>

These settings will vary depending on how the IdP is configured, and I'm afraid configuring an IdP is beyond the scope of this blog. There may be additional configuration items as well. If you're stuck having to set that up too, here's a link to the Shibboleth IdP setup page.

Save that file and be sure to start Shibboleth.

sudo /usr/sbin/shibd -fc /etc/shibboleth/shibboleth2.xml

To be continued...




Wednesday, August 10, 2011

Open Source vs. Proprietary Solutions

I haven't posted in a while because we've been working on a new portal that will use Shibboleth as the authentication provider.

Once it's up and running I plan to share the details here in this blog, but for now I can tell you that if ever there was a perfect example of the stark contrast between building websites in the open source world and building them in the proprietary software world, this is it.

I used to be a Microsoft .NET developer. I loved C# and hated having to code in VB.NET or J#. In any case, web development and associated activities in the .NET world are much, much easier than in the Java world. This is both good and bad.

It's good, in that it allows for people of a relatively lower skill level to contribute and fill a niche where you need decent websites built but don't really need for every one of them to be a web guru. It's also bad, in that it really hides a lot of what's going on from the developer, and that can be a real disadvantage if the scope of their responsibilities change to the point where they need to know it.

The Java world is a world of open source, of a thousand solutions to any given problem and a world of completely unshackled creativity. Good stuff, but you'd better know what you're doing or it's amazing how fast you can get in over your head. A thousand solutions to any problem may sound great, except that there's really no way to know which solution is best for you without a thorough understanding of what's going on and what to do with it.

So it has been with this portal project.

My initial plan was to build the portal and have it accessible by Shibboleth using the casshib extension. You see, Liferay doesn't come with out-of-the-box compatibility with Shibboleth (at least, not explicitly) but it does come with explicit support for CAS. Casshib sort of acts as an adapter so you can use Shibboleth to provide the authentication and then pass it to Liferay as if it were coming in the format for CAS.

Making casshib work in a case like this is a configuration nightmare of epic proportions if you've never worked with these things before.

If you're a Liferay developer, and you aren't afraid to open up the Liferay source code and tinker with it a little, there is absolutely no earthly reason to use casshib. No offense to you casshib guys. Your tool would be very useful in cases where an app absolutely had to use CAS but all that's available is Shibboleth. With Liferay however, it does NOT absolutely have to use CAS over Shibboleth.

Converting Liferay to play nice with Shibboleth is surprisingly easy, and once I've had the opportunity to test the code and procedure I'll post the details here. In short, my approach at this point has been to create a Liferay Hook Plugin (to modify the Authentication configuration page) and an Extension Plugin (to add the code to handle Shibboleth) and make the appropriate configuration changes in portal-ext.properties.

The hard part has been getting Shibboleth, Apache and Tomcat to work together with SSL. This too is a problem with a thousand solutions and I expect to have locked ours down in the next couple of days.

If this had been a SiteMinder authentication setup using IIS and a SharePoint portal or something, this all would probably be very easy and quick. The downside is that I'd have learned very little about how these things work, and it would have cost us a whole LOT of money to get there. I don't mean that as a criticism of Microsoft, only that for me personally, I feel like a stronger developer for having had this experience, and I'm glad of it.

Tuesday, June 21, 2011

Button Action Not Firing in JSF

We see this one a LOT.

The action listener in JSF isn't complicated at all and yet it seems like half the time something as simple as clicking a commandButton can cause problems.

Today's Example:

Liferay 6.0.6
Tomcat 6
Windows 7
ICEfaces 1.8.1
Sun JSF 1.2

Some code from the view.xhtml page:

<ice:selectManyMenu style="width:150px;" value="#portletBacking.currentsite}">
<f:selectItems id="siteList" value="#{sites.sites}" />
</ice:selectManyMenu>
<ice:commandButton actionListener="#{portletBacking.add}"
name="addButton" type="submit" value="Add"/>

And the add() method from the backing bean:

public void add(ActionEvent e){
System.out.println("Adding...");
//A bunch of processing...
}

Clicking the button did NOTHING. It didn't even print the "Adding..." message to standard output. Why? Was the action method set up properly? Yes. Was the backing bean registered in faces-config.xml? Yes.

What happens sometimes is that JSF gets stalled when there's an incorrect return type on a control. One way to figure out which control is causing the problem is to comment out the page controls and add them back in, one at a time, until the problem happens again. That's what I did, and in this case the flaw was in the ice:selectManyMenu control. Here's the code that was linked to the value:

public void setCurrentsite(String currentsite) {
this.currentsite = currentsite;
}

public String getCurrentsite() {
return currentsite;
}

See the problem?

The ice:selectManyMenu doesn't take a String value. It takes a String[]. If I had been working with an ice:selectOneMenu this would have been correct. All I had to do was fix the code to use a String[] instead:

public void setCurrentsite(String[] currentsite) {
this.currentsite = currentsite;
}

public String[] getCurrentsite() {
return currentsite;
}

And voila'! The action fired when I clicked the button.

So if you're having this problem and everything else seems right, start taking a closer look at the other controls on the page.

If that doesn't fix it, take a look at this.

Monday, June 13, 2011

Creating a Liferay Portlet From Google Web Toolkit

Yes, you can do it.

Now, I'm still in the process of refining the method but here's a quick and dirty approach. You just have to remember:
  • GWT applications "compile" Java source code into JavaScript.
  • Liferay Portlets have their JavaScript source files defined in the liferay-portlet.xml
Keep that in mind and you'll have no problems.

The Environment:
Liferay 6.06
Tomcat 6.0.29
Windows 7
Google Web Toolkit

*This should work on Liferay 5 as well but I haven't tried it.

First, if you're an experienced GWT developer then you already know how to build and compile your code. If you aren't, complete this tutorial first.

Once you've "compiled" your GWT project you'll need an empty Liferay portlet project to transplant your GWT project into. Go ahead and create one using the Liferay SDK.

Now that you have your empty Liferay portlet and your compiled GWT project, you have everything you need.

Suppose our project is called SuperPortlet.

  • Copy your war/WEB-INF/appengine-web.xml and war/WEB-INF/logging.properties files into the empty portlet's WEB-INF folder.
  • Copy your /SuperPortlet folder into the empty portlet's /docroot folder.
  • Copy your SuperPortlet.css and SuperPortlet.html files into the portlet's docroot folder.
  • Rename SuperPortlet.html to view.jsp
  • In your view.jsp file, there are a few tags that need to go away. In portlets we don't need <html>, <body>, <title> or <head> tags. Blow them away.
  • You don't need the <link> or <script> tags either. We're going to specify our .css and .js files elsewhere.
  • Add this to the top of your view.jsp file:

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects />

  • Now, open your liferay-portlet.xml file.
  • In the <header-portlet-css> tag, add the path to the .css file. In this case the line would look like this:
<header-portlet-css>/SuperPortlet.css</header-portlet-css>

*Note: This assumes the .css file is in the /docroot folder. If you have a subfolder with your .css just put it there and be sure the path is defined correctly in the above tag.

  • This is the tricky part... The auto-generates portlet defines a <footer-portlet-javascript> tag. If you put the path to your SuperPortlet.nocache.js file in there, it won't work. You need to change "footer" to "header."
<header-portlet-javascript>/SuperPortlet/SuperPortlet.nocache.js</header-portlet-javascript>

Again, adjusting that file path depending on how you organize your portlet.

Be sure to copy over any images or other .css and .js files you have (defining each in its own separate tag in the liferay-portlet.xml file) and you should be ready to rock & roll. I did this in Eclipse and exporting the project to a .war file gave me a deployable .war that worked great.

Friday, June 10, 2011

java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.String

If you see this error:

java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.String When using HttpServletRequest getParameters()

and you're using Liferay 6 with ICEfaces 1.8, then maybe this will help you.

The environment:

Liferay 6.06
Tomcat 6.0.29
Windows 7
ICEfaces 1.8.1 on JSF 1.2

The error is the result of having the out-of-the-box Chat portlet running at the same time as an ICEfaces portlet. I haven't worked out yet exactly why, but what's hapening is the PollerServlet in Liferay is having its getContent() method called and the pollerRequestString is giving it indigestion.

Liferay expects to be able to take the pollerRequestString, deserialize it, and cast it into a Map. For whatever reason, when there's an ICEfaces 1.8 portlet present, it gets a string it can't handle. Since Liferay didn't check the cast in the code for this Servlet, it throws the above error every 60 seconds. This can make for a very full log file in a very short period of time.

The offending line of code:

Map<String, Object>[] pollerRequestChunks =
(Map<String, Object>[])JSONFactoryUtil.deserialize(
pollerRequestString);


What to do?

Well, one suggestion on the Liferay forum for this issue is to remove the Chat portlet. That will make the errors go away, but it isn't much of a solution when you want to actually use the Chat portlet.

What I did was a quick & dirty "fix" using the Ext plugin system Liferay 6 provides.

Now, the Liferay In Action book from Manning has a pretty good section on setting up an Ext plugin and a good overall explanation of how Ext plugins work, but for some reason he seems to be in love with using Struts examples. He even sort of hangs a lampshade on that by saying that its 99% likely that if you're using the Ext plugin it's because you want to modify the Struts behavior.

Well I guess this is that 1% when we're not. What now?

We go in and use brute force, of course!

The author also strongly encourages the reader to extend, not replace, the classes in Liferay. The logic is that if you replace a class, then Liferay subsequently performs an update it will break your code. Well, he also acknowledges that your Ext plugin will probably break anyway so I don't see this as a major risk beyond what we're already doing. Further, all we're doing here is a simple fix, not changing any big functionality, so I'm going ahead and replacing.

Using either your Eclipse Helios (with Liferay plugin) or Liferay 6.0 SDK create a new Ext plugin project.

Know how?

You should have the Liferay 6 SDK set up on your machine. For details on that, you can check out one of my earlier posts. From there, if you're not using Eclipse, you can navigate into the ext folder and type

create uberext "Uber Extension"

(Substitute your own names)

That creates the plugin structure for you in a new folder in the ext folder.

In Eclipse, it's the same idea and it'll even create the plugin folder in your SDK ext folder, exactly as if you'd done it the command line way. (Assuming you've set Eclipse to use your SDK. If not, you need to do that first.)

To set Eclipse to use your SDK:
  1. Open Eclipse
  2. Click the dropdown arrow next to the Create New Server button. It should be right under "Search" on your Menu bar and it's the icon that looks a little like Oscar the Grouch's Trash Can. (Make of that what you will.)
  3. Select "New Liferay SDK"
  4. Follow the prompts to add the new SDK to your Eclipse environment.
Now you can use Eclipse to create your plugins and use the supplied Ant build scripts from the SDK.

Now to create your new Ext project:
  1. Either click the Liferay icon that looks like a power plug or go to File >> New >> Liferay Project
  2. Choose a project name and Display name, entering them in the appropriate fields.
  3. Under Plugin Type, select Ext
Eclipse will, behind the scenes, run the command that creates the project in the ext folder just as if you'd done it there yourself. It also displays the project in the navigation pane at the left.

So, to fix the above error:

In the source code for the plugin, under the docroot/WEB-INF/ext-impl/src folder, create your version of the com.liferay.portal.poller package. Inside it, create your PollerServlet class.

Now copy/paste the code from the original source to your version.

What I did (and this is a quick and dirty solution, I leave a more elegant approach to you) was to take the offending line of code above and replace it with this:

Map<String, Object>[] pollerRequestChunks;

try{
pollerRequestChunks =
(Map<String, Object>[])JSONFactoryUtil.deserialize(
pollerRequestString);
}catch (java.lang.ClassCastException e){
return null;
}

This handles the exception being thrown by the bad cast. Would it be better to check the input and handle it more gracefully? Yes it would, especially because this will still result in a log entry every minute of "Current URL /poller/send generates exception: null" as the method is returning a null value, but the idea here is to show how to do a simple Ext plugin that isn't related to Struts. Besides, it's still better than a stack trace dump every minute.

Now to create the deployable war...

The Liferay SDK has an ant deploy command that you can run from the command line in the folder for your ext project or you can double click it in the Ant window in Eclipse. Either way, if you've configured your SDK environment properly the result will be an Ext plugin that's been compiled, built as a .war file, and copied to the deploy folder in your Liferay instance.

Liferay will install it, but because we're making changes to the compiled code we will need to restart Liferay to make the changes take effect.

This is the quickest, simplest and dirtiest way to build an Ext plugin. Normally we'd do it with more finesse than that but this should be enough to get you started.

Thursday, June 9, 2011

Can't find bundle for base name javax.faces.Messages, locale en_US

This is another one of those error messages that, if you Google it, will bring back a LOT of message board posts and Jira entries and most of them won't help.

This post will talk specifically about times when this error message is thrown and the first two lines look like this:

java.util.MissingResourceException: Can't find bundle for base name javax.faces.Messages, locale en_US java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1427)

This one is what you get when you try and submit a JSF form.

I'm using:

Liferay 6.0.6
ICEfaces 1.8.1
Windows 7

In my case, I was modifying a portlet originally built for Liferay 4 and ICEfaces 1.7 to make it run on Liferay 6.

What was happening is the message-override.properties file wasn't being found. Now, a lot of people have a lot of different ideas on what folder that file goes in, and yet seldom mention the other half of the problem. You see, you have to tell JSF about this file, and that means going into the faces-config.xml.

<message-bundle>messages-override</message-bundle>

And make sure that goes in your <application> block.

And where should that file be? In WEB-INF/classes.

"So, how do you get that in Maven?"

Getting this messages-override.properties file into WEB-INF/classes isn't hard, but it isn't intuitive either if you're not used to dealing with Maven. In your project file structure, create a folder called resources under main. Your file goes there. When Maven is building your .war file, it'll take whatever is in resources and put it in WEB-INF/classes for you.