Raible's Wiki
Raible Designs AppFuseHomepage- Korean - Chinese - Italian - Japanese QuickStart Guide User Guide Tutorials Other ApplicationsStruts ResumeSecurity Example Struts Menu
Set your name in
UserPreferences
Referenced by
JSPWiki v2.2.33
Hide Menu |
This is version 7.
It is not the current version, and thus it cannot be edited. 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
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:
Plugin insertion failed: Unsupported parameter 'public'. UserManagerImpl add:
Plugin insertion failed: Unsupported parameter 'UserExistsException'. 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:
|