View Javadoc

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