Friday, March 25, 2011

Hibernate Audit Logs with Spring Security

I have recently had the worst time finding a solution to a seemingly simple problem - I had created a Hibernate interceptor to automate audit logs in the database, but I couldn't figure out how to obtain the currently logged in user.

If you've never created a Hibernate interceptor, there is really only one critical piece of information you need to know related to that - there is no way that I know of to obtain the current HttpRequest object inside of the interceptor. This was my biggest problem, as this system stored the currently authenticated user object in session, but I couldn't find a way to get to the session.

In researching how to do this, I read in a lot of places that you should implement your login solution using Spring Security. That way, from literally anywhere in the system, you can use the following code to retrieve the authenticated user:
SecurityContext secureContext = SecurityContextHolder.getContext();
Authentication auth = secureContext.getAuthentication();
Object principal = auth.getPrincipal();

String userName = null;
if (principal instanceof UserDetails) {
   UserDetails userDetails = (UserDetails) principal;
   userName = userDetails.getUsername();
} else {
   userName = principal.toString();
}

I was glad that there was such an easy solution to my problem, except for one minor hiccup - the project I was working on didn't use Spring Security for authentication. I also did not have the time or the authority to change that.

However, with a little more digging, I found that you can add in Spring Security to other authentication systems, so you can make use of some of these other features. So I set out with a new goal - find the minimum amount of Spring Security configuration necessary in order to execute the above snippet.

The first step was relatively easy. It turns out that you just add the following code to your Login page, after the user successfully authenticates:
Authentication auth = new UsernamePasswordAuthenticationToken(
        user.getUsername(), 
        user.getPassword()
    );
SecurityContextHolder.getContext().setAuthentication(auth);

With just that code snippet alone, the audit logs seemed to work great. I tested it, everything was working great, so I pushed out my changes to the production system. Unfortunately, I then bumped into one of the pitfalls of only having one person testing - the above solution alone is not thread safe.

When first exposed to that statement, many Spring Security familiar developers will scoff at my inexperience, and let me know that SecurityContextHolder attaches to a ThreadLocal, so we should be good to go. However, a little bit of additional digging reveals the following weakness:
In an application which receives concurrent requests in a single session, the same SecurityContext instance will be shared between threads. Even though a ThreadLocal is being used, it is the same instance that is retrieved from the HttpSession for each thread. This has implications if you wish to temporarily change the context under which a thread is running. If you just use SecurityContextHolder.getContext().setAuthentication(anAuthentication), then the Authentication object will change in all concurrent threads which share the same SecurityContext instance.

As it turns out, there is one more piece of critical configuration you need in order to enable all of this to work successfully. You need to create a Spring bean for the class SecurityContextPersistenceFilter. From the same page as above:
In Spring Security, the responsibility for storing the SecurityContext between requests falls to the SecurityContextPersistenceFilter, which by default stores the context as an HttpSession attribute between HTTP requests. It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes.

So the final piece of the puzzle is to add the following line to your applicationContext:
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

So, long story short, the above three code snippets are all that you need in order to implement Spring Security's ability to retrieve the current user from anywhere in your system.

Wednesday, March 2, 2011

Novell IdM Local Variables

This is a post that is really going to make sense only to those of you who work with building policies with Novell's Identity Manager system. I ran into this problem recently, and wanted to get it out into the ether in case others run into the same issue.

In Novell policies, it is possible to create local variables. One typically uses these local variables in order to temporarily store information about a user. There are two different types of scope that you can use: policy or driver.

The policy scope I was already well versed with. It is available to any other rule within the policy, allowing you to transfer information from one rule to the other.

The driver scoped local variable was, I assumed, a way to transfer information between policies about a particular transaction. What I have discovered, though, is that driver scoped means that the variable remains set for as long as the driver is running, across all transactions.

This came to bite me due to how I was using the variable. Inside of the Create Rule, I was setting a local variable "genCN", which is a generated CN value. I was then setting this to be the default value for the CN attribute. Note that I was not setting the Operation Attribute CN, but rather the default value for CN - thus I can not access Operation Attribute CN later in the transaction.

Later, in the Command Rule, I needed access to "genCN" if it had been set earlier. So I did an if local variable is set call, expecting that for updates the genCN would not be set, and I would need to query the meta tree to obtain the object's CN. Come to find out, though, that I was picking up a CN value from that local variable when an object was being updated - but unfortunately for me it had the value of a prevous entry's CN. Oops.

Why in the world driver-scoped local variables would function like that, I have no idea. They basically now take on the role of GCVs that you can update dynamically. Furthermore, there is no way to pass information about a particular transaction from one policy to another, unless you are sure to be setting that variable each time through the driver.

Oh well, such is a Novell user's life, I suppose. I was able to come up with a work around for my issue, and no harm done. Basically, I just added a policy at the front of my driver that sets that local variable to an empty string, and instead of doing an is set check, I do a check that it is not equal to empty string. Just wanted to throw this out there in case anybody else was having a similar issue.

Tuesday, March 1, 2011

Maven Release Plugin

I have recently started using a plugin for Maven that has been an incredible time saver, so I thought I would post a quick tutorial on what it is and how to use it. It is called the Maven Release Plugin, and when developing a java library it should be your new best friend.

Basically, this plugin automates a ton of the little manual steps that you have to go through every time you perform a release. It will:
  1. Increment the version number of the project in the pom
  2. Check the updated version number into svn
  3. Add a tag to the project in svn for the current release
  4. Compile, Test, Package
  5. Upload the packaged jar file to your local Maven repository
  6. Append “-SNAPSHOT” to the current version in the project pom for continued development
  7. Check the updated version number into svn

It makes a few assumptions in order to execute successfully:
  1. Before trying to release, all of your local changes must be checked in. It will only release what is current in svn.
  2. Your current version number, the first time you use this, must end with “-SNAPSHOT”
  3. You must have a local Maven repository set up

Once you have this all set up, then releasing a new version of your library becomes as easy as checking your regular code changes into svn and running a simple maven command. No more worrying about remembering to increment your version number, no more concern about not having things checked into svn. It mandates a lot of the best practices that one should be following anyway.

In order to get this set up, there are a handful of steps that must be followed:
  1.  You must have the “svn” command on your command line execution path. For Windows, I downloaded and installed SlikSvn, which automatically adds the svn command to your path (requires a restart after installation)
  2. Add the following block to your project’s pom.xml at the top level under the root <project> node:
    <scm> 
       <connection>scm:svn:https://your.svn.url/ProjRoot</connection> 
       <developerConnection>scm:svn:https://your.svn.url/ProjRoot</developerConnection> 
    </scm>
    
  3. Add the following block to your project’s pom.xml under <build><plugins>:
    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-release-plugin</artifactId>
       <version>2.1</version>
    </plugin>
  4. If you are not already set up to deploy to your local Maven repository, you need to add the following block under <build><extensions>
    <extension>
       <groupId>org.apache.maven.wagon</groupId>
       <artifactId>wagon-webdav</artifactId>
       <version>1.0-beta-2</version>
    </extension>
  5. If you are not already set up to deploy to your local Maven repository, you need to add the following block under <distributionManagement>
    <repository>
       <id>internal</id>
       <name>Internal Release Repository</name>
       <url>dav:http://your.internal.repository</url>
    </repository>
In order to run the plugin, you just execute "mvn release:prepare release:perform", or from Eclipse you would do a Run As > Maven build... and put "release:prepare release:perform" in the Goals.

That's all there is to it. With a single command, you can now handle versioning, building, testing, and deploying of your java library.