Spring Security - Annotation Based Method level Security Handler - Part2



Now lets explore the code a little deeper to get a better understanding of how Spring in a best way performs all the Task.
So from the above configuration it is quite evident that at the heart lies the CustomMethodSecurityExpressionHandler,
which incorporates a permissionEvaluator i.e. DefaultPermissionEvaluator and priviledgeEvaluator i.e.
CustomPriviledgeEvaluator The code for permissionEvaluator is:
public class DefaultPermissionEvaluator implements PermissionEvaluator {

 @Override
 public boolean hasPermission(Authentication auth, Object arg1, Object arg2) {
  // TODO Auto-generated method stub
  
  System.out.println(" *******1) The Authentication is:"+auth+"\n"+arg1+"\n"+arg2);
  return true;
 }

 @Override
 public boolean hasPermission(Authentication auth, Serializable arg1,
   String arg2, Object arg3) {
  // TODO Auto-generated method stub
  System.out.println(" *******  2) The Authentication is:"+auth+":"+arg1+":"+arg2+":"+arg3);
  
  
  return true;
 }




and Priviledge Evaluator is:
 
 public class CustomPriviledgeEvaluator implements PriviledgeEvaluator {

 @Override
 public boolean hasPriviledge(Authentication paramAuthentication,
   Object paramObject1, Object paramObject2) {
  // TODO Auto-generated method stubn
  
  System.out.println("********** 1) CustomPriviledgeEvaluator:"+paramAuthentication+":"+paramObject1+":"+paramObject2);
  return true;
 }

 @Override
 public boolean hasPriviledge(Authentication paramAuthentication,
   Serializable paramSerializable, String paramString,
   Object paramObject) {
  // TODO Auto-generated method stub
  System.out.println("*********   Returning TRUE \n*************  2) CustomPriviledgeEvaluator:"+paramAuthentication+":"+paramSerializable+":"+paramString+":"+paramObject);
  
  return true;
 }

 
 
Now, the CustomPriviledgeEvaluator contains the logic corresponding to our custom annotation.
The code for CustomMethodSecurityExpressionHandler is:


import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.access.PermissionCacheOptimizer;
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;
import org.springframework.util.Assert;


public class CustomMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler
implements MethodSecurityExpressionHandler{

 //protected final Log logger = LogFactory.getLog(getClass());
   private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
   private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();
   private PermissionCacheOptimizer permissionCacheOptimizer = null;
   private PriviledgeEvaluator priviledgeEvaluator;// = new CustomPriviledgeEvaluator();
   
   public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi)
   {
    System.out.println(" *********************** In  Create createEvaluationContextInternal with auth:"+auth+"  Class:"+auth.getClass()+":"+mi.getClass());
    return new CustomMethodSecurityEvaluationContext(auth, mi, this.parameterNameDiscoverer);
   }
   
   protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation)
   {
    
  System.out.println(" ******************** In  createSecurityExpressionRoot authentication:"+authentication.getClass()+":"+invocation.getClass());
     CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
     root.setThis(invocation.getThis());
     root.setPermissionEvaluator(getPermissionEvaluator());
     root.setTrustResolver(this.trustResolver);
     root.setRoleHierarchy(getRoleHierarchy());
     root.setPriviledgeEvaluator(getPriviledgeEvaluator());
     
     return root;
   }
   
