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:
9
pom.xml
9
pom.xml
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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");
|
||||||
@@ -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);
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hideyoshi.auth.base.auth.entity;
|
package br.com.hideyoshi.auth.enums;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.hideyoshi.auth.base.auth.model;
|
package br.com.hideyoshi.auth.model;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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();
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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,9 +33,9 @@ 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;
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
try {
|
||||||
return this.getUser(username);
|
return this.getUser(username);
|
||||||
|
} catch (BadRequestException e) {
|
||||||
|
throw new UsernameNotFoundException("User not found.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String validatePassword(UserDTO user) {
|
private String validatePassword(UserDTO user) {
|
||||||
@@ -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();
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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.*;
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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) {
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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.")
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
br:
|
||||||
com:
|
com:
|
||||||
hideyoshi:
|
hideyoshi:
|
||||||
frontendPath: ${FRONTEND_PATH}
|
frontendPath: ${FRONTEND_PATH}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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());
|
||||||
},
|
},
|
||||||
@@ -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;
|
||||||
Reference in New Issue
Block a user