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


Referenced by
AppFuseSecurityMetho...




JSPWiki v2.2.33

[RSS]


Hide Menu

AppFuseSecurityMethods2


This is version 5. It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]


In part one 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: Plugin insertion failed: Unsupported parameter 'userManager.saveUser'.

ParameterDescriptionExample
printVersionIf specified, the plugin only prints its name an version.printVersion='true'
tabSizeNumber of spaces representing a tab character. Default is 2.tabSize='4'
stylesupported styles are: Eclipse, Kawa, Monochromestyle='monochrome'
borderboolean flag for rendering a table border around the converted result. Default is falseborder='true'
attachmentIf specified, the source code from the attached Java file will be used.attachment='HelloWorld.java'
urlIf specified, the source code from the Java file given by the url will be used (only available if this option is enabled in the wiki properties).url='http://www.java2html.de/HelloWorld.java'
alignmentSpecifies the horizontal alignment of the output. Supported values are: left, center, right default is left.alignment='center'
lineNumbersboolean flag for rendering line numbers. Default is falselineNumbers='true'
sourceIf specified, the source code contained in this parameter value will be used (only valid for one line of code).source='public final static main(String[] args);'
to: Plugin insertion failed: Unsupported parameter 'userManager.updateUser'.
ParameterDescriptionExample
printVersionIf specified, the plugin only prints its name an version.printVersion='true'
tabSizeNumber of spaces representing a tab character. Default is 2.tabSize='4'
stylesupported styles are: Eclipse, Kawa, Monochromestyle='monochrome'
borderboolean flag for rendering a table border around the converted result. Default is falseborder='true'
attachmentIf specified, the source code from the attached Java file will be used.attachment='HelloWorld.java'
urlIf specified, the source code from the Java file given by the url will be used (only available if this option is enabled in the wiki properties).url='http://www.java2html.de/HelloWorld.java'
alignmentSpecifies the horizontal alignment of the output. Supported values are: left, center, right default is left.alignment='center'
lineNumbersboolean flag for rendering line numbers. Default is falselineNumbers='true'
sourceIf specified, the source code contained in this parameter value will be used (only valid for one line of code).source='public final static main(String[] args);'

UserManager add:


     * Updates a user's information
     *
     * @param user the user's information
     * @throws UserExistsException
     */
    public void updateUser(User userthrows UserExistsException;

UserManagerImpl add:


     * @see org.appfuse.service.UserManager#saveUser(org.appfuse.model.User)
     */
    public void updateUser(User userthrows 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: Plugin insertion failed: Unsupported parameter 'mgr.saveUser'.

ParameterDescriptionExample
printVersionIf specified, the plugin only prints its name an version.printVersion='true'
tabSizeNumber of spaces representing a tab character. Default is 2.tabSize='4'
stylesupported styles are: Eclipse, Kawa, Monochromestyle='monochrome'
borderboolean flag for rendering a table border around the converted result. Default is falseborder='true'
attachmentIf specified, the source code from the attached Java file will be used.attachment='HelloWorld.java'
urlIf specified, the source code from the Java file given by the url will be used (only available if this option is enabled in the wiki properties).url='http://www.java2html.de/HelloWorld.java'
alignmentSpecifies the horizontal alignment of the output. Supported values are: left, center, right default is left.alignment='center'
lineNumbersboolean flag for rendering line numbers. Default is falselineNumbers='true'
sourceIf specified, the source code contained in this parameter value will be used (only valid for one line of code).source='public final static main(String[] args);'
to: Plugin insertion failed: Unsupported parameter 'mgr.updateUser'.
ParameterDescriptionExample
printVersionIf specified, the plugin only prints its name an version.printVersion='true'
tabSizeNumber of spaces representing a tab character. Default is 2.tabSize='4'
stylesupported styles are: Eclipse, Kawa, Monochromestyle='monochrome'
borderboolean flag for rendering a table border around the converted result. Default is falseborder='true'
attachmentIf specified, the source code from the attached Java file will be used.attachment='HelloWorld.java'
urlIf specified, the source code from the Java file given by the url will be used (only available if this option is enabled in the wiki properties).url='http://www.java2html.de/HelloWorld.java'
alignmentSpecifies the horizontal alignment of the output. Supported values are: left, center, right default is left.alignment='center'
lineNumbersboolean flag for rendering line numbers. Default is falselineNumbers='true'
sourceIf specified, the source code contained in this parameter value will be used (only valid for one line of code).source='public final static main(String[] args);'

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.

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]

test-all should now work. 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.
Attachments:
OwnerVoter.java Info on OwnerVoter.java 12677 bytes


Go to top   More info...   Attach file...
This particular version was published on 06-Nov-2006 13:52:44 MST by NathanAnderson.