   public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx)
   {
    System.out.println(" In filter:"+filterTarget.getClass()+":"+filterExpression.getValue()+":"+ctx.getRootObject());
     MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations)ctx.getRootObject().getValue();
     /*boolean debug = this.logger.isDebugEnabled();
     if (debug) {
       this.logger.debug("Filtering with expression: " + filterExpression.getExpressionString());
     }*/
     if ((filterTarget instanceof Collection))
     {
       Collection collection = (Collection)filterTarget;
       List retainList = new ArrayList(collection.size());
       /*if (debug) {
         this.logger.debug("Filtering collection with " + collection.size() + " elements");
       }*/
       if (this.permissionCacheOptimizer != null) {
         this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), collection);
       }
       for (Object filterObject : (Collection)filterTarget)
       {
         rootObject.setFilterObject(filterObject);
         if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
           retainList.add(filterObject);
         }
       }
       /*if (debug) {
         this.logger.debug("Retaining elements: " + retainList);
       }*/
       collection.clear();
       collection.addAll(retainList);
       
       return filterTarget;
     }
     if (filterTarget.getClass().isArray())
     {
       Object[] array = (Object[])filterTarget;
       List retainList = new ArrayList(array.length);
      /* if (debug) {
         this.logger.debug("Filtering array with " + array.length + " elements");
       }*/
       if (this.permissionCacheOptimizer != null) {
         this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), Arrays.asList(array));
       }
       for (Object o : array)
       {
         rootObject.setFilterObject(o);
         if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
           retainList.add(o);
         }
       }
       /*if (debug) {
         this.logger.debug("Retaining elements: " + retainList);
       }*/
       Object[] filtered = (Object[])Array.newInstance(filterTarget.getClass().getComponentType(), retainList.size());
       for (int i = 0; i < retainList.size(); i++) {
         filtered[i] = retainList.get(i);
       }
       return filtered;
     }
     throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
   }
   
   public void setTrustResolver(AuthenticationTrustResolver trustResolver)
   {
     Assert.notNull(trustResolver, "trustResolver cannot be null");
     this.trustResolver = trustResolver;
   }
   
   public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer)
   {
     this.parameterNameDiscoverer = parameterNameDiscoverer;
   }
   
   public void setPermissionCacheOptimizer(PermissionCacheOptimizer permissionCacheOptimizer)
   {
     this.permissionCacheOptimizer = permissionCacheOptimizer;
   }
   
   public void setReturnObject(Object returnObject, EvaluationContext ctx)
   {
     ((MethodSecurityExpressionOperations)ctx.getRootObject().getValue()).setReturnObject(returnObject);
   }

 public PriviledgeEvaluator getPriviledgeEvaluator() {
  return priviledgeEvaluator;
 }

 public void setPriviledgeEvaluator(PriviledgeEvaluator priviledgeEvaluator) {
  this.priviledgeEvaluator = priviledgeEvaluator;
 }
   
   
 
}

 
 
In CustomMethodSecurityExpressionHandler class in createEvaluationContextInternal() a
CustomMethodSecurityEvaluationContext object is created which performs method level security evaluation task.
The code is:
 
 public class CustomMethodSecurityEvaluationContext extends
  StandardEvaluationContext {
 
   private ParameterNameDiscoverer parameterNameDiscoverer;
   private final MethodInvocation mi;
   private boolean argumentsAdded;
   
   public CustomMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi)
   {
     this(user, mi, new DefaultSecurityParameterNameDiscoverer());
   }
   
   public CustomMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi, ParameterNameDiscoverer parameterNameDiscoverer)
   {
     this.mi = mi;
     System.out.println("1)********************"+this.mi.getClass()+":"+this.mi.getMethod());
     this.parameterNameDiscoverer = parameterNameDiscoverer;
     //System.out.println( this.parameterNameDiscoverer.getParameterNames(this.mi.getMethod()).length);
   }
   
   public Object lookupVariable(String name)
   {
     Object variable = super.lookupVariable(name);
     if (variable != null) {
       return variable;
     }
     if (!this.argumentsAdded)
     {
       addArgumentsAsVariables();
       this.argumentsAdded = true;
     }
     variable = super.lookupVariable(name);
     if (variable != null) {
       return variable;
     }
     return null;
   }
   
   public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer)
   {
     this.parameterNameDiscoverer = parameterNameDiscoverer;
   }
   
   private void addArgumentsAsVariables()
   {
     Object[] args = this.mi.getArguments();
     if (args.length == 0) {
       return;
     }
     Object targetObject = this.mi.getThis();
     
     Class targetClass = AopProxyUtils.ultimateTargetClass(targetObject);
     if (targetClass == null) {
       targetClass = targetObject.getClass();
     }
     Method method = AopUtils.getMostSpecificMethod(this.mi.getMethod(), targetClass);
     String[] paramNames = this.parameterNameDiscoverer.getParameterNames(method);
     if (paramNames == null)
     {
      // logger.warn("Unable to resolve method parameter names for method: " + method + ". Debug symbol information is required if you are using parameter names in expressions.");
       
       return;
     }
     for (int i = 0; i < args.length; i++) {
       super.setVariable(paramNames[i], args[i]);
     }
   }

}

 
 
In CustomMethodSecurityExpressionHandler class in createSecurityExpressionRoot() the Custom
PermissionEvaluator and Custom PriviledgeEvaluator are being set in the CustomMethodSecurityExpressionRoot,
which performs the rest of the operations:
The Code for CustomMethodSecurityExpressionRoot is:
 
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;

public class CustomMethodSecurityExpressionRoot extends CustomSecurityExpressionRoot
  implements MethodSecurityExpressionOperations {

 private Object filterObject;
   private Object returnObject;
   private Object target;
   
   CustomMethodSecurityExpressionRoot(Authentication a)
   {
     super(a);
   }
   
   public void setFilterObject(Object filterObject)
   {
     this.filterObject = filterObject;
   }
   
   public Object getFilterObject()
   {
     return this.filterObject;
   }
   
   public void setReturnObject(Object returnObject)
   {
     this.returnObject = returnObject;
   }
   
   public Object getReturnObject()
   {
     return this.returnObject;
   }
   
   void setThis(Object target)
   {
     this.target = target;
   }
   
   public Object getThis()
   {
     return this.target;
   }

}

 
The code for CustomSecurityExpressionRoot is:
 

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;


