Refactor and restructure - No API Breakage (#49)

* Renames Project GroupId

* Correcting Naming Conventions

* Initial Restructuring

* Altera Formato Application Config

* Removes Auth for ReImplementation

* Changes Project Structure

* Restructure Microservice

* Fixes Tests

* Initial Authentication and Authorization ReImplementation

* Optimize Imports and Reformats Code

* Implements ResourceGuard Mapping and Automatic Security Configuration

* First Refactored Feature Complete Version - OAuth2 Implementation

* Moves All Enums to the Correct Dir

* Reimplements Authentication with Providers and JWTFilters
This commit is contained in:
2024-05-25 21:48:21 -03:00
committed by GitHub
parent 0ecc1f3127
commit ffc35c2f5e
68 changed files with 713 additions and 753 deletions

View File

@@ -8,7 +8,7 @@
<version>2.7.17</version> <version>2.7.17</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.hideyoshi</groupId> <groupId>br.com.hideyoshi</groupId>
<artifactId>auth-api</artifactId> <artifactId>auth-api</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>auth-api</name> <name>auth-api</name>
@@ -76,6 +76,13 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- support for "Java 8 date time api" -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

View File

@@ -0,0 +1,13 @@
package br.com.hideyoshi.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AuthServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
}

View File

@@ -1,10 +1,10 @@
package com.hideyoshi.auth.base.config; package br.com.hideyoshi.auth.config;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Provider;
import com.hideyoshi.auth.base.auth.entity.Role; import br.com.hideyoshi.auth.enums.Role;
import com.hideyoshi.auth.base.auth.model.UserDTO; import br.com.hideyoshi.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.repo.UserRepository; import br.com.hideyoshi.auth.repository.UserRepository;
import com.hideyoshi.auth.base.auth.service.UserService; import br.com.hideyoshi.auth.service.UserService;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -15,16 +15,16 @@ import java.util.ArrayList;
@Configuration @Configuration
public class DefaultUserConfig { public class DefaultUserConfig {
@Value("${com.hideyoshi.defaultUser.fullName}") @Value("${br.com.hideyoshi.defaultUser.fullName}")
private String ADMIN_NAME; private String ADMIN_NAME;
@Value("${com.hideyoshi.defaultUser.email}") @Value("${br.com.hideyoshi.defaultUser.email}")
private String ADMIN_EMAIL; private String ADMIN_EMAIL;
@Value("${com.hideyoshi.defaultUser.username}") @Value("${br.com.hideyoshi.defaultUser.username}")
private String ADMIN_USERNAME; private String ADMIN_USERNAME;
@Value("${com.hideyoshi.defaultUser.password}") @Value("${br.com.hideyoshi.defaultUser.password}")
private String ADMIN_PASSWORD; private String ADMIN_PASSWORD;
@Bean @Bean
@@ -53,4 +53,4 @@ public class DefaultUserConfig {
}; };
} }
} }

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.microservice.storageService.config; package br.com.hideyoshi.auth.config;
import lombok.Getter; import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -8,6 +8,6 @@ import org.springframework.context.annotation.Configuration;
@Getter @Getter
@Configuration @Configuration
public class StorageServiceConfig { public class StorageServiceConfig {
@Value("${com.hideyoshi.microservice.storageServicePath}") @Value("${br.com.hideyoshi.microservice.storageServicePath}")
private String fileServicePath; private String fileServicePath;
} }

View File

