Archive for May, 2007

Business Logic Layer Security and Transactions with Annotations

Not too long again, my place of employment switched to using the Acegi framework for security. The major reasons for this switch were the large user community using Acegi and it’s officially the “Security System for Spring.” One of the loose ends that we didn’t tie up as part of the initial migration was method level security declared with Java 1.5 Annotations. Our old security system had it, but it was practically unused (since the web layer performed the same checks), so we just chucked it. Unfortunately, adding the functionality back it was not as easy as we would have liked. This post is basically an overview of how we got Acegi to use Spring AOP for method level security and still play nice with our transactional support.

We already had our transactional support configured before we started implementing method level security. Like our security, we had this configured with annotations. Our pattern is to annotate our business logic interfaces. The corresponding implementation remains un-marked. This is an example of how our business interface looked before security.

@Transactional
public interface UserManager {

    User createUser(String login, String password);

    @Transactional(readOnly=true)
    User getUser(String userId);

    User deleteUser(String userId);
}

Spring’s AOP support reads the @Transactional annotation on these interfaces and intercepts the method invocation to add a transaction around it. Getting Spring to perform this dark magic is as simple as adding a few lines to your spring context xml file.

First, you need to tell Spring to do it’s AOP magic.

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

This bean definition tells Spring to check for potential AOP advice (ie, method interceptors) to run on a classes method at bean creation time. Anytime your bean is injected into another class, you will get a aop-aware, proxied version, instead of your vanilla concrete implementation. In order to add transactional “advice” to your proxied version, the following lines need to go into your spring context configuration.

    <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
        <property name="transactionInterceptor" ref="txInterceptor"/>
    </bean>

    <bean id="txInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="transactionAttributeSource">
            <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
        </property>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

Since we use Hibernate, we use the HibernateTransactionManager. If you don’t use hibernate, you have a smorgasbord to choose from.

This is what we had before we tried to add method level security. To add method level security, the first step is to realize that you can only have one AOP Proxy Creator. Therefore, blindly following the tutorials on the Spring website won’t help too much. I’m also assuming that you have a working Acegi setup that does authentication at a URL level and that you have an authentication manager bean configured. Configuring that aspect of Acegi is outside the scope of this post.

We decided to use role-based authorization for our methods, so we configured a RoleVoter and an AccessDecisionManager to make decisions based on the users GrantedAuthorities. How users get granted authorities is covered in the Acegi documentation.

    <bean id='accessDecisionManager' class='org.acegisecurity.vote.AffirmativeBased'>
        <property name='decisionVoters'>
            <list><ref bean='roleVoter'/>
        </property>
    </bean>

    <bean id='roleVoter' class='org.acegisecurity.vote.RoleVoter'>
        <property name="rolePrefix" value="PERMISSION_" />
    </bean>

The standard rolePrefix is ‘ROLE_’. We just used ‘PERMISSION_’ since it maps to our egacy model more appropriately. Next, we need to configure something to read the Security annotations:

    <bean id="objectDefinitionSource" class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
      <property name="attributes">
        <bean class="org.acegisecurity.annotation.SecurityAnnotationAttributes" />
      </property>
    </bean>

Then we put it all together by creating a security interceptor and adding it to the chain of advisors that the proxy will call before the method invocation.

    <bean class="org.acegisecurity.intercept.method.aopalliance.MethodDefinitionSourceAdvisor">
        <constructor -arg>
            <ref bean="methodSecurityInterceptor" />
        </constructor>
    </bean>

    <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
        <property name="validateConfigAttributes"><value>true</value>
        <property name="authenticationManager"><ref bean="authenticationManager"/>
        <property name="accessDecisionManager"><ref bean="accessDecisionManager"/>
        <property name="objectDefinitionSource"><ref bean="objectDefinitionSource"/>
    </bean>

Finally, we can annotate our classes. If we wanted to ensure that only users with the permission to create a user (PERMISSION_UserCreate) then we need to add ‘@Secured({“PERMISSION_UserCreate”})’ to the correct method on the interface. Our final interface looks like:

@Transactional
public interface UserManager {

    @Secured({"PERMISSION_UserCreate"})
    User createUser(String login, String password);

