Better UserGuard hasAccess Implementation
This commit is contained in:
@@ -7,7 +7,7 @@ import br.com.hideyoshi.auth.security.oauth2.repository.OAuthRequestRepository;
|
|||||||
import br.com.hideyoshi.auth.security.service.AuthService;
|
import br.com.hideyoshi.auth.security.service.AuthService;
|
||||||
import br.com.hideyoshi.auth.service.UserService;
|
import br.com.hideyoshi.auth.service.UserService;
|
||||||
import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidExceptionDetails;
|
import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidExceptionDetails;
|
||||||
import br.com.hideyoshi.auth.util.guard.UserResourceHandler;
|
import br.com.hideyoshi.auth.util.guard.UserResourceEndpointManager;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
@@ -44,7 +44,7 @@ public class SecurityConfig {
|
|||||||
private final AuthService authService;
|
private final AuthService authService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final OAuthRequestRepository oAuthRequestRepository;
|
private final OAuthRequestRepository oAuthRequestRepository;
|
||||||
private final UserResourceHandler userResourceHandler;
|
private final UserResourceEndpointManager userResourceEndpointManager;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthenticationProvider authenticationProvider() {
|
public AuthenticationProvider authenticationProvider() {
|
||||||
@@ -86,11 +86,11 @@ public class SecurityConfig {
|
|||||||
UsernamePasswordAuthenticationFilter.class
|
UsernamePasswordAuthenticationFilter.class
|
||||||
);
|
);
|
||||||
|
|
||||||
for (String endpoint : this.userResourceHandler.getOpenPaths()) {
|
for (String endpoint : this.userResourceEndpointManager.getOpenPaths()) {
|
||||||
http.authorizeRequests().antMatchers(endpoint).permitAll();
|
http.authorizeRequests().antMatchers(endpoint).permitAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String endpoint : this.userResourceHandler.getGuardedPaths()) {
|
for (String endpoint : this.userResourceEndpointManager.getGuardedPaths()) {
|
||||||
http.authorizeRequests().antMatchers(endpoint).hasAnyAuthority("ROLE_USER", "ROLE_ADMIN");
|
http.authorizeRequests().antMatchers(endpoint).hasAnyAuthority("ROLE_USER", "ROLE_ADMIN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package br.com.hideyoshi.auth.security.interceptor;
|
package br.com.hideyoshi.auth.security.interceptor;
|
||||||
|
|
||||||
import br.com.hideyoshi.auth.service.UserService;
|
|
||||||
import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
|
|
||||||
import br.com.hideyoshi.auth.util.exception.AuthorizationException;
|
import br.com.hideyoshi.auth.util.exception.AuthorizationException;
|
||||||
import br.com.hideyoshi.auth.util.guard.UserResourceGuard;
|
import br.com.hideyoshi.auth.util.guard.UserResourceGuard;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import br.com.hideyoshi.auth.util.guard.UserResourceHandler;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -20,7 +18,7 @@ import java.util.Objects;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserResourceAccessInterceptor implements HandlerInterceptor {
|
public class UserResourceAccessInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserResourceHandler userResourceHandler;
|
||||||
|
|
||||||
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
|
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
|
||||||
|
|
||||||
@@ -32,8 +30,10 @@ public class UserResourceAccessInterceptor implements HandlerInterceptor {
|
|||||||
.getMethodAnnotation(UserResourceGuard.class);
|
.getMethodAnnotation(UserResourceGuard.class);
|
||||||
|
|
||||||
if (Objects.nonNull(annotation)) {
|
if (Objects.nonNull(annotation)) {
|
||||||
Boolean accessPermission =
|
Boolean accessPermission = this.userResourceHandler.hasAccess(
|
||||||
annotation.accessType().hasAccess(this.userService, request);
|
annotation.accessType(), request
|
||||||
|
);
|
||||||
|
|
||||||
if (!accessPermission) {
|
if (!accessPermission) {
|
||||||
throw new AuthorizationException(annotation.denialMessage());
|
throw new AuthorizationException(annotation.denialMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package br.com.hideyoshi.auth.util.guard;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserResourceEndpointManager {
|
||||||
|
private final ListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
public List<String> getGuardedPaths() {
|
||||||
|
return this.extractPathsFromMethods(this.getGuardedResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOpenPaths() {
|
||||||
|
return this.extractPathsFromMethods(this.getOpenResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> extractPathsFromMethods(List<Method> methods) {
|
||||||
|
final List<String> paths = new ArrayList<>();
|
||||||
|
for (final Method method : methods) {
|
||||||
|
String[] parentPath = new String[0];
|
||||||
|
|
||||||
|
RequestMapping classAnnotation = method.getDeclaringClass().getAnnotation(RequestMapping.class);
|
||||||
|
if (classAnnotation != null) {
|
||||||
|
parentPath = this.getPathFromAnnotation(classAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Annotation> annotations = List.of(method.getAnnotations());
|
||||||
|
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
final String[] path = this.getPathFromAnnotation(annotation);
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
paths.add(String.join("/", parentPath) + String.join("/", path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Method> getGuardedResources() {
|
||||||
|
final List<UserResourceGuardEnum> guardedAccessTypes = Arrays.asList(
|
||||||
|
UserResourceGuardEnum.USER,
|
||||||
|
UserResourceGuardEnum.SAME_USER,
|
||||||
|
UserResourceGuardEnum.ADMIN_USER
|
||||||
|
);
|
||||||
|
final List<Method> methods = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final Class<?> controllerClass : this.getControllerClasses()) {
|
||||||
|
methods.addAll(this.getMethodsByAccessType(controllerClass, guardedAccessTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Method> getOpenResources() {
|
||||||
|
final List<UserResourceGuardEnum> openAccessTypes = List.of(UserResourceGuardEnum.OPEN);
|
||||||
|
final List<Method> methods = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final Class<?> controllerClass : this.getControllerClasses()) {
|
||||||
|
methods.addAll(this.getMethodsByAccessType(controllerClass, openAccessTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Method> getMethodsByAccessType(final Class<?> controllerClass, List<UserResourceGuardEnum> accessTypes) {
|
||||||
|
final List<Method> methods = new ArrayList<>();
|
||||||
|
for (final Method method : controllerClass.getDeclaredMethods()) {
|
||||||
|
if (!method.isAnnotationPresent(UserResourceGuard.class)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UserResourceGuard annotation = method.getAnnotation(UserResourceGuard.class);
|
||||||
|
if (!accessTypes.contains(annotation.accessType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.add(method);
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Class<?>> getControllerClasses() {
|
||||||
|
final List<Class<?>> controllerClasses = new ArrayList<>();
|
||||||
|
for (final String beanName : this.beanFactory.getBeanNamesForAnnotation(Controller.class)) {
|
||||||
|
controllerClasses.add(this.beanFactory.getType(beanName));
|
||||||
|
}
|
||||||
|
return controllerClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getPathFromAnnotation(Annotation annotation) {
|
||||||
|
String[] path; String[] value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
value = (String[]) annotation.annotationType().getMethod("value").invoke(annotation);
|
||||||
|
path = (String[]) annotation.annotationType().getMethod("path").invoke(annotation);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.length > 0 ? value : path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,41 +15,13 @@ import java.util.HashMap;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum UserResourceGuardEnum {
|
public enum UserResourceGuardEnum {
|
||||||
|
|
||||||
USER("user") {
|
USER("user"),
|
||||||
@Override
|
|
||||||
public Boolean hasAccess(
|
|
||||||
UserService userService,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
return UserResourceGuardEnum.justUser(userService, request);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
SAME_USER("same_user") {
|
SAME_USER("same_user"),
|
||||||
@Override
|
|
||||||
public Boolean hasAccess(
|
|
||||||
UserService userService,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
return UserResourceGuardEnum.sameUser(userService, request);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ADMIN_USER("admin_user") {
|
ADMIN_USER("admin_user"),
|
||||||
@Override
|
|
||||||
public Boolean hasAccess(
|
|
||||||
UserService userService,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
return UserResourceGuardEnum.adminUser(userService, request);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
OPEN("open") {
|
OPEN("open");
|
||||||
@Override
|
|
||||||
public Boolean hasAccess(
|
|
||||||
UserService userService,
|
|
||||||
HttpServletRequest request) {
|
|
||||||
return openAccess(userService, request);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final String accessType;
|
private final String accessType;
|
||||||
|
|
||||||
@@ -66,36 +38,4 @@ public enum UserResourceGuardEnum {
|
|||||||
throw new IllegalArgumentException("Argument not valid.");
|
throw new IllegalArgumentException("Argument not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean justUser(UserService userService, HttpServletRequest request) {
|
|
||||||
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
|
||||||
|
|
||||||
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.USER.getDescription()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean sameUser(UserService userService, HttpServletRequest request) {
|
|
||||||
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
|
||||||
|
|
||||||
Object requestPathVariable = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
|
||||||
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
HashMap<String, String> pathVariable = objectMapper.convertValue(requestPathVariable, HashMap.class);
|
|
||||||
UserDTO userInfo = userService.getUser(Long.parseLong(pathVariable.get("id")));
|
|
||||||
|
|
||||||
return userLogged.getUsername().equals(userInfo.getUsername());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean adminUser(UserService userService, HttpServletRequest request) {
|
|
||||||
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
|
||||||
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.getDescription()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Boolean openAccess(UserService userService, HttpServletRequest request) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Boolean hasAccess(
|
|
||||||
UserService userService,
|
|
||||||
HttpServletRequest request);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +1,63 @@
|
|||||||
package br.com.hideyoshi.auth.util.guard;
|
package br.com.hideyoshi.auth.util.guard;
|
||||||
|
|
||||||
|
|
||||||
|
import br.com.hideyoshi.auth.enums.Role;
|
||||||
|
import br.com.hideyoshi.auth.model.UserDTO;
|
||||||
|
import br.com.hideyoshi.auth.service.UserService;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserResourceHandler {
|
public class UserResourceHandler {
|
||||||
private final ListableBeanFactory beanFactory;
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public List<String> getGuardedPaths() {
|
private final UserService userService;
|
||||||
return this.extractPathsFromMethods(this.getGuardedResources());
|
|
||||||
|
public Boolean hasAccess(UserResourceGuardEnum userResourceGuardEnum, HttpServletRequest request) {
|
||||||
|
return switch (userResourceGuardEnum) {
|
||||||
|
case USER -> justUser();
|
||||||
|
case SAME_USER -> sameUser(request);
|
||||||
|
case ADMIN_USER -> adminUser();
|
||||||
|
case OPEN -> openAccess();
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getOpenPaths() {
|
private boolean justUser() {
|
||||||
return this.extractPathsFromMethods(this.getOpenResources());
|
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
|
||||||
|
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.USER.getDescription()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> extractPathsFromMethods(List<Method> methods) {
|
private boolean sameUser(HttpServletRequest request) {
|
||||||
final List<String> paths = new ArrayList<>();
|
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
for (final Method method : methods) {
|
|
||||||
String[] parentPath = new String[0];
|
|
||||||
|
|
||||||
RequestMapping classAnnotation = method.getDeclaringClass().getAnnotation(RequestMapping.class);
|
Object requestPathVariable = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||||
if (classAnnotation != null) {
|
HashMap<String, String> pathVariable = this.objectMapper.convertValue(
|
||||||
parentPath = this.getPathFromAnnotation(classAnnotation);
|
requestPathVariable, new TypeReference<>() {}
|
||||||
}
|
|
||||||
|
|
||||||
List<Annotation> annotations = List.of(method.getAnnotations());
|
|
||||||
|
|
||||||
for (Annotation annotation : annotations) {
|
|
||||||
final String[] path = this.getPathFromAnnotation(annotation);
|
|
||||||
|
|
||||||
if (path != null)
|
|
||||||
paths.add(String.join("/", parentPath) + String.join("/", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Method> getGuardedResources() {
|
|
||||||
final List<UserResourceGuardEnum> guardedAccessTypes = Arrays.asList(
|
|
||||||
UserResourceGuardEnum.USER,
|
|
||||||
UserResourceGuardEnum.SAME_USER,
|
|
||||||
UserResourceGuardEnum.ADMIN_USER
|
|
||||||
);
|
);
|
||||||
final List<Method> methods = new ArrayList<>();
|
|
||||||
|
|
||||||
for (final Class<?> controllerClass : this.getControllerClasses()) {
|
UserDTO userInfo = this.userService.getUser(Long.parseLong(pathVariable.get("id")));
|
||||||
methods.addAll(this.getMethodsByAccessType(controllerClass, guardedAccessTypes));
|
|
||||||
}
|
return userLogged.getUsername().equals(userInfo.getUsername());
|
||||||
|
|
||||||
return methods;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Method> getOpenResources() {
|
private boolean adminUser() {
|
||||||
final List<UserResourceGuardEnum> openAccessTypes = List.of(UserResourceGuardEnum.OPEN);
|
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
final List<Method> methods = new ArrayList<>();
|
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.getDescription()));
|
||||||
|
|
||||||
for (final Class<?> controllerClass : this.getControllerClasses()) {
|
|
||||||
methods.addAll(this.getMethodsByAccessType(controllerClass, openAccessTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
return methods;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Method> getMethodsByAccessType(final Class<?> controllerClass, List<UserResourceGuardEnum> accessTypes) {
|
private Boolean openAccess() {
|
||||||
final List<Method> methods = new ArrayList<>();
|
return true;
|
||||||
for (final Method method : controllerClass.getDeclaredMethods()) {
|
|
||||||
if (!method.isAnnotationPresent(UserResourceGuard.class)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
UserResourceGuard annotation = method.getAnnotation(UserResourceGuard.class);
|
|
||||||
if (!accessTypes.contains(annotation.accessType())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
methods.add(method);
|
|
||||||
}
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Class<?>> getControllerClasses() {
|
|
||||||
final List<Class<?>> controllerClasses = new ArrayList<>();
|
|
||||||
for (final String beanName : this.beanFactory.getBeanNamesForAnnotation(Controller.class)) {
|
|
||||||
controllerClasses.add(this.beanFactory.getType(beanName));
|
|
||||||
}
|
|
||||||
return controllerClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getPathFromAnnotation(Annotation annotation) {
|
|
||||||
String[] path; String[] value;
|
|
||||||
|
|
||||||
try {
|
|
||||||
value = (String[]) annotation.annotationType().getMethod("value").invoke(annotation);
|
|
||||||
path = (String[]) annotation.annotationType().getMethod("path").invoke(annotation);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.length > 0 ? value : path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user