@@ -1,23 +1,18 @@
package com.hideyoshi.auth.healthChecker.api; package br.com.hideyoshi.auth.controller;
import com.hideyoshi.auth.util.guard.UserResourceGuard; import br.com.hideyoshi.auth.util.guard.UserResourceGuard;
import com.hideyoshi.auth.util.guard.UserResourceGuardEnum; import br.com.hideyoshi.auth.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Log4j2 @Log4j2
@Controller @Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/health")
public class HealthCheckerController { public class HealthCheckerController {
@RequestMapping @RequestMapping("/health")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<String> healthCheck() { public ResponseEntity<String> healthCheck() {
log.info("Health check requested"); log.info("Health check requested");

View File

@@ -1,7 +1,9 @@
package com.hideyoshi.auth.base.session.api; package br.com.hideyoshi.auth.controller;
import com.hideyoshi.auth.base.auth.model.AuthDTO; import br.com.hideyoshi.auth.model.UserAuthDTO;
import com.hideyoshi.auth.base.session.service.SessionManagerService; import br.com.hideyoshi.auth.service.SessionManagerService;
import br.com.hideyoshi.auth.util.guard.UserResourceGuard;
import br.com.hideyoshi.auth.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -16,17 +18,19 @@ import javax.servlet.http.HttpSession;
@Controller @Controller
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping(path = "/session") @RequestMapping("/session")
public class SessionController { public class SessionController {
private final SessionManagerService sessionManagerService; private final SessionManagerService sessionManagerService;
@GetMapping(path = "/validate") @GetMapping("/validate")
public ResponseEntity<AuthDTO> validateCurrentSession(HttpSession session) { @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<UserAuthDTO> validateCurrentSession(HttpSession session) {
return ResponseEntity.ok(this.sessionManagerService.validateSession(session)); return ResponseEntity.ok(this.sessionManagerService.validateSession(session));
} }
@DeleteMapping(path = "/destroy") @DeleteMapping("/destroy")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<Void> destroyCurrentSession(HttpSession session) { public ResponseEntity<Void> destroyCurrentSession(HttpSession session) {
this.sessionManagerService.destroySession(session); this.sessionManagerService.destroySession(session);
return new ResponseEntity<>(HttpStatus.NO_CONTENT); return new ResponseEntity<>(HttpStatus.NO_CONTENT);

View File

@@ -1,16 +1,17 @@
package com.hideyoshi.auth.base.auth.api; package br.com.hideyoshi.auth.controller;
import com.hideyoshi.auth.base.auth.model.AuthDTO; import br.com.hideyoshi.auth.enums.FileTypeEnum;
import com.hideyoshi.auth.base.auth.service.AuthService; import br.com.hideyoshi.auth.model.UserAuthDTO;
import com.hideyoshi.auth.base.auth.model.TokenDTO; import br.com.hideyoshi.auth.model.TokenDTO;
import com.hideyoshi.auth.base.auth.model.UserDTO; import br.com.hideyoshi.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.service.UserService; import br.com.hideyoshi.auth.model.microservice.StorageServiceUploadResponse;
import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum; import br.com.hideyoshi.auth.security.service.AuthService;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse; import br.com.hideyoshi.auth.service.UserService;
import com.hideyoshi.auth.microservice.storageService.service.StorageService; import br.com.hideyoshi.auth.service.microservice.StorageService;
import com.hideyoshi.auth.util.exception.BadRequestException; import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import com.hideyoshi.auth.util.guard.UserResourceGuard; import br.com.hideyoshi.auth.util.exception.BadRequestException;
import com.hideyoshi.auth.util.guard.UserResourceGuardEnum; import br.com.hideyoshi.auth.util.guard.UserResourceGuard;
import br.com.hideyoshi.auth.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -20,13 +21,14 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
@Log4j2 @Log4j2
@Controller @Controller
@RestController
@RequestMapping("/user") @RequestMapping("/user")
@RequiredArgsConstructor @RequiredArgsConstructor
public class UserController { public class UserController {
@@ -45,7 +47,7 @@ public class UserController {
@PostMapping("/signup") @PostMapping("/signup")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<AuthDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) { public ResponseEntity<UserAuthDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) {
URI uri = URI.create( URI uri = URI.create(
ServletUriComponentsBuilder ServletUriComponentsBuilder
.fromCurrentContextPath() .fromCurrentContextPath()
@@ -56,7 +58,7 @@ public class UserController {
@PostMapping("/login/refresh") @PostMapping("/login/refresh")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<AuthDTO> refreshAccessToken( public ResponseEntity<UserAuthDTO> refreshAccessToken(
@RequestBody @Valid TokenDTO refreshToken, @RequestBody @Valid TokenDTO refreshToken,
HttpServletRequest request) { HttpServletRequest request) {
return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request)); return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request));
@@ -64,7 +66,7 @@ public class UserController {
@PostMapping("/login/validate") @PostMapping("/login/validate")
@UserResourceGuard(accessType = UserResourceGuardEnum.USER) @UserResourceGuard(accessType = UserResourceGuardEnum.USER)
public ResponseEntity<AuthDTO> validateAccessToken(HttpServletRequest request) { public ResponseEntity<UserAuthDTO> validateAccessToken(HttpServletRequest request) {
return ResponseEntity.ok(this.authService.validateAccessToken(request)); return ResponseEntity.ok(this.authService.validateAccessToken(request));
} }

View File

@@ -1,5 +1,6 @@
package com.hideyoshi.auth.base.auth.entity; package br.com.hideyoshi.auth.entity;
import br.com.hideyoshi.auth.enums.Role;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;

View File

@@ -1,7 +1,7 @@
package com.hideyoshi.auth.microservice.storageService.enums; package br.com.hideyoshi.auth.enums;
import br.com.hideyoshi.auth.util.exception.BadRequestException;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.Getter; import lombok.Getter;
@Getter @Getter

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.microservice.storageService.enums; package br.com.hideyoshi.auth.enums;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.entity; package br.com.hideyoshi.auth.enums;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.entity; package br.com.hideyoshi.auth.enums;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.model; package br.com.hideyoshi.auth.model;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.model; package br.com.hideyoshi.auth.model;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

View File

@@ -1,9 +1,9 @@
package com.hideyoshi.auth.base.auth.model; package br.com.hideyoshi.auth.model;
import br.com.hideyoshi.auth.enums.Provider;
import br.com.hideyoshi.auth.enums.Role;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.auth.base.auth.entity.Role;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@@ -16,7 +16,7 @@ import java.util.List;
@AllArgsConstructor @AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class AuthDTO implements Serializable { public class UserAuthDTO implements Serializable {
private Long id; private Long id;
@@ -36,7 +36,7 @@ public class AuthDTO implements Serializable {
private TokenDTO refreshToken; private TokenDTO refreshToken;
public AuthDTO(UserDTO user, TokenDTO accessToken, TokenDTO refreshToken) { public UserAuthDTO(UserDTO user, TokenDTO accessToken, TokenDTO refreshToken) {
this.id = user.getId(); this.id = user.getId();
this.name = user.getName(); this.name = user.getName();
this.email = user.getEmail(); this.email = user.getEmail();

View File

@@ -1,14 +1,14 @@
package com.hideyoshi.auth.base.auth.model; package br.com.hideyoshi.auth.model;
import br.com.hideyoshi.auth.enums.Provider;
import br.com.hideyoshi.auth.enums.Role;
import br.com.hideyoshi.auth.entity.User;
import br.com.hideyoshi.auth.util.validator.email.unique.UniqueEmail;
import br.com.hideyoshi.auth.util.validator.email.valid.ValidEmail;
import br.com.hideyoshi.auth.util.validator.password.ValidPassword;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.auth.base.auth.entity.User;
import com.hideyoshi.auth.util.validator.email.unique.UniqueEmail;
import com.hideyoshi.auth.util.validator.email.valid.ValidEmail;
import com.hideyoshi.auth.util.validator.password.ValidPassword;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.microservice.storageService.model; package br.com.hideyoshi.auth.model.microservice;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.microservice.storageService.model; package br.com.hideyoshi.auth.model.microservice;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.base.auth.repo; package br.com.hideyoshi.auth.repository;
import com.hideyoshi.auth.base.auth.entity.User; import br.com.hideyoshi.auth.entity.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.config; package br.com.hideyoshi.auth.security.config;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -14,7 +14,7 @@ import java.util.List;
@Configuration @Configuration
public class CorsConfig { public class CorsConfig {
@Value("${com.hideyoshi.frontendPath}") @Value("${br.com.hideyoshi.frontendPath}")
private String FRONTEND_PATH; private String FRONTEND_PATH;
@Bean @Bean
@@ -35,4 +35,4 @@ public class CorsConfig {
return source; return source;
} }
} }

View File

@@ -0,0 +1,16 @@
package br.com.hideyoshi.auth.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfig {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,153 @@
package br.com.hideyoshi.auth.security.config;
import br.com.hideyoshi.auth.model.UserAuthDTO;
import br.com.hideyoshi.auth.model.UserDTO;
import br.com.hideyoshi.auth.security.filter.JWTAuthenticationFilter;
import br.com.hideyoshi.auth.security.oauth2.repository.OAuthRequestRepository;
import br.com.hideyoshi.auth.security.service.AuthService;
import br.com.hideyoshi.auth.service.UserService;
import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidExceptionDetails;
import br.com.hideyoshi.auth.util.guard.UserResourceHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final PasswordEncoder passwordEncoder;
private final AuthService authService;
private final UserService userService;
private final OAuthRequestRepository oAuthRequestRepository;
private final UserResourceHandler userResourceHandler;
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(this.userService);
provider.setPasswordEncoder(this.passwordEncoder);
return provider;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.cors().and().csrf().disable();
this.addEndpointSecurityToHttp(http);
this.addOAuthSecurityToHttp(http);
this.configureEndpoints(http);
return http.build();
}
private void configureEndpoints(HttpSecurity http) throws Exception {
for (String endpoint : this.userResourceHandler.getOpenPaths()) {
http.authorizeRequests().antMatchers(endpoint).permitAll();
}
for (String endpoint : this.userResourceHandler.getGuardedPaths()) {
http.authorizeRequests().antMatchers(endpoint).hasAnyAuthority("ROLE_USER", "ROLE_ADMIN");
}
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http.addFilterBefore(
new JWTAuthenticationFilter(this.authService),
UsernamePasswordAuthenticationFilter.class
);
}
private void addEndpointSecurityToHttp(HttpSecurity http) throws Exception {
http.formLogin(form -> form
.loginProcessingUrl("/user/login")
.successHandler(this::successFormHandler)
.failureHandler(this::failureHandler)
);
http.authorizeRequests().antMatchers("/login").denyAll();
}
private void addOAuthSecurityToHttp(HttpSecurity http) throws Exception {
http.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(this.oAuthRequestRepository)
.and().successHandler(this::successOAuth2Handler)
.failureHandler(this::failureHandler);
}
private void successFormHandler(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
UserAuthDTO authUser = this.authService.loginUser(
request,
(UserDTO) authentication.getPrincipal()
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authUser);
}
private void successOAuth2Handler(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
UserAuthDTO authUser = this.authService.loginOAuthUser(
request,
(OAuth2User) authentication.getPrincipal()
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authUser);
}
private void failureHandler(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException {
response.setHeader("error", e.getMessage());
response.setStatus(FORBIDDEN.value());
AuthenticationInvalidExceptionDetails error = new AuthenticationInvalidExceptionDetails("Authentication Failed. Check your credentials.",
HttpStatus.FORBIDDEN.value(), e.getMessage(),
e.getClass().getName(), LocalDateTime.now());
response.setContentType(APPLICATION_JSON_VALUE);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.writeValue(response.getOutputStream(), error);
}
}

View File

@@ -0,0 +1,58 @@
package br.com.hideyoshi.auth.security.filter;
import br.com.hideyoshi.auth.security.service.AuthService;
import br.com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
public class JWTAuthenticationFilter extends OncePerRequestFilter {
private static final String AUTHORIZATION_TYPE_STRING = "Bearer ";
private final AuthService authService;
public JWTAuthenticationFilter(AuthService authService) {
this.authService = authService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
this.setUserContext(request);
} catch (Exception ignored) {
// ignored
}
filterChain.doFilter(request, response);
}
private void setUserContext(HttpServletRequest request) {
if (SecurityContextHolder.getContext().getAuthentication() != null) {
return;
}
String authorizationHeader = request.getHeader(AUTHORIZATION);
Authentication loggedUserInfo = this.validateUserAccess(authorizationHeader);
SecurityContextHolder.getContext().setAuthentication(loggedUserInfo);
}
private Authentication validateUserAccess(String authorizationHeader) {
if (Objects.nonNull(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) {
String accessToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length());
return this.authService.extractAccessTokenInfo(accessToken);
} else {
throw new AuthenticationInvalidException("Access denied");
}
}
}

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.interceptor; package br.com.hideyoshi.auth.security.interceptor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -7,12 +7,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class ConfigInterceptor implements WebMvcConfigurer { public class InterceptorConfigurer implements WebMvcConfigurer {
private final UserResourceAccessInterceptor userResourceAccessInterceptor; private final UserResourceAccessInterceptor userResourceAccessInterceptor;
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userResourceAccessInterceptor); registry.addInterceptor(this.userResourceAccessInterceptor);
} }
} }

View File

@@ -1,9 +1,10 @@
package com.hideyoshi.auth.base.auth.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.guard.UserResourceGuard;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.base.auth.service.UserService;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import com.hideyoshi.auth.util.guard.UserResourceGuard;
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;
@@ -21,8 +22,6 @@ public class UserResourceAccessInterceptor implements HandlerInterceptor {
private final UserService userService; private final UserService userService;
private final ObjectMapper objectMapper;
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
if (!(handler instanceof HandlerMethod)) { if (!(handler instanceof HandlerMethod)) {
@@ -34,12 +33,12 @@ public class UserResourceAccessInterceptor implements HandlerInterceptor {
if (Objects.nonNull(annotation)) { if (Objects.nonNull(annotation)) {
Boolean accessPermission = Boolean accessPermission =
annotation.accessType().hasAccess(this.userService, this.objectMapper, request); annotation.accessType().hasAccess(this.userService, request);
if (!accessPermission) { if (!accessPermission) {
throw new AuthenticationInvalidException(annotation.denialMessage()); throw new AuthorizationException(annotation.denialMessage());
} }
} }
return true; return true;
} }
} }

View File

@@ -0,0 +1,43 @@
package br.com.hideyoshi.auth.security.oauth2.handler;
import br.com.hideyoshi.auth.enums.Provider;
import br.com.hideyoshi.auth.security.oauth2.model.OAuthDTO;
import br.com.hideyoshi.auth.util.exception.BadRequestException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
@Component
public class OAuthHandler {
public Provider getProviderFromURL(String url) {
String[] urlPartition = url.split("/");
return Provider.byValue(urlPartition[urlPartition.length - 1]);
}
public OAuthDTO parseOAuth2User(OAuth2User user, Provider provider) {
return switch (provider) {
case GITHUB -> parseFromGithub(user);
case GOOGLE -> parseFromGoogle(user);
default -> throw new BadRequestException("Provider not supported.");
};
}
private OAuthDTO parseFromGithub(OAuth2User user) {
return new OAuthDTO(
user.getAttribute("name"),
user.getAttribute("login"),
user.getAttribute("email"),
user.getAttribute("avatar_url"),
Provider.GITHUB
);
}
private OAuthDTO parseFromGoogle(OAuth2User user) {
return new OAuthDTO(
user.getAttribute("name"),
user.getAttribute("given_name"),
user.getAttribute("email"),
user.getAttribute("picture"),
Provider.GOOGLE
);
}
}

View File

@@ -0,0 +1,19 @@
package br.com.hideyoshi.auth.security.oauth2.model;
import br.com.hideyoshi.auth.enums.Provider;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class OAuthDTO {
private String name;
private String username;
private String email;
private String profilePictureUrl;
private Provider provider;
}

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.base.auth.oauth.repo; package br.com.hideyoshi.auth.security.oauth2.repository;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
@@ -64,4 +64,4 @@ public class OAuthRequestRepository implements AuthorizationRequestRepository<OA
); );
} }
} }

View File

@@ -1,23 +1,25 @@
package com.hideyoshi.auth.base.auth.service; package br.com.hideyoshi.auth.security.service;
import br.com.hideyoshi.auth.enums.Provider;
import br.com.hideyoshi.auth.enums.Role;
import br.com.hideyoshi.auth.model.UserAuthDTO;
import br.com.hideyoshi.auth.model.TokenDTO;
import br.com.hideyoshi.auth.model.UserDTO;
import br.com.hideyoshi.auth.model.microservice.StorageServiceDownloadResponse;
import br.com.hideyoshi.auth.security.oauth2.handler.OAuthHandler;
import br.com.hideyoshi.auth.security.oauth2.model.OAuthDTO;
import br.com.hideyoshi.auth.service.UserService;
import br.com.hideyoshi.auth.service.microservice.StorageService;
import br.com.hideyoshi.auth.util.exception.BadRequestException;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.auth.base.auth.oauth.mapper.OAuthMap;
import com.hideyoshi.auth.base.auth.oauth.mapper.OAuthMapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.auth.base.auth.model.TokenDTO;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.auth.microservice.storageService.service.StorageService;
import com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@@ -44,16 +46,18 @@ public class AuthService {
private final StorageService storageService; private final StorageService storageService;
@Value("${com.hideyoshi.tokenSecret}") private final OAuthHandler oAuthHandler;
@Value("${br.com.hideyoshi.tokenSecret}")
private String TOKEN_SECRET; private String TOKEN_SECRET;
@Value("${com.hideyoshi.accessTokenDuration}") @Value("${br.com.hideyoshi.accessTokenDuration}")
private Integer ACCESS_TOKEN_DURATION; private Integer ACCESS_TOKEN_DURATION;
@Value("${com.hideyoshi.refreshTokenDuration}") @Value("${br.com.hideyoshi.refreshTokenDuration}")
private Integer REFRESH_TOKEN_DURATION; private Integer REFRESH_TOKEN_DURATION;
public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) { public UserAuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL); user.setProvider(Provider.LOCAL);
UserDTO authenticatedUser = this.userService.saveUser(user); UserDTO authenticatedUser = this.userService.saveUser(user);
@@ -66,7 +70,7 @@ public class AuthService {
} }
public AuthDTO loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException { public UserAuthDTO loginUser(HttpServletRequest request, @Valid UserDTO user) throws IOException {
user.setProfilePictureUrl(this.extractProfilePictureUrl(user)); user.setProfilePictureUrl(this.extractProfilePictureUrl(user));
return this.generateNewAuthenticatedUser( return this.generateNewAuthenticatedUser(
@@ -75,20 +79,16 @@ public class AuthService {
); );
} }
public AuthDTO loginOAuthUser(OAuth2User oauthUser, HttpServletRequest request) { public UserAuthDTO loginOAuthUser(HttpServletRequest request, OAuth2User oAuth2User) {
Provider clientProvider = Provider.byValue( Provider provider = this.oAuthHandler.getProviderFromURL(request.getRequestURL().toString());
this.getClientFromUrl(request.getRequestURL().toString()) OAuthDTO oAuthDTO = this.oAuthHandler.parseOAuth2User(oAuth2User, provider);
);
OAuthMap oauthMap = this.generateOAuthMap(clientProvider, oauthUser); UserDTO user = this.getUserFromOAuth2User(oAuthDTO);
return this.processOAuthPostLogin( return this.processOAuthPostLogin(user, request);
this.generateAuthenticatedUserFromOAuth(oauthMap, oauthUser),
request
);
} }
public AuthDTO refreshAccessToken(String requestToken, HttpServletRequest request) { public UserAuthDTO refreshAccessToken(String requestToken, HttpServletRequest request) {
DecodedJWT decodedJWT = this.decodeToken(requestToken) DecodedJWT decodedJWT = this.decodeToken(requestToken)
.orElseThrow(() -> new BadRequestException("Invalid Token")); .orElseThrow(() -> new BadRequestException("Invalid Token"));
@@ -100,7 +100,7 @@ public class AuthService {
return this.refreshAuthenticatedUser(user, request, new TokenDTO(requestToken, decodedJWT.getExpiresAt())); return this.refreshAuthenticatedUser(user, request, new TokenDTO(requestToken, decodedJWT.getExpiresAt()));
} }
public AuthDTO validateAccessToken(HttpServletRequest request) { public UserAuthDTO validateAccessToken(HttpServletRequest request) {
UserDTO user = this.getLoggedUser(); UserDTO user = this.getLoggedUser();
user.setProfilePictureUrl(this.extractProfilePictureUrl(user)); user.setProfilePictureUrl(this.extractProfilePictureUrl(user));
@@ -109,23 +109,20 @@ public class AuthService {
} }
public UserDTO getLoggedUser() { public UserDTO getLoggedUser() {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return userService.getUser(username);
} }
public UsernamePasswordAuthenticationToken extractAccessTokenInfo(String accessToken) { public Authentication extractAccessTokenInfo(String accessToken) {
DecodedJWT decodedJWT = this.decodeToken(accessToken) DecodedJWT decodedJWT = this.decodeToken(accessToken)
.orElseThrow(() -> new BadRequestException("Invalid Token")); .orElseThrow(() -> new BadRequestException("Invalid Token"));
String username = decodedJWT.getSubject(); return new UsernamePasswordAuthenticationToken(
String[] roles = decodedJWT.getClaim("roles").asArray(String.class); this.userService.getUser(decodedJWT.getSubject()),
null,
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>(); stream(decodedJWT.getClaim("roles").asArray(String.class))
stream(roles).forEach(role -> { .map(SimpleGrantedAuthority::new)
authorities.add(new SimpleGrantedAuthority(role)); .collect(Collectors.toList())
}); );
return new UsernamePasswordAuthenticationToken(username, null, authorities);
} }
private Optional<DecodedJWT> decodeToken(String token) { private Optional<DecodedJWT> decodeToken(String token) {
@@ -139,7 +136,26 @@ public class AuthService {
return Optional.empty(); return Optional.empty();
} }
private AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) { private UserDTO getUserFromOAuth2User(OAuthDTO oAuth2User) {
UserDTO user;
try {
user = this.userService.getUser(oAuth2User.getUsername());
} catch (BadRequestException e) {
user = UserDTO.builder()
.name(oAuth2User.getName())
.username(oAuth2User.getUsername())
.email(oAuth2User.getEmail())
.roles(List.of(Role.USER))
.provider(oAuth2User.getProvider())
.profilePictureUrl(oAuth2User.getProfilePictureUrl())
.build();
}
return user;
}
private UserAuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
if (Objects.nonNull(user.getId())) { if (Objects.nonNull(user.getId())) {
this.userService.alterUser(user.getId(), user); this.userService.alterUser(user.getId(), user);
@@ -150,47 +166,15 @@ public class AuthService {
return this.generateNewAuthenticatedUser(user, request); return this.generateNewAuthenticatedUser(user, request);
} }
private String getClientFromUrl(String url) {
String[] urlPartition = url.split("/");
return urlPartition[urlPartition.length - 1];
}
private OAuthMap generateOAuthMap(Provider clientProvider, OAuth2User oauthUser) {
try {
return OAuthMapper.byValue(clientProvider).getMap()
.getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser);
} catch (Exception e) {
throw new BadRequestException("Unsupported OAuth Client.");
}
}
private String extractProfilePictureUrl(UserDTO user) { private String extractProfilePictureUrl(UserDTO user) {
return this.storageService.getFileUrl(user.getUsername(), "profile") return this.storageService.getFileUrl(user.getUsername(), "profile")
.map(StorageServiceDownloadResponse::getPresignedUrl) .map(StorageServiceDownloadResponse::getPresignedUrl)
.orElse(null); .orElse(null);
} }
private UserDTO generateAuthenticatedUserFromOAuth(OAuthMap oauthMap, OAuth2User oauthUser) { private UserAuthDTO generateNewAuthenticatedUser(UserDTO user, HttpServletRequest request) {
UserDTO user;
try {
user = this.userService.getUser(oauthMap.getPrincipal());
} catch (BadRequestException e) {
user = UserDTO.builder()
.name(oauthUser.getAttribute("name"))
.username(oauthMap.getPrincipal())
.email(oauthUser.getAttribute("email"))
.roles(List.of(Role.USER))
.provider(oauthMap.getProvider())
.build();
}
user.setProfilePictureUrl(oauthMap.getProfilePicture());
return user;
}
private AuthDTO generateNewAuthenticatedUser(UserDTO user, HttpServletRequest request) {
HttpSession httpSession = request.getSession(); HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO( UserAuthDTO authObject = new UserAuthDTO(
user, user,
this.generateToken(user, request, ACCESS_TOKEN_DURATION), this.generateToken(user, request, ACCESS_TOKEN_DURATION),
this.generateToken(user, request, REFRESH_TOKEN_DURATION) this.generateToken(user, request, REFRESH_TOKEN_DURATION)
@@ -201,9 +185,9 @@ public class AuthService {
return authObject; return authObject;
} }
private AuthDTO refreshAuthenticatedUser(UserDTO user, HttpServletRequest request, TokenDTO refreshToken) { private UserAuthDTO refreshAuthenticatedUser(UserDTO user, HttpServletRequest request, TokenDTO refreshToken) {
HttpSession httpSession = request.getSession(); HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO( UserAuthDTO authObject = new UserAuthDTO(
user, user,
this.generateToken(user, request, ACCESS_TOKEN_DURATION), this.generateToken(user, request, ACCESS_TOKEN_DURATION),
refreshToken refreshToken

View File

@@ -0,0 +1,21 @@
package br.com.hideyoshi.auth.service;
import br.com.hideyoshi.auth.model.UserAuthDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;
@Service
@RequiredArgsConstructor
public class SessionManagerService {
public UserAuthDTO validateSession(HttpSession session) {
return (UserAuthDTO) session.getAttribute("user");
}
public void destroySession(HttpSession session) {
session.invalidate();
}
}

View File

@@ -1,15 +1,16 @@
package com.hideyoshi.auth.base.auth.service; package br.com.hideyoshi.auth.service;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Provider;
import com.hideyoshi.auth.base.auth.entity.Role; import br.com.hideyoshi.auth.enums.Role;
import com.hideyoshi.auth.base.auth.entity.User; import br.com.hideyoshi.auth.entity.User;
import com.hideyoshi.auth.base.auth.model.UserDTO; import br.com.hideyoshi.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.repo.UserRepository; import br.com.hideyoshi.auth.repository.UserRepository;
import com.hideyoshi.auth.util.exception.BadRequestException; import br.com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -132,7 +133,11 @@ public class UserService implements UserDetailsService {
} }
public UserDetails loadUserByUsername(String username) { public UserDetails loadUserByUsername(String username) {
return this.getUser(username); try {
return this.getUser(username);
} catch (BadRequestException e) {
throw new UsernameNotFoundException("User not found.");
}
} }
private String validatePassword(UserDTO user) { private String validatePassword(UserDTO user) {

View File

@@ -1,11 +1,11 @@
package com.hideyoshi.auth.microservice.storageService.service; package br.com.hideyoshi.auth.service.microservice;
import br.com.hideyoshi.auth.config.StorageServiceConfig;
import br.com.hideyoshi.auth.enums.FileTypeEnum;
import br.com.hideyoshi.auth.model.microservice.StorageServiceDownloadResponse;
import br.com.hideyoshi.auth.model.microservice.StorageServiceUploadResponse;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.microservice.storageService.config.StorageServiceConfig;
import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
@@ -20,9 +20,9 @@ import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Optional; import java.util.Optional;
@@ -137,11 +137,7 @@ public class StorageService {
HttpPost request = new HttpPost(requestURI); HttpPost request = new HttpPost(requestURI);
request.setHeader("Content-Type", "application/json"); request.setHeader("Content-Type", "application/json");
try { request.setEntity(new ByteArrayEntity(requestBody.getBytes(StandardCharsets.UTF_8)));
request.setEntity(new ByteArrayEntity(requestBody.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
CloseableHttpClient httpClient = HttpClientBuilder.create() CloseableHttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy()).build(); .setRedirectStrategy(new LaxRedirectStrategy()).build();

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -0,0 +1,12 @@
package br.com.hideyoshi.auth.util.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.FORBIDDEN)
public class AuthorizationException extends RuntimeException {
public AuthorizationException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,9 @@
package br.com.hideyoshi.auth.util.exception;
import java.time.LocalDateTime;
public class AuthorizationExceptionDetails extends ExceptionDetails {
public AuthorizationExceptionDetails(String title, Integer status, String details, String developerMessage, LocalDateTime timestamp) {
super(title, status, details, developerMessage, timestamp);
}
}

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.exception; package br.com.hideyoshi.auth.util.exception;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.guard; package br.com.hideyoshi.auth.util.guard;
import java.lang.annotation.*; import java.lang.annotation.*;

View File

@@ -1,9 +1,9 @@
package 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.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.service.UserService;
import lombok.Getter; import lombok.Getter;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@@ -19,9 +19,8 @@ public enum UserResourceGuardEnum {
@Override @Override
public Boolean hasAccess( public Boolean hasAccess(
UserService userService, UserService userService,
ObjectMapper objectMapper,
HttpServletRequest request) { HttpServletRequest request) {
return justUser(userService, objectMapper, request); return UserResourceGuardEnum.justUser(userService, request);
} }
}, },
@@ -29,9 +28,8 @@ public enum UserResourceGuardEnum {
@Override @Override
public Boolean hasAccess( public Boolean hasAccess(
UserService userService, UserService userService,
ObjectMapper objectMapper,
HttpServletRequest request) { HttpServletRequest request) {
return sameUser(userService, objectMapper, request); return UserResourceGuardEnum.sameUser(userService, request);
} }
}, },
@@ -39,9 +37,8 @@ public enum UserResourceGuardEnum {
@Override @Override
public Boolean hasAccess( public Boolean hasAccess(
UserService userService, UserService userService,
ObjectMapper objectMapper,
HttpServletRequest request) { HttpServletRequest request) {
return adminUser(userService, objectMapper, request); return UserResourceGuardEnum.adminUser(userService, request);
} }
}, },
@@ -49,9 +46,8 @@ public enum UserResourceGuardEnum {
@Override @Override
public Boolean hasAccess( public Boolean hasAccess(
UserService userService, UserService userService,
ObjectMapper objectMapper,
HttpServletRequest request) { HttpServletRequest request) {
return openAccess(userService, objectMapper, request); return openAccess(userService, request);
} }
}; };
@@ -70,19 +66,18 @@ public enum UserResourceGuardEnum {
throw new IllegalArgumentException("Argument not valid."); throw new IllegalArgumentException("Argument not valid.");
} }
private static boolean justUser(UserService userService, ObjectMapper objectMapper, HttpServletRequest request) { private static boolean justUser(UserService userService, HttpServletRequest request) {
UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDTO userLogged = userService.getUser(username);
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.USER.getDescription())); return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.USER.getDescription()));
} }
private static boolean sameUser(UserService userService, ObjectMapper objectMapper, HttpServletRequest request) { private static boolean sameUser(UserService userService, HttpServletRequest request) {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDTO userLogged = userService.getUser(username);
Object requestPathVariable = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); Object requestPathVariable = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
ObjectMapper objectMapper = new ObjectMapper();
HashMap<String, String> pathVariable = objectMapper.convertValue(requestPathVariable, HashMap.class); HashMap<String, String> pathVariable = objectMapper.convertValue(requestPathVariable, HashMap.class);
UserDTO userInfo = userService.getUser(Long.parseLong(pathVariable.get("id"))); UserDTO userInfo = userService.getUser(Long.parseLong(pathVariable.get("id")));
@@ -90,20 +85,17 @@ public enum UserResourceGuardEnum {
} }
private static boolean adminUser(UserService userService, ObjectMapper objectMapper, HttpServletRequest request) { private static boolean adminUser(UserService userService, HttpServletRequest request) {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserDTO userLogged = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDTO userLogged = userService.getUser(username);
return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.getDescription())); return userLogged.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.getDescription()));
} }
private static Boolean openAccess(UserService userService, ObjectMapper objectMapper, HttpServletRequest request) { private static Boolean openAccess(UserService userService, HttpServletRequest request) {
return true; return true;
} }
public abstract Boolean hasAccess( public abstract Boolean hasAccess(
UserService userService, UserService userService,
ObjectMapper objectMapper,
HttpServletRequest request); HttpServletRequest request);
} }

View File

@@ -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 UserResourceHandler {
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;
}
}

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.util.guard; package br.com.hideyoshi.auth.util.guard;
import com.hideyoshi.auth.base.auth.model.UserDTO; import br.com.hideyoshi.auth.model.UserDTO;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.util.handler; package br.com.hideyoshi.auth.util.handler;
import com.hideyoshi.auth.util.exception.*; import br.com.hideyoshi.auth.util.exception.*;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -40,6 +40,15 @@ public class RestExceptionHandler extends ResponseEntityExceptionHandler {
HttpStatus.FORBIDDEN); HttpStatus.FORBIDDEN);
} }
@ExceptionHandler(AuthorizationException.class)
public ResponseEntity<AuthorizationExceptionDetails> handleBadRequest(final AuthorizationException exception) {
return new ResponseEntity<>(
new AuthorizationExceptionDetails("Authorization Failed. Check your permissions.",
HttpStatus.FORBIDDEN.value(), exception.getMessage(),
exception.getClass().getName(), LocalDateTime.now()),
HttpStatus.FORBIDDEN);
}
@Override @Override
protected ResponseEntity<Object> handleMethodArgumentNotValid( protected ResponseEntity<Object> handleMethodArgumentNotValid(
final MethodArgumentNotValidException exception, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { final MethodArgumentNotValidException exception, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.util.validator.email.unique; package br.com.hideyoshi.auth.util.validator.email.unique;
import com.hideyoshi.auth.base.auth.repo.UserRepository; import br.com.hideyoshi.auth.repository.UserRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.validator.email.unique; package br.com.hideyoshi.auth.util.validator.email.unique;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.validator.email.valid; package br.com.hideyoshi.auth.util.validator.email.valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth.util.validator.email.valid; package br.com.hideyoshi.auth.util.validator.email.valid;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.util.validator.password; package br.com.hideyoshi.auth.util.validator.password;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Provider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.auth.util.validator.password; package br.com.hideyoshi.auth.util.validator.password;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Provider;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,21 +0,0 @@
package com.hideyoshi.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootApplication
public class AuthServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -1,122 +0,0 @@
package com.hideyoshi.auth.base.auth.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.base.config.RestAuthenticationEntryPointConfig;
import com.hideyoshi.auth.base.auth.filter.CustomAuthenticationFilter;
import com.hideyoshi.auth.base.auth.filter.CustomAuthorizationFilter;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.auth.base.auth.oauth.repo.OAuthRequestRepository;
import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Log4j2
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthService authService;
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder passwordEncoder;
private final OAuthRequestRepository oAuthRequestRepository;
private final RestAuthenticationEntryPointConfig restAuthenticationEntryPointConfig;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
this.addSecurityToHttp(http);
this.addOAuthSecurityToHttp(http);
}
private void addSecurityToHttp(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter =
new CustomAuthenticationFilter(this.authenticationManager(), this.authService, this.restAuthenticationEntryPointConfig);
customAuthenticationFilter.setFilterProcessesUrl("/user/login");
http.authorizeRequests()
.antMatchers("/session/**").permitAll()
.and().authorizeRequests().antMatchers("/health").permitAll()
.and().authorizeRequests().antMatchers("/user/signup").permitAll()
.and().authorizeRequests().antMatchers("/user/oauth/**").permitAll()
.and().authorizeRequests().antMatchers("/user/login/**").permitAll()
.and().authorizeRequests().antMatchers("/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().addFilter(customAuthenticationFilter)
.addFilterBefore(new CustomAuthorizationFilter(this.authService), UsernamePasswordAuthenticationFilter.class);
}
private void addOAuthSecurityToHttp(HttpSecurity http) throws Exception {
http.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(this.oAuthRequestRepository)
.and().successHandler(this::successHandler)
.failureHandler(this::failureHandler);
}
private void successHandler(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
AuthDTO authUser = this.authService.loginOAuthUser(oauthUser, request);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authUser);
}
private void failureHandler(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) {
throw new AuthenticationInvalidException("Invalid Authentication Attempt.");
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}

View File

@@ -1,69 +0,0 @@
package com.hideyoshi.auth.base.auth.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.base.config.RestAuthenticationEntryPointConfig;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Log4j2
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthService authService;
private final AuthenticationManager authenticationManager;
private final RestAuthenticationEntryPointConfig restAuthenticationEntryPointConfig;
public CustomAuthenticationFilter(AuthenticationManager authenticationManager, AuthService authService, RestAuthenticationEntryPointConfig restAuthenticationEntryPointConfig) {
this.authService = authService;
this.authenticationManager = authenticationManager;
this.restAuthenticationEntryPointConfig = restAuthenticationEntryPointConfig;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Authentication userAuthentication = null;
try {
userAuthentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
} catch (AuthenticationException e) {
restAuthenticationEntryPointConfig.commence(request, response, e);
}
return userAuthentication;
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException {
AuthDTO authUser = this.authService.loginUser(
request,
response,
(UserDTO) authentication.getPrincipal()
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authUser);
}
}

View File

@@ -1,86 +0,0 @@
package com.hideyoshi.auth.base.auth.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidExceptionDetails;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
public class CustomAuthorizationFilter extends OncePerRequestFilter {
private static final List<String> notProtectedPaths = Arrays.asList(
"/health",
"/user/login",
"/user/signup",
"/user/login/refresh",
"/session/validate",
"/session/destroy"
);
private static final String AUTHORIZATION_TYPE_STRING = "Bearer ";
private final AuthService authService;
public CustomAuthorizationFilter(AuthService authService) {
this.authService = authService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.isPathNotProtected(request.getServletPath())) {
filterChain.doFilter(request, response);
return;
}
String authorizationHeader = request.getHeader(AUTHORIZATION);
try {
UsernamePasswordAuthenticationToken authenticationToken =
this.validateUserAccess(authorizationHeader);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} catch (Exception e) {
response.setHeader("error", e.getMessage());
response.setStatus(FORBIDDEN.value());
AuthenticationInvalidExceptionDetails error = new AuthenticationInvalidExceptionDetails("Authentication Failed. Check your credentials.",
HttpStatus.FORBIDDEN.value(), e.getMessage(),
e.getClass().getName(), LocalDateTime.now());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), error);
}
}
private Boolean isPathNotProtected(String path) {
return notProtectedPaths.contains(path);
}
private UsernamePasswordAuthenticationToken validateUserAccess(String authorizationHeader) {
if (Objects.nonNull(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) {
String accessToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length());
return this.authService.extractAccessTokenInfo(accessToken);
} else {
throw new AuthenticationInvalidException("Access denied");
}
}
}

View File

@@ -1,27 +0,0 @@
package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.AllArgsConstructor;
import org.springframework.security.oauth2.core.user.OAuth2User;
@AllArgsConstructor
public class GithubOAuthMap implements OAuthMap {
private OAuth2User oAuth2User;
@Override
public String getPrincipal() {
return oAuth2User.getAttribute("login");
}
@Override
public String getProfilePicture() {
return this.oAuth2User.getAttribute("avatar_url");
}
@Override
public Provider getProvider() {
return Provider.GITHUB;
}
}

View File

@@ -1,27 +0,0 @@
package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.AllArgsConstructor;
import org.springframework.security.oauth2.core.user.OAuth2User;
@AllArgsConstructor
public class GoogleOAuthMap implements OAuthMap {
private OAuth2User oAuth2User;
@Override
public String getPrincipal() {
return this.oAuth2User.getAttribute("given_name");
}
@Override
public String getProfilePicture() {
return this.oAuth2User.getAttribute("picture");
}
@Override
public Provider getProvider() {
return Provider.GOOGLE;
}
}

View File

@@ -1,13 +0,0 @@
package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
public interface OAuthMap {
String getPrincipal();
String getProfilePicture();
Provider getProvider();
}

View File

@@ -1,35 +0,0 @@
package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.Getter;
public enum OAuthMapper {
GOOGLE(GoogleOAuthMap.class, Provider.GOOGLE),
GITHUB(GithubOAuthMap.class, Provider.GITHUB);
private final Class<? extends OAuthMap> oAuthMap;
@Getter
private final Provider provider;
private OAuthMapper(Class<? extends OAuthMap> oAuthMap, Provider provider) {
this.oAuthMap = oAuthMap;
this.provider = provider;
}
public static OAuthMapper byValue(Provider provider) {
for (OAuthMapper e : values()) {
if (e.getProvider().equals(provider)) {
return e;
}
}
throw new IllegalArgumentException("Argument not valid.");
}
public Class<? extends OAuthMap> getMap() {
return oAuthMap;
}
}

View File

@@ -1,33 +0,0 @@
package com.hideyoshi.auth.base.config;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Log4j2
@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPointConfig implements AuthenticationEntryPoint {
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) {
resolver.resolveException(
request,
response,
null,
new AuthenticationInvalidException("Authentication Failed. Check your credentials.")
);
}
}