    @Transactional(readOnly=true)
    User getUser(String userId);

    User deleteUser(String userId);
}

Viola! Users without the correct permissions will no longer be allowed to create new users.

Unfortunately, things aren’t ever that simple, and we ran into one pretty major issue. A good chunk of our beans in the business logic layer didn’t get proxied. Therefore, we got neither transactions nor security on these beans. To add the confusion, the business-logic calls failed in a very odd way. Whenever we did an update or create on a persisted model object, we got a:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove ‘readOnly’ marker from transaction definition.

This, of course, was totally unhelpful, since we didn’t have a readOnly marker on our transaction definition, and we were pretty sure that we were setting our FlushMode in our transaction interceptor. Removing the security annotations stuff made the problem go away again. Eventually we, realized the problem was that instead of a proxy with transaction advice and security advice, we received the configured implementation of the interface. Our interceptors hadn’t finished initializing by the time some beans were creating. The culprit turn out to be the manner in which we created our spring context files. The structure of our files was:

   <!--Transaction Stuff -->
   <!-- a lot of bean defs -->
   <!-- Security stuff -->
   <!-- Some more beans -->

Changing that to:

   <!-- Security stuff -->
   <!--Transaction Stuff -->
   <!-- a lot of bean defs -->
   <!-- Some more beans -->

made the problem go away for good. If you’re having weird issues with transactional annotations and security annotations, this would probably be a good place to look.

  • Share/Bookmark

Installing Freevo on OpenSUSE

In my last post, I detailed the process of getting Freevo installed on Ubuntu. It actually wasn’t too bad, unless you wanted your Multi-Media PC to respond to an IR remote. This post is a quick overview on how to install Freevo on an OpenSUSE box. The details are once again left out, since each step is much better explained from the pages found in a google search.

  1. Install openSUSE
  2. Add the OSS and NON-OSS repositories to the software sources
  3. Add the Packman, Guru, and NVIDIA (optional) repositories
  4. Open the Yast software install tool. Install Freevo, Lirc, Xine, Mplayer, and DVD packages
  5. Search the web on how to install libdvdcss on OpenSUSE and follow those directions
  6. Configure Freevo
  7. Configure NVIDIA card for TV-OUT
  8. Configure Xine to play to the correct audio source
  9. Set $LIRC_MODULE correctly in /etc/sysconfig/lirc
  10. Configure lirc for freevo
  11. Configure LIRC and Freevo to start on boot
  12. Reboot
  13. Done.
  • Share/Bookmark

Installing Freevo On Ubuntu

We were pretty impressed by the demos of LinuxMCE, so we decided to give it a try. More details on that, when my wife decides to stop being lazy. The end result was pretty disastrous and we decided to switch back to Freevo. Since I keep hearing good things about Ubuntu, I decided to use that at the base of my Freevo install. This is by no means a detailed walk-though, it just describes the steps I took to try and get it working.

  1. Install Ubuntu
  2. Install restricted formats
  3. Attempt to install Freevo, but get stymied by a python dependency error
  4. Search internet for a list of dependencies and install those
  5. Check that mp3s, dvds, and movie files play in xine or mplayer
  6. Compile and install Kaa, install any dependencies you didn’t catch earlier
  7. Compile and install Freevo
  8. Marvel about how not bad that process was
  9. Install lirc (remote control package) from repositories
  10. Scratch head over the non-existence of lirc modules
  11. Discover that package was just source, and compile it using the directions
  12. Load module, gawk at strange IRQ related error
  13. Add boot option, recommended by the strange IRQ error, to grub
  14. Reboot
  15. Load module, get same error
  16. Search google, find hack, and try the last to steps again, repeatedly
  17. Break down in tears
  18. Reach for OpenSUSE disk
  • Share/Bookmark

OpenSUSE NFS Configuration Tutorial

In a heterogeneous OS environment, the network file system of choice is clearly SMB/CIFS, mostly since heterogeneous implies the existence of windows machines. Lately though, I’ve been getting a little annoyed with some of the quirks of a file-system from the single user Windows world. I also don’t have any Windows machines left. Therefore, when it came time to share one resource to multiple machines, I decided to give NFS a try.

