View Javadoc

1   package net.ramapuram.thomas.service;
2   
3   import org.apache.commons.collections.CollectionUtils;
4   import org.apache.commons.logging.Log;
5   import org.apache.commons.logging.LogFactory;
6   import net.ramapuram.thomas.Constants;
7   import net.ramapuram.thomas.model.Role;
8   import net.ramapuram.thomas.model.User;
9   import org.springframework.aop.AfterReturningAdvice;
10  import org.springframework.aop.MethodBeforeAdvice;
11  import org.springframework.security.access.AccessDeniedException;
12  import org.springframework.security.authentication.AuthenticationTrustResolver;
13  import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
14  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
15  import org.springframework.security.core.Authentication;
16  import org.springframework.security.core.GrantedAuthority;
17  import org.springframework.security.core.context.SecurityContext;
18  import org.springframework.security.core.context.SecurityContextHolder;
19  import org.springframework.security.core.userdetails.UserDetails;
20  
21  import java.lang.reflect.Method;
22  import java.util.Collection;
23  import java.util.HashSet;
24  import java.util.Set;
25  
26  /**
27   * This advice is responsible for enforcing security and only allowing administrators
28   * to modify users. Users are allowed to modify themselves.
29   *
30   * @author mraible
31   */
32  public class UserSecurityAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
33      /**
34       * Default "Access Denied" error message (not i18n-ized).
35       */
36      public static final String ACCESS_DENIED = "Access Denied: Only administrators are allowed to modify other users.";
37      private final Log log = LogFactory.getLog(UserSecurityAdvice.class);
38  
39      /**
40       * Method to enforce security and only allow administrators to modify users. Regular
41       * users are allowed to modify themselves.
42       *
43       * @param method the name of the method executed
44       * @param args the arguments to the method
45       * @param target the target class
46       * @throws Throwable thrown when args[0] is null or not a User object
47       */
48      public void before(Method method, Object[] args, Object target) throws Throwable {
49          SecurityContext ctx = SecurityContextHolder.getContext();
50  
51          if (ctx.getAuthentication() != null) {
52              Authentication auth = ctx.getAuthentication();
53              boolean administrator = false;
54              Collection<GrantedAuthority> roles = auth.getAuthorities();
55              for (GrantedAuthority role1 : roles) {
56                  if (role1.getAuthority().equals(Constants.ADMIN_ROLE)) {
57                      administrator = true;
58                      break;
59                  }
60              }
61  
62              User user = (User) args[0];
63  
64              AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
65              // allow new users to signup - this is OK b/c Signup doesn't allow setting of roles
66              boolean signupUser = resolver.isAnonymous(auth);
67  
68              if (!signupUser) {
69                  User currentUser = getCurrentUser(auth);
70  
71                  if (user.getId() != null && !user.getId().equals(currentUser.getId()) && !administrator) {
72                      log.warn("Access Denied: '" + currentUser.getUsername() + "' tried to modify '" + user.getUsername() + "'!");
73                      throw new AccessDeniedException(ACCESS_DENIED);
74                  } else if (user.getId() != null && user.getId().equals(currentUser.getId()) && !administrator) {
75                      // get the list of roles the user is trying add
76                      Set<String> userRoles = new HashSet<String>();
77                      if (user.getRoles() != null) {
78                          for (Object o : user.getRoles()) {
79                              Role role = (Role) o;
80                              userRoles.add(role.getName());
81                          }
82                      }
83  
84                      // get the list of roles the user currently has
85                      Set<String> authorizedRoles = new HashSet<String>();
86                      for (GrantedAuthority role : roles) {
87                          authorizedRoles.add(role.getAuthority());
88                      }
89  
90                      // if they don't match - access denied
91                      // regular users aren't allowed to change their roles
92                      if (!CollectionUtils.isEqualCollection(userRoles, authorizedRoles)) {
93                          log.warn("Access Denied: '" + currentUser.getUsername() + "' tried to change their role(s)!");
94                          throw new AccessDeniedException(ACCESS_DENIED);
95                      }
96                  }
97              } else {
98                  if (log.isDebugEnabled()) {
99                      log.debug("Registering new user '" + user.getUsername() + "'");
100                 }
101             }
102         }
103     }
104 
105     /**
106      * After returning, grab the user, check if they've been modified and reset the SecurityContext if they have.
107      * @param returnValue the user object
108      * @param method the name of the method executed
109      * @param args the arguments to the method
110      * @param target the target class
111      * @throws Throwable thrown when args[0] is null or not a User object
112      */
113     public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
114     throws Throwable {
115         User user = (User) args[0];
116 
117         if (user.getVersion() != null) {
118             // reset the authentication object if current user
119             Authentication auth = SecurityContextHolder.getContext().getAuthentication();
120             AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
121             // allow new users to signup - this is OK b/c Signup doesn't allow setting of roles
122             boolean signupUser = resolver.isAnonymous(auth);
123             if (auth != null && !signupUser) {
124                 User currentUser = getCurrentUser(auth);
125                 if (currentUser.getId().equals(user.getId())) {
126                     auth = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
127                     SecurityContextHolder.getContext().setAuthentication(auth);
128                 }
129             }
130         }
131     }
132 
133     private User getCurrentUser(Authentication auth) {
134         User currentUser;
135         if (auth.getPrincipal() instanceof UserDetails) {
136             currentUser = (User) auth.getPrincipal();
137         } else if (auth.getDetails() instanceof UserDetails) {
138             currentUser = (User) auth.getDetails();
139         } else {
140             throw new AccessDeniedException("User not properly authenticated.");
141         }
142         return currentUser;
143     }
144 }