View File

@@ -1,22 +0,0 @@
package com.hideyoshi.auth.base.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
public class SessionConfig {
@Value("${com.hideyoshi.frontEndPath}")
private String frontEndPath;
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSION");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("(^.+)?(\\.)?(" + frontEndPath + ")((/#!)?(/\\w+)+)?");
return serializer;
}
}

View File

@@ -1,13 +0,0 @@
package com.hideyoshi.auth.base.session.service;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import javax.servlet.http.HttpSession;
public interface SessionManagerService {
AuthDTO validateSession(HttpSession session);
void destroySession(HttpSession session);
}

View File

@@ -1,23 +0,0 @@
package com.hideyoshi.auth.base.session.service;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;
@Service
@RequiredArgsConstructor
public class SessionManagerServiceImpl implements SessionManagerService {
@Override
public AuthDTO validateSession(HttpSession session) {
return (AuthDTO) session.getAttribute("user");
}
@Override
public void destroySession(HttpSession session) {
session.invalidate();
}
}

View File

@@ -1,17 +1,18 @@
com: br:
hideyoshi: com:
frontendPath: ${FRONTEND_PATH} hideyoshi:
tokenSecret: ${TOKEN_SECRET} frontendPath: ${FRONTEND_PATH}
accessTokenDuration: ${ACCESS_TOKEN_DURATION} tokenSecret: ${TOKEN_SECRET}
refreshTokenDuration: ${REFRESH_TOKEN_DURATION} accessTokenDuration: ${ACCESS_TOKEN_DURATION}
defaultUser: refreshTokenDuration: ${REFRESH_TOKEN_DURATION}
fullName: ${DEFAULT_USER_FULLNAME} defaultUser:
email: ${DEFAULT_USER_EMAIL} fullName: ${DEFAULT_USER_FULLNAME}
username: ${DEFAULT_USER_USERNAME} email: ${DEFAULT_USER_EMAIL}
password: ${DEFAULT_USER_PASSWORD} username: ${DEFAULT_USER_USERNAME}
password: ${DEFAULT_USER_PASSWORD}
microservice: microservice:
storageServicePath: ${STORAGE_SERVICE_PATH} storageServicePath: ${STORAGE_SERVICE_PATH}
server: server:

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.auth; package br.com.hideyoshi.auth;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

View File

@@ -1,10 +1,9 @@
package com.hideyoshi.auth.base.user.repo; package br.com.hideyoshi.auth.repository;
import com.hideyoshi.auth.base.auth.repo.UserRepository; import br.com.hideyoshi.auth.enums.Provider;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Role;
import com.hideyoshi.auth.base.auth.entity.Role; import br.com.hideyoshi.auth.entity.User;
import com.hideyoshi.auth.base.auth.entity.User; import br.com.hideyoshi.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -1,12 +1,11 @@
package com.hideyoshi.auth.base.user.service; package br.com.hideyoshi.auth.service;
import com.hideyoshi.auth.base.auth.service.UserService; import br.com.hideyoshi.auth.enums.Provider;
import com.hideyoshi.auth.base.auth.entity.Provider; import br.com.hideyoshi.auth.enums.Role;
import com.hideyoshi.auth.base.auth.entity.Role; import br.com.hideyoshi.auth.entity.User;
import com.hideyoshi.auth.base.auth.entity.User; import br.com.hideyoshi.auth.model.UserDTO;
import com.hideyoshi.auth.base.auth.model.UserDTO; import br.com.hideyoshi.auth.repository.UserRepository;
import com.hideyoshi.auth.base.auth.repo.UserRepository; import br.com.hideyoshi.auth.util.exception.BadRequestException;
import com.hideyoshi.auth.util.exception.BadRequestException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@@ -17,6 +16,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
@@ -387,7 +387,7 @@ class UserServiceTest {
// When // When
//Then //Then
assertThrows( assertThrows(
BadRequestException.class, UsernameNotFoundException.class,
() -> { () -> {
this.underTest.loadUserByUsername(user.getUsername()); this.underTest.loadUserByUsername(user.getUsername());
}, },

View File

@@ -1,10 +1,10 @@
package com.hideyoshi.auth.microservice.storageService.service; package br.com.hideyoshi.auth.service.microservice;
import br.com.hideyoshi.auth.config.StorageServiceConfig;
import br.com.hideyoshi.auth.enums.FileTypeEnum;
import br.com.hideyoshi.auth.model.microservice.StorageServiceDownloadResponse;
import br.com.hideyoshi.auth.model.microservice.StorageServiceUploadResponse;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.auth.microservice.storageService.config.StorageServiceConfig;
import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;