Announcement Announcement Module
Collapse
No announcement yet.
JPA entity method security Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JPA entity method security

    Hey,

    I would like to use method security in JPA entity setters, so that I do not have to write DTOs.
    How to do this?

    @Secured works well with all beans in spring application context but not with entity methods like:
    Code:
    @Entity
    public class MyEntity {
    	...
    	@Secured("ROLE_ADMIN")
    	public void setOwner(Principal owner) { ... }
    }
    applicationContext-security.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
    
    	<!-- define method ADM -->
    	<beans:bean id="methodAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    		<beans:property name="decisionVoters">
    			<beans:list>
    				<beans:ref bean="authorizationVoter" />
    			</beans:list>
    		</beans:property>
    	</beans:bean>
    	
    	<!-- support @Secured annotation -->
    	<global-method-security secured-annotations="enabled" access-decision-manager-ref="methodAccessDecisionManager" />
    	...
    </beans:beans>
    applicationContext.xml:
    Code:
    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    ...
    I have also put some printlns in authorizationVoter but they are not triggered when calling setOwner in such an entity!
    Any solution?

    If there is no global solution (also objects created by new statement have to be secured) then I could also prepare those objects, manually, but where can that preparation method be found?

    regards,
    Max
    Last edited by MaxMan; May 11th, 2011, 06:12 AM.

  • #2
    Help, please!!!
    I've built annotation-driven spring web controller, generic service and form generation that handle multiple entity types (for rapid development).
    @PreAuthorize.. annotations are great. I do not need DTOs because Spring generates security proxies for annotated objects?!
    It should be possible to wrap objects out of spring context, manually. BUT HOW?
    In case of JPA entities it would be nice if the entity stays attached because properties are bound from request and all changed associations should also be persisted when saving the entity, afterwards.
    Is there some JPA adapter for that I doesn't find?

    regards,
    Max
    Last edited by MaxMan; May 12th, 2011, 10:45 AM.

    Comment


    • #3
      The Spring Security filters are not applied to your entities because they are not managed by Spring. Your entities are created either through the new operator or your ORM (e.g. Hibernate), so no "security proxies" are created here. I don't know if there's a way to configure this through an AspectJ compile, but otherwise you will need to apply access control to your service methods and/or controllers.

      Comment


      • #4
        Thanks for your reply.
        I have also tried this with the same result:
        Code:
        <protect-pointcut expression="execution(* de.algorythm.auth.interfaces.IOwnedResource.setOwner(..))" access="manage"/>
        Is there something else you mean with "AspectJ compile" (I am also new to AOP)?
        Access Control on DAOs and controllers works fine. It is just that everything works except entity property security.
        Finally, I would like to create an entity, annotate it and have a nice looking secured UI generated.
        Special entity properties that only authorized people can get or set should also be protected by annotation.
        Would be a pity if the idea fails through this problem.
        There has to be a simple method to wrap an object. That would be enough:
        Then, only the controller/facade has to remember the attached jpa entity reference to save it later if binding successful.
        ...Ah, no... of course the (lazy loading) entity associations also have to be wrapped, damn!

        EDIT:
        I could also write polymorphic update()/create() dao methods with security annotations but I would also need to write a DTO for such entity to secure request binding or define disallowed attributes in BeanWrapper but generic finding and evaluating secured properties is as complicated as writing the BeanWrapper by myself and I do not want to reinvent the wheel.

        EDIT:
        Also tried with spring security acl and explicit aop declaration without success:
        Code:
        @PreAuthorize("isAuthenticated() and hasPermission(#owner, 'MANAGE')")
        public void setOwner(Principal owner) { ... }
        ...
        <aop:config proxy-target-class="true" />
        <global-method-security pre-post-annotations="enabled" proxy-target-class="true">
        	<expression-handler ref="expressionHandler" />
        </global-method-security>
        EDIT:
        Secured objects cannot be resolved by ConversionService if DAO is annotated. Somehow, this is property security .
        In the worst case one has to put secured properties in a referenced @Embeddable class without setters but a supporting ConversionService with secured prototype bean from spring context.
        But every entity association has to be secured when binding request by calling a DAO's annotated write method.
        Therefore some write listener/filter has to exist in BeanWrapper...
        Last edited by MaxMan; May 12th, 2011, 01:43 PM.

        Comment


        • #5
          The pointcut you configured will only match Spring-registered components, i.e. classes declared as <bean>s in your application context or classes annotated with @Component (or some other stereotype annotation). Entities are not registered as components, so Spring simply does not know that they exist (instances at least) so it cannot intercept calls to their methods.

          As I said, maybe there's a feature in Spring Security that allows this if your code is compiled with AspectJ - this compiler could "weave in" access control code to your entities even if they aren't registered Spring components. This would apply to objects created with new or through the ORM (loaded from the database as you wrote).

          There's a section in the reference doc about domain object security http://static.springsource.org/sprin...main-acls.html maybe it can offer something you need.

          Comment


          • #6
            You could always retrieve the current SecurityContext in your entity using SecurityContextHolder.getContext() and perform access control manually. Not very clean as it would introduce a runtime dependency from your entities to Spring Security and add some boilerplate code in your entities, but it would work.

            Comment


            • #7
              My current attempt is as ugly as yours but without runtime dependencies in entities.
              At least, secures associated object's properties before binding request, inefficiently:
              Code:
              public interface IPermissionManager {
              	public void secure(Object o);
              }
              
              public class UpdatePermManager implements IPermissionManager {
              	// trigger authorization
              	@PreAuthorize("hasPermission(#o, 'UPDATE')")
              	public void secure(Object o) { }
              }
              
              public class FormUtil {
              	public BindingResult bind(Object entity, String name, ConversionService conversionService, Validator validator, IPermissionManager p, ServletRequest req, Model model) {
              		// check permission
              		secureBinding(entity, req, p);
              		// bind data
              		ServletRequestDataBinder binder = new ServletRequestDataBinder(entity, name);
              		binder.setConversionService(conversionService);
              		if (validator==null) {
              			binder.bind(req); // publishes BindingResult as request attribute
              		} else {
              			binder.setValidator(validator);
              			binder.bind(req); // publishes BindingResult as request attribute
              			binder.validate();
              		}
              		BindingResult result = binder.getBindingResult();
              		if (model!=null)
              			// publish BindingResult in model
              			model.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, result);
              		return result;
              	}
              	
              	private void secureBinding(Object entity, ServletRequest req, IPermissionManager p) {
              		Set<String> referencesDone = new HashSet<String>();
              		Enumeration<?> paramNames = req.getParameterNames();
              		String paramName, propertyOwner;
              		int lastDot;
              		BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(entity);
              		// iterate through request params, try to get value and enforce security by calling secured spring bean method with associated entity
              		while (paramNames.hasMoreElements()) {
              			paramName = (String)paramNames.nextElement();
              			lastDot = paramName.lastIndexOf('.');
              			if (lastDot > -1) {
              				propertyOwner = paramName.substring(0, lastDot);
              				if (!referencesDone.contains(propertyOwner)) {
              					referencesDone.add(propertyOwner);
              					try {
              						p.secure(bw.getPropertyValue(propertyOwner));
              					} catch(InvalidPropertyException e) {
              					} catch(PropertyAccessException e) {
              					} catch(BeansException e) { }
              				}
              			}
              		}
              	}
              }
              Next time I'll configure that default spring acl implementation although I do not believe this will solve my problem because it is also annotation-driven and as long as it is not integrated in jpa it cannot "know" which restriction is required for an entity/method without reading the annotation/configuration.
              I'll post my results here.

              regards,
              Max
              Last edited by MaxMan; May 12th, 2011, 07:19 PM.

              Comment


              • #8
                Last attempt was not a solution.

                The question is now how to configure (compile-time?) overweaving for @Secured-annotations for all classes (also those created with new) in spring?

                Comment

                Working...
                X