public class CustomSecurityExpressionRoot implements
  CustomSecurityExpressionOperations {

  protected final Authentication authentication;
   private AuthenticationTrustResolver trustResolver;
   private RoleHierarchy roleHierarchy;
   private Set roles;
   public final boolean permitAll = true;
   public final boolean denyAll = false;
   private PermissionEvaluator permissionEvaluator;
   public final String read = "read";
   public final String write = "write";
   public final String create = "create";
   public final String delete = "delete";
   public final String admin = "administration";
   private PriviledgeEvaluator priviledgeEvaluator;
   
   public CustomSecurityExpressionRoot(Authentication authentication)
   {
     if (authentication == null) {
       throw new IllegalArgumentException("Authentication object cannot be null");
     }
     this.authentication = authentication;
   }
   
   public final boolean hasAuthority(String authority)
   {
     return hasRole(authority);
   }
   
   public final boolean hasAnyAuthority(String... authorities)
   {
     return hasAnyRole(authorities);
   }
   
   public final boolean hasRole(String role)
   {
     return getAuthoritySet().contains(role);
   }
   
   public final boolean hasAnyRole(String... roles)
   {
     Set roleSet = getAuthoritySet();
     for (String role : roles) {
       if (roleSet.contains(role)) {
         return true;
       }
     }
     return false;
   }
   
   public final Authentication getAuthentication()
   {
     return this.authentication;
   }
   
   public final boolean permitAll()
   {
     return true;
   }
   
   public final boolean denyAll()
   {
     return false;
   }
   
   public final boolean isAnonymous()
   {
     return this.trustResolver.isAnonymous(this.authentication);
   }
   
   public final boolean isAuthenticated()
   {
     return !isAnonymous();
   }
   
   public final boolean isRememberMe()
   {
     return this.trustResolver.isRememberMe(this.authentication);
   }
   
   public final boolean isFullyAuthenticated()
   {
     return (!this.trustResolver.isAnonymous(this.authentication)) && (!this.trustResolver.isRememberMe(this.authentication));
   }
   
   public Object getPrincipal()
   {
     return this.authentication.getPrincipal();
   }
   
   public void setTrustResolver(AuthenticationTrustResolver trustResolver)
   {
     this.trustResolver = trustResolver;
   }
   
   public void setRoleHierarchy(RoleHierarchy roleHierarchy)
   {
     this.roleHierarchy = roleHierarchy;
   }
   
   private Set getAuthoritySet()
   {
     if (this.roles == null)
     {
       this.roles = new HashSet();
       Collection userAuthorities = this.authentication.getAuthorities();
       if (this.roleHierarchy != null) {
         userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
       }
       this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
     }
     return this.roles;
   }
   
   public boolean hasPermission(Object target, Object permission)
   {
     return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
   }
   
   public boolean hasPermission(Object targetId, String targetType, Object permission)
   {
     return this.permissionEvaluator.hasPermission(this.authentication, (Serializable)targetId, targetType, permission);
   }
   
   public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator)
   {
     this.permissionEvaluator = permissionEvaluator;
   }
   
   
   

 public void setPriviledgeEvaluator(PriviledgeEvaluator priviledgeEvaluator) {
  this.priviledgeEvaluator = priviledgeEvaluator;
 }

 @Override
 public boolean hasPriviledge(Object paramObject1, Object paramObject2) {
  // TODO Auto-generated method stubn 
  System.out.println(" ************* In Has priviledge 1");
  return this.priviledgeEvaluator.hasPriviledge(this.authentication, paramObject1, paramObject2);
  
  
 }

 @Override
 public boolean hasPriviledge(Object paramObject1, String paramString,
   Object paramObject2) {
  System.out.println(" ************** In Has priviledge 2");
  return this.priviledgeEvaluator.hasPriviledge(this.authentication, (Serializable)paramObject1, paramString, paramObject2);
  // TODO Auto-generated method stub
  
 }

}

 
 
& the code for CustomSecurityExpressionOperations is:

import org.springframework.security.access.expression.SecurityExpressionOperations;

public interface CustomSecurityExpressionOperations extends SecurityExpressionOperations {
 
  
   public abstract boolean hasPriviledge(Object paramObject1, Object paramObject2);
   
   public abstract boolean hasPriviledge(Object paramObject1, String paramString, Object paramObject2);
   

}

 
 

This is in short the Method level Security Implementation Architecture in Spring.
Any Queries and Suggestions are always welcome.
& Happy Coding till Next Time.

Comments

Popular posts from this blog

Use of @Configurable annotation.

Spring WS - Part 5

Spring WS - Part 4