The resources for NFS are out there, but there wasn’t anything specific for openSUSE and some level of translation was required. The two docs I used to get this working where:
RedHat’s and Linux Help’s. If you’re trying to configure nfs on something besides openSUSE, that’s probably a better place to look.

The first step on openSUSE is to open up Yast2 and enter the software management section. Filter on packages with a name containing ‘nfs’ and you should see something like this:
Install NFS
Since this is a server box, I only have the console version of Yast installed. The X version will look similar. Note that the nfs-server package is not installed. This is recommended, since the kernel support for NFS is superior to the userland version. Packages you should have installed are portmap, and yast2-nfs-server. Having NTP installed running is also a good idea, otherwise you might encounter errors about files that only exist in the future.

Once you’ve installed the required packages, it’s time to open the NFS configuration wizard.
configure nfs

This wizard is pretty straight-forward. The first screen asks if you want to start the NFS server (you should say yes), followed by a screen that allows you to configure directories to share (or ‘exports’ in nfs lingo).
Start Server

Add directories

The directory must be an absolute path. You can also specify options such as read-write permissions and how to treat root clients on this screen. After you’ve added a few directories, that’s it. You’re done configuring the server. Yast2 will restart the daemons for you.

You can make sure nfs is running properly on your box by entering the command rpcinfo -p. You should see multiple entries for nfs with both udp and tcp sockets.

verify if nfs is working

Now that the server is functional, it’s time to configure the client. The steps are similar. You need to make sure that you have yast2-nfs-client installed on the machine. The dependencies for this package include everything you need. If you don’t want to use yast, you can just mount the resources manually:
mount server.domain.com:/path/to/resource /where/to/mount
Or if you want to do it the SUSE way, you can open Yast2 on the client, and enter the same information on the NFS Client screen. This will also configure the correct fstab entries for you.

Configure Client

If everything went right, your files will be available at /where/to/mount on the client machine. If it didn’t, then take a look at this troubleshooting guide.

  • Share/Bookmark

Test-driven Programming

I’m definitely one of those programmers that believes that a programmer’s true calling is to create bugs. After all, if we don’t create them, we’ll have nothing to fix and fixing a lot of issues looks really good when performance reviews come around. However, writing bugs comes with the added hassles of irate customers and frustrated qa people. These two are normally don’t look so good when performance reviews come around. But… committing buggy code saves you from the annoyance of making a change, compiling, restarting jboss, clicking through a bunch of screens just to discover that you wrote ‘LIKE’ instead of ‘ILIKE’ in your query, and then repeating ad infinium. On one hand, you have irate customers on the other you have mind-numbing clicking. Either way, you’re likely to lose your mind.

The slow decline towards insanity lead me to experiment with unit testing. Only a crazy person would dare to figure out why 99% of the written unit tests failed. Eventually, they started to work, and it became clear that it might not have been so crazy after all. Although it sounds ridiculous, the test-driven proponents claims that unit-testing saves developer cycles really is true.

That statement is very counter-intuitive. How on earth can writing twice the code save me time in implementing a feature? It really doesn’t make sense, until you realize that by the time you’ve waited for ant to finish, jboss to start, and you’ve had the time to click through the 5 screens, it’s been half the time it takes to write a test case. By the second run, you’re even. With a decent IDE, you don’t even have to wait for a compile. Also, since test cases run in a minimal context, they take a fraction of the startup time as JBoss. You pay Somewhat high initial cost, but almost no recurring cost.

Of course, if you’re using a homebrew stack and framework, there might be astronomically high initial environment setup costs that negate any benefit you could achieve. Thankfully, all the major frameworks make it dead simple to write useful test cases. Spring gives you an autowire test context that you can extend. In this context, all your beans will magically just work. Rails goes even further by providing tools to create data and migrate your database on top of just running tests.

It took a while, but I’m firmly a believer now. I hate clicking through screens, and this gives me a programmer-friendly, automated way of making sure my code doesn’t suck and actually works. Once you get used to it, there’s little not to like.

  • Share/Bookmark
Return top

About

This is my blog about programming. For random stuff, checkout my Twitter or Tumblr