Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences

Edit this page


Referenced by
AppFuseSecurityMetho...




JSPWiki v2.2.33

[RSS]


Hide Menu

AppFuseSecurityMethods2


In Part I of this tutorial we got basic Method Invocation security working. But there still remains some holes in the security of our User object at the service level. For example if someone could get the controller to run UserManager.saveUser() on someone other than themselves, there is nothing at the service level to stop them.

This tutorial adds object specific security into the mix. Another way to accomplish this would be to use an access control list, but that solution may be a bit heavy for many apps. So ACL's will be a tutorial but not included in the core of AppFuse. So without getting into ACL's we can still secure our objects by making sure the user who is authenticated can only retreive, edit or delete his User object.

Table of Contents

  • [0] Prerequisites
  • [1] Add UserManager.updateUser() method
  • [2] Modify txProxyTemplate configuration
  • [3] Create the OwnerVoter
  • [4] Modify userManagerSecurity configuration
  • [5] Test All

Prerequisites [#0]

Basically you need to have completed part one of this tutorial.

Add UserManager.updateUser() method [#1]

Because we want new users to be able to create a new account, and we want existing users (who are not admin's) to only be able to update their own User object we need two separate methods in our UserManager to handle this. One method, updateUser(), will be secured and the other, saveUser() will not. NOTE: These methods should probably be updateUser() and createUser(), but I wanted to change as little as possible to get this working. You should be able to figure out how to make this change after understanding this tutorial.

We need to add or modify the following code to these classes: UserManagerTest.testSaveUser() change:

        userManager.saveUser(user);
to:
        userManager.updateUser(user);

UserManager add:

    public void updateUser(User user) throws UserExistsException;

UserManagerImpl add:

    public void updateUser(User user) throws UserExistsException {
        saveUser(user);
    }
NOTE: These manager methods should probably be smarter by making saveUser() only be able to save a new user and updateUser() only update an existing user, but for now this is what we have.

UserAction.save() change:

        mgr.saveUser(user);
to:
        mgr.updateUser(user);

Modify txProxyTemplate configuration [#2]

Because we are now saving a record with a method that does not fit the pattern save* we need to update the txProxyTemplate bean in applicationContext-service.xml.
    <bean id="txProxyTemplate" abstract="true"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager"><ref bean="transactionManager"/></property>
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="remove*">PROPAGATION_REQUIRED</prop>
                <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>

Create the OwnerVoter [#3]

I thought this would be appropriate to place in an org.appfuse.security package within src/service. You can download OwnerVoter here.

Modify userManagerSecurity configuration [#4]

We need to define a few more beans and modify the bean definition of userManagerSecurity in applicationContext-security.xml.

The OwnerVoter only works on Method Invocation security, so we now need to have a seperate accessDecisionManager for userManagerSecurity bean from the one being used for URI security.

    <bean id="businessAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
        <property name="allowIfAllAbstainDecisions"><value>false</value></property>
        <property name="decisionVoters">
            <list>
                <ref local="roleVoter"/>
                <ref local="userOwnerVoter"/>
                <ref local="usernameStringOwnerVoter"/>
            </list>
        </property>
    </bean>

You will notice that businessAccessDecisionManager now has references to a couple of new beans based on OwnerVoter. The first one looks for methods that have an argument of type User and grants access when the authenticated user's username matches User.getUsername() on that object.

    <bean id="userOwnerVoter" class="org.appfuse.security.OwnerVoter">
        <property name="processConfigAttribute">
            <value>OWNER_USER_EQUALS</value></property>
        <property name="processDomainObjectClass">
            <value>org.appfuse.model.User</value></property>
        <property name="internalMethod">
            <value>getUsername</value></property>
    </bean>

The second bean is for methods that are passed a username String rather than the entire object. In this case the string is compared to the authenticated user's username

    <bean id="usernameStringOwnerVoter" class="org.appfuse.security.OwnerVoter">
        <property name="processConfigAttribute">
            <value>OWNER_STRING_EQUALS</value></property>
        <property name="processDomainObjectClass">
            <value>java.lang.String</value></property>
        <property name="internalMethod">
            <value>toString</value></property>
    </bean>

Next we need to update the userManagerSecurity defintion to utilize the OwnerVoter's we just defined.

   <bean id="userManagerSecurity" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      <property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
      <property name="objectDefinitionSource">
         <value>
            org.appfuse.service.UserManager.getUser=admin,OWNER_STRING_EQUALS
            org.appfuse.service.UserManager.getUsers=admin
            org.appfuse.service.UserManager.removeUser=admin,OWNER_STRING_EQUALS
            org.appfuse.service.UserManager.updateUser=admin,OWNER_USER_EQUALS
         </value>
      </property>
   </bean>
You will notice that saveUser is not in the list because it is a public method, and the tomcat role is no longer needed because the user must be authenticated in order for either of the OwnerVoter's to grant access.

Test All [#5]

ant test-all should work now. It is difficult to show any differences in the security now that we have added this to the service layer. That is because the User object is pretty well protected in the UserAction. The main reason I wanted to make this is for other objects in people's apps based on AppFuse that have not had the same level of scruty in the controller as User.
Attachments:
OwnerVoter.java Info on OwnerVoter.java 12677 bytes


Go to top   Edit this page   More info...   Attach file...
This page last changed on 06-Nov-2006 13:52:59 MST by NathanAnderson.