Announcement Announcement Module
Collapse
No announcement yet.
How to implement Entity/Finder authorization? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to implement Entity/Finder authorization?

    Hello everyone.

    Consider a newly created Roo-App (including "security setup") in which each user may only access his own data (order, shopping cart... the usual). How do I integrate authorization "the right way" in this situation?

    I know about the Spring Security annotations (PreAuthorize, PostFilter) and they do work for my service methods, but when copying the static findOrder(Long id) method and adding a @PostAuthorize to it, I felt like doing something wrong. I did take a brief look at Spring Security's contact sample with its ACLs, it seemed to complex for my case, but you may refer me back to that if it is the way to go.

    So I am still wondering, if there is a more general solution to this, especially to "harden" the generated finder method or an entity globally (like "accessing an order in any way is only allowed if order.customer.username == principal.username or principal.hasRole(ROLE_ADMIN)").

    Thanks for any answer/reference link/sample code

    Wolfram

  • #2
    Two recommendations.

    1) Use Hibernate Filters.

    2) My new favorite, use Spring Expression Language in your @RolesAllowed to access user information. I have seen this in the new Spring Security Documentation, but haven't actually done it yet myself.

    Mark

    Comment


    • #3
      Hi, I'm doing something like this right now, and thought I'd share what i've got working right now.
      Short answer is by doing
      Code:
      SecurityContextHolder.getContext().getAuthentication().getPrincipal()
      you get the currently authenticated user. However the users are pre-defined on an XML file. So what to do?

      First thing is getting the user from the database. To do that I create an entity that will hold users. I also have now Roles mapped to an enum, as I won't be changing them for now and add a finder to locate an user by its username
      Code:
      entity --class ~.domain.Person
      field string --fieldName username --notNull
      field string --fieldName password
      field boolean --fieldname enabled
      
      enum type --class ~.domain.PersonRole
      enum constant ROLE_USER
      enum constant ROLE_ADMIN
      
      field enum --fieldName role --type ~.domain.PersonRole --enumType STRING  --class ~.domain.Person
      finder add findPeopleByUsername
      the open the Role enum and let it implement the GrantedAuthority Interface
      Code:
      public enum PersonRole implements GrantedAuthority {
      	ROLE_USER, 
      	ROLE_ADMIN;
      
      	@Override
      	public String getAuthority() {
      		return name();
      	}
      }
      and get the Person class to implement UserDetails
      Code:
      @Entity
      @RooJavaBean
      @RooEntity(finders = { "findPeopleByUsername" })
      public class People implements UserDetails {
          @NotNull
          @Size(min = 4, max = 30)
          private String username;
      
          @NotNull
          private String password;
      
          @Enumerated(EnumType.STRING)
          private PersonRole role;
      
          private Boolean enabled;
         @Override
         public Collection<GrantedAuthority> getAuthorities() {
           return Arrays.asList((GrantedAuthority) role);
         }
      
         @Override
         public boolean isAccountNonExpired() {
           return enabled;
         }
      
         @Override
         public boolean isAccountNonLocked() {
           return enabled;
         }
      
         @Override
         public boolean isCredentialsNonExpired() {
           return enabled;
         }
      
         @Override
         public boolean isEnabled() {
           return enabled;
         }
      the last thing here is implement a class that implements UserDetailsService
      Code:
      @Component("userDetailsService")
      public class JpaUserDetailsService implements UserDetailsService {
      
      	@Override
      	public UserDetails loadUserByUsername(String username)
      			throws UsernameNotFoundException, DataAccessException {
      		try {
      			return (UserDetails) Person.findPeopleByUsername(username).getSingleResult();
      		} catch (EntityNotFoundException ex) {
      			throw new UsernameNotFoundException(ex.getMessage());
      		}
      	}
      
      }
      and enable it on applicationContext-security
      replace
      Code:
      <authentication-manager alias="authenticationManager">
          	<!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
          	<authentication-provider>
      	    	<password-encoder hash="sha-256"/>
      	        <user-service>
      	            <user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN"/>
      		        <user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" authorities="ROLE_USER"/>
      		    </user-service>
          	</authentication-provider>
      	</authentication-manager>
      by
      Code:
       <authentication-manager alias="authenticationManager">
          	<authentication-provider user-service-ref="userDetailsService"/>
       </authentication-manager>
      Up to here you will have the authentication working on the database instead of the pre-defined users on the xml file. It would be a great moment to load users and test it to make sure the auth is working.

      So the second part is obtaining the user data. You can load as I told at the beggining by doing that static method call, however static method feel seem un-springful to me, so I created a helper class that I inject through spring
      Code:
      public class ContextUserDetails {
      	private final SecurityContextHolderStrategy holderStrategy;
      
      	public ContextUserDetails(SecurityContextHolderStrategy holderStrategy) {
      		this.holderStrategy = holderStrategy;
      	}
      
      	public Person getPerson() {
      		Object principal = holderStrategy.getContext().getAuthentication().getPrincipal();
      		if (principal instanceof Person) {
      			return (Person) principal;
      		}
      		// principal object is either null or represents anonymous user -
      	    // neither of which our domain User object can represent - so return null
      	    
      		return null;
      	}
      }
      and define it on applicationContext.xml
      Code:
      	<!--Gets the active person object-->
       	<bean id="contextUserDetails" class="com.jeduan.util.ContextUserDetails">
       		<constructor-arg>
       			<bean class="org.springframework.security.core.context.SecurityContextHolder" factory-method="getContextHolderStrategy"/>
       		</constructor-arg>
       	</bean>
      so now when you want to use it just
      Code:
      @Autowired
      private ContextUserDetails contextUser;
      
      public someMethod() {
        Person person = contextUser.getPerson()
      }
      Whoa, so that was long, but hopefully you will get the hold of it rather promptly.
      Kind regards and best of lucks with your application!

      EDIT:
      Forgot the important part, what you asked.

      So, after all this setup, you then could do something like
      Code:
      @RequestMapping
      public String addToCart(Long itemId, ModelMap modelMap) {
          Item item = Item.findItem(itemId);
          item.setShopper(contextUser.getPerson);
          item.persist()
      }
      or setting up finders to find by the person you just got from the context, you get the idea.
      Best Regards
      Last edited by jeduan; Feb 18th, 2010, 07:19 PM.

      Comment


      • #4
        That's good as far as it goes, but I think what Wolfram is asking is how do you allow the user to go to this URL:
        http://hostname/appname/shoppingcart/29
        ... where that's the user's own shopping cart, but not to this URL:
        http://hostname/appname/shoppingcart/30
        ... where that's someone else's shopping cart.

        Comment


        • #5
          OK, I found this

          http://blog.springsource.com/2009/06...00m1-released/

          But I thought it was adding something to @RolesAllowed, but it is @PreAuthorize, or @PostAuthorize.

          I really like this approach the best now, it is far less to write than what jeduan posted, in which he is customizing the UserDetailsService and UserDetails objects.

          Now I am not against customizing UserDetails at all, as a matter of fact, I have done that in a real app. (I do some non-real apps to practice) And it makes for some cool ways to keep data that you would have normally put into the Session, but able to hold in the UserDetails object for quick access.

          But even with a custom UserDetails object, you can use it in the Spring expression language in your @Post or @PreAuthorize.

          Oooh, just saw this I like it

          http://stsmedia.net/spring-finance-p...3-integration/

          the third code box, first @PostFilter.

          But something looked familiar there. jeduan, that isn't your stuff is it? I saw a Person object and thinking it was your custom UserDetails object.

          Anyway, really looking at that last link and all the parts of that series, it looks like a really sweet good article to read.

          I digress

          Mark

          Comment


          • #6
            DOH!.

            Now, I realize why it is a cool blog, cause it is Stefan Schmidt's site.

            That is really funny. I think I am turning red.

            Mark

            Comment


            • #7
              There techniques I described are parts from here and there, but definitely Stefan's blog was one of the references I got

              Currently SpringSecurity's @PostFilter and @PreAuthorize are not working on Roo AFAIK, due to some stuff going on with aspects. The blog you linked creates an intermediate layer annotated with @Service which communicates with Entities and turns the results back to the Controllers.

              Agreed on the part that my method is really long and maybe cumbersome. OTOH you can get friendlier URLs that do not include the user's id, due to it being already on the SecurityContext, ie
              Code:
              http://server:8080/appname/shoppingcart
              instead of
              Code:
              http://server:8080/appname/shoppingcart/81
              which may not sound as that much of a hassle, but I find it more polished.

              Comment


              • #8
                @jeduan:

                Thanks for your elaborate description, but as andrews said, I do have all of that working already, but was looking for the "cleanest" solution for a business logic authorization structure (let's call it that).

                You are right that in case of a OneToOne relationship you do not really have to include any ID as you can get the shopping cart by using the information about the principal. However, in case of a OneToMany (like shipping addresses or orders) you need to specifiy an ID ("/app/order/14" and 14 is not one of the principal's orders). That is where I was stuck, stuck as in "could do it but it feels wrong somehow".

                Regarding methods on the entity you are right, as soon as I annotate a finder-method with @PostFilter, I do get an java.lang.IllegalStateException from the checkExposedObject() as the exposedObject and originalBeanInstance differ:

                Code:
                Post-processor tried to replace bean instance of type [com.company.project.MyType] with (proxy) object of type [$Proxy53] - not supported for aspect-configured classes!
                @bytor99999:

                I'll definitely have a closer look on those Hibernate filters, didn't know about them as I was looking for a Spring Security based solution.

                ---

                Considering the following two statements + the aforementioned exception, I come to think that you have to use a service layer in order to apply Spring Security's annotations in a Roo application.

                From the Roo reference:
                A web application will rarely require a services layer, as most logic can be placed in the web controller handle methods and the remainder in entity methods.
                From the Spring Security FAQ:
                Generally we would recommend applying method security at the service layer rather than on individual web controllers.
                I think I will go with @Services and securing their methods using Spring Security annotations (at least for write operations) + using Hibernate filters or additional constraints when reading data from the databse.

                Thanks everyone!

                Comment


                • #9
                  This is definitely an area where it would be nice to know the "standard" best practice approach to "ACL" security. There seems to be many different possible solutions, and which one is the "best" might end up just being subjective.

                  jeduan - I was definitely not disagreeing with you. I have followed and used similar approaches, and will have to on the project I am working for my iPhone app I have in the Apple App Store. Long Story

                  But lets just say that there is "Entitys" that I have that might equal to User. Or in Spring terms UserDetails object. Meaning I need the UserDetails object for security and there are entities in my model that you can map one to one to my User, and storing those entity objects in the UserDetails object seemed to me a clean nice solution, and I have used it.

                  Now, an issue might come up where there can be a One To Many relationship between your UserDetails and another Entity, but then comes into question of are we trying to store too much into the UserDetails and the benefit/cleaness is gone? Or what happens when the related data ends up mapping Many To Many between UserDetails and the other Entity.

                  I don't know if maybe starting a new thread would be better to see if anyone want to further discuss this and see what we might collectively come up with. I am definitely interested.

                  Mark

                  Comment


                  • #10
                    In looking at the Spring 3.x documentation, I saw this

                    "<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
                    <aop:scoped-proxy/>
                    </bean>

                    <bean id="userManager" class="com.foo.UserManager">
                    <property name="userPreferences" ref="userPreferences"/>
                    </bean>"

                    So, what if the Entity that we stored in the UserDetails object instead was declared as a bean set to session scope?

                    Then, do we have access to it using SPel, in the @Pre or @Post authorize annotation, and can get a value from that bean, and compare it to a PathVariable in the URI??

                    OK, this does seem out of scope to Roo at this point.

                    But, if Roo could have an option in an entity to set it to Session scope and well, I am just thinking here.

                    Mark

                    Oh, one last point, we could also make the Entity in question to be a FactoryBean, or use factory-method, if there needs to have some properties dynamically set at runtime to help create the Entity instance. OK, my brain hurts now, I will stop
                    Last edited by bytor99999; Feb 19th, 2010, 02:38 PM. Reason: Add more stuff

                    Comment


                    • #11
                      I had similar thoughts in these threads:

                      http://forum.springsource.org/showthread.php?t=79002

                      http://forum.springsource.org/showthread.php?t=79371

                      I share Wolfram's and andrews' concerns:

                      That's good as far as it goes, but I think what Wolfram is asking is how do you allow the user to go to this URL:

                      http://hostname/appname/shoppingcart/29

                      ... where that's the user's own shopping cart, but not to this URL:

                      http://hostname/appname/shoppingcart/30

                      ... where that's someone else's shopping cart.
                      (I also like jeduan's notion that the REST-Ful urls shouldn't necessarily have to contain the user's id just to return the logged-in user's objects. I haven't adopted that, but would like to.)

                      My solution to general object-level authorization requires some initial setup and would benefit from Roo doing some of these things for us. I fear I won't explain this well enough, but here's what I have done (experimentally - I'm not completely convinced yet):

                      1) Use an AppUser object that implements UsersDetails.

                      2) Note that the "owner" property of each of my entities is an object of type AppUser, which implements UserDetails as per #1 above. So each object is persisted with an owner (or AppUser) id column.

                      3) Use a BaseEntity interface and an AbstractBaseEntity abstract implementation to include id, version, and owner properties. Make every Roo entity extend AbstractBaseEntity. (I would also add dateCreated and dateModified, and possibly modifiedBy to include cases where the object can be and was modified by someone other than the owner.)

                      4) Include "Authentication auth" in the method signature of each controller method. (This currently requires me to push in my controller methods. I would be thrilled if Roo did this in its managed controllers.)

                      With the above, I can:

                      5) Write aspectj advice for each controller method to check if a requested object is "owned" by the currently logged-in user (auth.getPrincipal().getId()==baseEntity.getOwner( ).getId())

                      I think Roo could handle 1,2,3, and 4, and the developer could write the aspects with his own logic. A common aspect pointcut/advice would seem to be, as it is for me, something like:

                      Code:
                      @Before("execution(* com.mycompany.web.*.*.update(Authentication, BasePersistableObject+, BindingResult, ModelMap)) && args(auth, object, result, model)")
                      public void update(Authentication auth, BasePersistableObject object, BindingResult result, ModelMap model) { 
                      	AppUser u = (AppUser) auth.getPrincipal();
                      	if(u.getId().longValue()!=object.getOwner().getId().longValue())
                      	{
                      		throw new IllegalArgumentException("access denied");
                      		
                      	}
                       }
                      The benefit of using the BaseEntity interface is that a single joinpoint+advice block could be written to cover each "type" of controller method (update, delete, persist, find, findAll, findSome). This also allows for custom coding beyond what would be available using @PreAuthorize methods. Additionally, this creates security at the controller method level, leaving the developer to use all of the entity methods and/or service methods freely in administrative code.

                      One potential problem I can see is that the generated Controller method signatures are already complex, and it would not be unreasonable to expect Roo to change how it does its method signatures in controllers. If that changed from one version of Roo to another, all of the user-written aspects would break.
                      Last edited by mikej; Feb 20th, 2010, 11:20 AM. Reason: fixed urls (looked like duplicates)

                      Comment


                      • #12
                        Brainstorming: Could Roo provide a "@PreDelete" annotation or something, which would include access to the Authentication object and the relevant entity object, and which could be used by a developer to write his own methods to accept or reject calls to the delete, persist, update, and find controller calls?

                        I.e., a developer would write a method like this to control whether or not the "delete" method is actually called:

                        Code:
                        public class TaskController {
                        //..
                        	@PreDelete
                        	public boolean preDelete(Authorization auth, BaseEntity entity){
                        		 //logic here accepts or rejects the delete call
                        	}
                        }

                        Comment


                        • #13
                          Mike,

                          wouldn't just something like the @PreAuthorize("#entity.owner.id == principal.owner.id)") annotation solve your problem?

                          Luke described that in a blog (http://blog.springsource.com/2009/06...00m1-released/) and it's also scratched in the documentation (http://static.springsource.org/sprin...html#el-access).

                          One thing I didn't find out yet is how to specialize an ITD method without completely overriding it. Let's say I want to add the above named annotation to the ITD method "remove()" or I wanted to modify the income or the output of this method.

                          Does anybody know whether there's another way to do so than defining an aspect?

                          Thanks in advance
                          Alex

                          Comment


                          • #14
                            See http://forum.springsource.org/showpo...67&postcount=3 for a useful approach.

                            Comment

                            Working...
                            X