News

Welcome to End Point’s blog

Ongoing observations by End Point people

Spring authentication plugin

One of our clients regularly deploys Pentaho with their application, and wanted their users to be able to log in to both applications with the same credentials. We could, of course, have copied the user information from one application to another, but Pentaho, and the Spring system it uses for authentication, allows us to be much more elegant.

Spring is often described as an "application development framework", or sometimes an "Inversion of Control container", which essentially means that you can redefine, at run time, exactly which objects perform various services within your application. That you have to navigate a bewildering and tangled web of configuration files in order to achieve this lofty goal, and that those files suffer from all the verbosity you'd expect from the combined forces of XML and Java, normally isn't as loudly proclaimed. Those inconveniences notwithstanding, Spring can let you do some pretty powerful stuff, like in our case, redefining exactly how a user gets authenticated by implementing a few classes and reciting the proper incantation in the configuration files.

Spring handles most of the plumbing in the Pentaho authentication process, and it all starts with this particular definition from one of the Spring configuration files:


The authenticationManager bean contains a list of authentication processors, and here's where our custom code begins. These beans must implement the Spring AuthenticationProvider interface, and to customize the authentication we can just put a new bean in this list. The list is ordered; refer to the ProviderManager documentation for details, but essentially any of the beans in the list can accept a set of credentials. Here, we've simply added a reference to our new bean, to the beginning of the list. The new bean is indicated by name only; it needs to be defined elsewhere, like this:


Here we've mapped the bean name to a specific class name. We can also add whatever configuration properties we need. So long as there are corresponding setter methods in the actual code (e.g. "public void setProperty_1(String value)") these will Just Work. Along with these setter methods, we must implement the two methods in Spring's AuthenticationProvider interface. The most important is the aptly-named authenticate(), which Spring will call when a user tries to log in.

authenticate() gets one argument, an Authentication object, and when a user presents valid credentials, that's also what it returns. The first thing our new implementation needs to do is get the username and password from the authentication object:


At this point we've got two jobs to do: first, validate the credentials, and second (assuming the credentials are valid), determine what roles the user should be given. In this particular case we used the authentication database from the client's application to validate the credentials; that code will be completely different from one bean to the next, so I won't share it here, but the second part, finding the user's roles, is pretty consistent. Spring lets you set up as many roles as you'd like, giving each a different text name, and simply assumes all the different roles will mean something to the application (Pentaho, in this case) later on. For our setup, we look up the user's roles from a database, and then tell Spring about them like this:


At this point, the user has successfully logged in. If we didn't want to allow the user in, we could instead throw an AuthenticationException, and Spring would go on to next AuthenticationProvider in the list. The beauty of it all is now that I have a working plugin, I can modify my application's authentication system simply by modifying a configuration file or two (or three) and restarting the application.

5 comments:

David . said...

How did you deploy your authenticationprovider? Just create a jar and put it on lib folder?

Joshua Tolley said...

Yes, once you've modified the configuration files to tell Spring to expect that class, the only other thing you have to do is make sure that class ends up in the classpath somewhere. The easiest way to do this is often to put the class in a JAR file and put it someplace like tomcat's WEB_INF/lib folder.

KK said...

Where are the all roles specified or do we not need it?
Just add system roles provided by Pentaho after authentication?

I am able to extend the class and add the jar file to the WEB-INF/lib and for some reason it is going in but I get a bunch of errors like this repeatedly.
3:52:52,149 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/pentaho].[jaxrsEndpoint-spring]] (http-/127.0.0.1:8080-3) JBWEB000236: Servlet.service() for servlet jaxrsEndpoint-spring threw exception: java.lang.NullPointerException
at org.pentaho.platform.engine.security.PentahoSubstringRoleVoter.vote(PentahoSubstringRoleVoter.java:77) [pentaho-platform-core-5.0.4.jar:5.0.4]
at org.springframework.security.vote.AffirmativeBased.decide(AffirmativeBased.java:51) [spring-security-core-2.0.5.RELEASE.jar:]
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:262) [spring-security-core-2.0.5.RELEASE.jar:]
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106) [spring-security-core-2.0.5.RELEASE.jar:]

Joshua Tolley said...

You don't have to define the roles anywhere, actually. All Spring cares about (or cared about back when this was written, at least) was that you feed it a list of role names, and it will assume that entries in that list will match role names you use elsewhere. in our case, these role names show up in custom Pentaho metadata, but all Spring needs to know is the role's name.

As to your exception, I'm afraid I can't shed much light. I've often found it helpful to browse through Pentaho's source code when tracking down stuff like that. Even when the exception comes from Pentaho's code, I've found the problem was usually my own fault.

KK said...

Is it possible to attach the applicationContext-spring-security file you have? or any other customized files you used. I have been trying to figure this out for a week now :).