Authorization working
This commit is contained in:
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "java",
|
||||||
|
"name": "Spring Boot-RaidBuilderAPI<raid-builder-api>",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"mainClass": "com.mattrixwv.raidbuilder.RaidBuilderAPI",
|
||||||
|
"projectName": "raid-builder-api",
|
||||||
|
"args": "--spring.config.additional-location=local/application.properties --logging.config=local/log4j2-spring.xml",
|
||||||
|
"envFile": "${workspaceFolder}/.env"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"raidbuilder",
|
||||||
|
"springframework"
|
||||||
|
]
|
||||||
|
}
|
||||||
25
SpringStuffToIgnoreInExceptions.txt
Normal file
25
SpringStuffToIgnoreInExceptions.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
java.lang.reflect.Method
|
||||||
|
org.apache.cataline
|
||||||
|
org.springframework.aop
|
||||||
|
org.springframework.security
|
||||||
|
org.springframework.transaction
|
||||||
|
org.springframework.web
|
||||||
|
sun.reflect
|
||||||
|
net.sf.cglib
|
||||||
|
ByCGLIB
|
||||||
|
//Thsea re ones I'm removing because I'm not seeing anythign important in them
|
||||||
|
BySpringCGLIB //Spring impl of CGLIB?
|
||||||
|
org.springframework.cglib
|
||||||
|
org.springframework.orm //Spring automagic
|
||||||
|
org.springframework.dao //Spring automagic
|
||||||
|
org.springframework.session //Spring automagic
|
||||||
|
org.springframework.repository //Spring automagic
|
||||||
|
org.springframework.data //Spring automagic
|
||||||
|
org.springframework.scheduling //Spring automagic scheduling
|
||||||
|
org.springframework.beans //Spring automagic beans
|
||||||
|
org.springframework.boot //Spring Boot automagic
|
||||||
|
jdk.internal.reflect //Java automagic
|
||||||
|
sun.proxy //Java automagic
|
||||||
|
io.netty.util.concurrent //Server framework threading
|
||||||
|
com.google.common.util.concurrent //Threading
|
||||||
|
java.util.concurrent //Threading
|
||||||
4
db/1.0.0/1. schema.sql
Normal file
4
db/1.0.0/1. schema.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
CREATE USER raid_builder WITH PASSWORD 'raid_builder';
|
||||||
|
|
||||||
|
CREATE SCHEMA raid_builder;
|
||||||
|
GRANT ALL ON SCHEMA raid_builder TO raid_builder;
|
||||||
16
db/1.0.0/2. createAccount.sql
Normal file
16
db/1.0.0/2. createAccount.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
CREATE TYPE raid_builder.account_status AS ENUM ( 'ACTIVE', 'LOCKED', 'INACTIVE', 'DELETED', 'UNCONFIRMED');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS raid_builder.account(
|
||||||
|
account_id uuid PRIMARY KEY,
|
||||||
|
username text UNIQUE NOT NULL,
|
||||||
|
password text NOT NULL,
|
||||||
|
login_date timestamptz,
|
||||||
|
email text UNIQUE NOT NULL,
|
||||||
|
force_reset boolean NOT NULL,
|
||||||
|
refresh_token uuid UNIQUE,
|
||||||
|
refresh_token_expiration timestamptz,
|
||||||
|
account_status raid_builder.account_status NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE raid_builder.account TO raid_builder;
|
||||||
10
db/1.0.0/3. createAccountPermission.sql
Normal file
10
db/1.0.0/3. createAccountPermission.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TYPE raid_builder.account_permission_type AS ENUM ( 'ADMIN', 'USER' );
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS raid_builder.account_permission(
|
||||||
|
account_permission_id uuid PRIMARY KEY,
|
||||||
|
account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL,
|
||||||
|
account_permission_type raid_builder.account_permission_type NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE raid_builder.account_permission TO raid_builder;
|
||||||
14
db/1.0.0/4. createAccountTutorialStatus.sql
Normal file
14
db/1.0.0/4. createAccountTutorialStatus.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TYPE raid_builder.tutorial_status AS ENUM ( 'COMPLETED', 'NOT_COMPLETED' );
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS raid_builder.account_tutorial_status (
|
||||||
|
account_tutorial_status_id uuid PRIMARY KEY,
|
||||||
|
account_id uuid REFERENCES raid_builder.account(account_id),
|
||||||
|
games_tutorial_status raid_builder.tutorial_status NOT NULL,
|
||||||
|
game_tutorial_status raid_builder.tutorial_status NOT NULL,
|
||||||
|
raid_groups_tutorial_status raid_builder.tutorial_status NOT NULL,
|
||||||
|
raid_group_tutorial_status raid_builder.tutorial_status NOT NULL,
|
||||||
|
instance_tutorial_status raid_builder.tutorial_status NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE raid_builder.account_tutorial_status TO raid_builder;
|
||||||
144
pom.xml
Normal file
144
pom.xml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.mattrixwv.raidbuilder</groupId>
|
||||||
|
<artifactId>raid-builder-api</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<name>Raid Builder API</name>
|
||||||
|
<url>https://api.raidbuilder.mattrixwv.com</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<!--Compile-->
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
|
||||||
|
<!--Sonarqube-->
|
||||||
|
<sonar.java.source>21</sonar.java.source>
|
||||||
|
<sonar.dependencyCheck.jsonReportPath>target/dependency-check-report.json</sonar.dependencyCheck.jsonReportPath>
|
||||||
|
<sonar.dependencyCheck.htmlReportPath>target/dependency-check-report.html</sonar.dependencyCheck.htmlReportPath>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.4.3</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!--! Spring Dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-aspects</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.vaadin.external.google</groupId>
|
||||||
|
<artifactId>android-json</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--! Database -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.7.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.hypersistence</groupId>
|
||||||
|
<artifactId>hypersistence-utils-hibernate-63</artifactId>
|
||||||
|
<version>3.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--! Jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.18.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-hibernate6</artifactId>
|
||||||
|
<version>2.18.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--! Boilerplate Generator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.36</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--! Logging -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>2.0.16</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-layout-template-json</artifactId>
|
||||||
|
<version>2.24.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lmax</groupId>
|
||||||
|
<artifactId>disruptor</artifactId>
|
||||||
|
<version>4.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
17
src/main/java/com/mattrixwv/raidbuilder/RaidBuilderAPI.java
Normal file
17
src/main/java/com/mattrixwv/raidbuilder/RaidBuilderAPI.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.mattrixwv.raidbuilder;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.config.RsaKeyProperties;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties(RsaKeyProperties.class)
|
||||||
|
public class RaidBuilderAPI{
|
||||||
|
public static void main(String[] args){
|
||||||
|
SpringApplication.run(RaidBuilderAPI.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.annotation;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
|
||||||
|
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface AccountAuthorization{
|
||||||
|
public AccountPermissionType[] permissions();
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.aspect;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountPermission;
|
||||||
|
import com.mattrixwv.raidbuilder.exception.MissingAuthorizationException;
|
||||||
|
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
|
||||||
|
import com.mattrixwv.raidbuilder.service.AccountService;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountAuthorizationAspect{
|
||||||
|
private final AccountService accountService;
|
||||||
|
private final AccountPermissionService accountPermissionService;
|
||||||
|
|
||||||
|
|
||||||
|
@Pointcut("@annotation(com.mattrixwv.raidbuilder.annotation.AccountAuthorization)")
|
||||||
|
public void accountAuthorizationAnnotation(){
|
||||||
|
//Intentionally blank
|
||||||
|
}
|
||||||
|
|
||||||
|
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
|
||||||
|
public void mappedFunction(){
|
||||||
|
//Intentionally blank
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before("accountAuthorizationAnnotation()")
|
||||||
|
public void authorizeAccount(JoinPoint joinPoint){
|
||||||
|
log.debug("Authorizing account");
|
||||||
|
|
||||||
|
|
||||||
|
//Get the annotation
|
||||||
|
AccountAuthorization accountAuthorization = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(AccountAuthorization.class);
|
||||||
|
log.debug("Required authorizations = {}", accountAuthorization);
|
||||||
|
//Return if there are no required permissions
|
||||||
|
if(accountAuthorization.permissions().length == 0){
|
||||||
|
log.debug("No required permissions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the account
|
||||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
String username = ((Jwt)auth.getPrincipal()).getClaimAsString("sub");
|
||||||
|
Account account = accountService.getByUsername(username);
|
||||||
|
if(account.getAccountStatus() != AccountStatus.ACTIVE){
|
||||||
|
throw new AuthorizationDeniedException("Account is not active", () -> false);
|
||||||
|
}
|
||||||
|
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
|
||||||
|
|
||||||
|
//Return if the account has a matching permissions
|
||||||
|
for(AccountPermission permission : accountPermissions){
|
||||||
|
for(AccountPermissionType permissionType : accountAuthorization.permissions()){
|
||||||
|
if(permission.getAccountPermissionType() == permissionType){
|
||||||
|
log.debug("User is authorized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("User is not authorized");
|
||||||
|
|
||||||
|
//If the user doesn't have a matching permission, throw an authorization exception
|
||||||
|
throw new AuthorizationDeniedException("User is not authorized to perform this action", () -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before("mappedFunction() && !accountAuthorizationAnnotation()")
|
||||||
|
public void missingAuthorization(){
|
||||||
|
throw new MissingAuthorizationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "rsa")
|
||||||
|
public record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey){
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.config;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.jwk.JWK;
|
||||||
|
import com.nimbusds.jose.jwk.JWKSet;
|
||||||
|
import com.nimbusds.jose.jwk.RSAKey;
|
||||||
|
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
||||||
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||||
|
import com.nimbusds.jose.proc.SecurityContext;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SecurityConfig{
|
||||||
|
private final RsaKeyProperties rsaKeys;
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder(){
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
|
||||||
|
http
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
|
.authorizeHttpRequests(auth -> {
|
||||||
|
auth.requestMatchers("/auth/refresh").permitAll() //Permit refresh tokens
|
||||||
|
.requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm").permitAll() //Permit signup operations
|
||||||
|
.requestMatchers("/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations
|
||||||
|
.anyRequest().authenticated();
|
||||||
|
})
|
||||||
|
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
|
||||||
|
.httpBasic(Customizer.withDefaults())
|
||||||
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtEncoder jwtEncoder(){
|
||||||
|
JWK jwk = new RSAKey.Builder(rsaKeys.publicKey()).privateKey(rsaKeys.privateKey()).build();
|
||||||
|
|
||||||
|
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
|
||||||
|
|
||||||
|
return new NimbusJwtEncoder(jwks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtDecoder jwtDecoder(){
|
||||||
|
return NimbusJwtDecoder.withPublicKey(rsaKeys.publicKey()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TokenService{
|
||||||
|
private final JwtEncoder encoder;
|
||||||
|
private final AccountPermissionService accountPermissionService;
|
||||||
|
//Fields
|
||||||
|
@Value("${jwt.accessTokenDuration}")
|
||||||
|
private Duration accessTokenDuration;
|
||||||
|
|
||||||
|
|
||||||
|
public String generateAccessToken(Account account){
|
||||||
|
log.debug("Generating access token for account {}", account.getAccountId());
|
||||||
|
|
||||||
|
|
||||||
|
String scope = accountPermissionService.getByAccountId(account.getAccountId()).stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(" "));
|
||||||
|
|
||||||
|
Instant now = Instant.now();
|
||||||
|
JwtClaimsSet claims = JwtClaimsSet.builder()
|
||||||
|
.issuer("self")
|
||||||
|
.issuedAt(now)
|
||||||
|
.expiresAt(now.plus(accessTokenDuration))
|
||||||
|
.subject(account.getUsername())
|
||||||
|
.claim("scope", scope)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.config;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module;
|
||||||
|
import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module.Feature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class WebConfig implements WebMvcConfigurer{
|
||||||
|
@Value("${allowedOrigins}")
|
||||||
|
private String allowedOrigins;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(@NonNull CorsRegistry registry){
|
||||||
|
log.debug("Adding CORS mappings: {}", allowedOrigins);
|
||||||
|
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOriginPatterns(allowedOrigins)
|
||||||
|
.allowedMethods("GET", "PUT", "DELETE", "OPTIONS", "PATCH", "POST");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ObjectMapper objectMapper(){
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
log.debug("Starting mapping configuration");
|
||||||
|
|
||||||
|
//Make sure Jackson doesn't attempt lazy loading
|
||||||
|
Hibernate6Module hibernate6Module = new Hibernate6Module();
|
||||||
|
hibernate6Module.configure(Feature.FORCE_LAZY_LOADING, false);
|
||||||
|
hibernate6Module.configure(Feature.USE_TRANSIENT_ANNOTATION, false);
|
||||||
|
hibernate6Module.configure(Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER, true);
|
||||||
|
hibernate6Module.configure(Feature.WRITE_MISSING_ENTITIES_AS_NULL, false);
|
||||||
|
mapper.registerModule(hibernate6Module);
|
||||||
|
|
||||||
|
//Print dates as strings
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||||
|
mapper.findAndRegisterModules();
|
||||||
|
|
||||||
|
log.debug("Completed mapping configuration");
|
||||||
|
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class WebFilter extends OncePerRequestFilter{
|
||||||
|
@Override
|
||||||
|
public void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException{
|
||||||
|
if(!request.getMethod().equalsIgnoreCase("OPTIONS")){
|
||||||
|
setupMDC(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Continue to the controller
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
|
||||||
|
//Clear the MDC for the next request
|
||||||
|
MDC.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMDC(HttpServletRequest request){
|
||||||
|
//Get the requestId
|
||||||
|
String requestId = request.getHeader("X-Request-Id");
|
||||||
|
if(requestId != null){
|
||||||
|
MDC.put("requestId", requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get IP address
|
||||||
|
String forwardedFor = request.getHeader("X-Forwarded-For");
|
||||||
|
if(forwardedFor != null){
|
||||||
|
MDC.put("ip", forwardedFor.split(",")[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get all of the parameters in the request and print them in the log
|
||||||
|
StringJoiner parameters = new StringJoiner(", ");
|
||||||
|
request.getParameterMap().entrySet().forEach(entry -> {
|
||||||
|
if(!entry.getKey().equals("_")){
|
||||||
|
String key = entry.getKey();
|
||||||
|
String value = "";
|
||||||
|
if(entry.getValue().length > 1){
|
||||||
|
StringJoiner joiner = new StringJoiner(", ", "[", "]");
|
||||||
|
for(String str : entry.getValue()){
|
||||||
|
joiner.add(str);
|
||||||
|
}
|
||||||
|
value = joiner.toString();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
value = entry.getValue()[0];
|
||||||
|
}
|
||||||
|
parameters.add(key + "->" + value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(parameters.length() > 0){
|
||||||
|
log.info("Request parameters: {}", parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the path
|
||||||
|
MDC.put("url", request.getRequestURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
|
||||||
|
import com.mattrixwv.raidbuilder.config.TokenService;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
import com.mattrixwv.raidbuilder.service.AccountService;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/auth")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthenticationController{
|
||||||
|
private final ObjectMapper mapper;
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
@PostMapping("/token")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode token(Authentication authentication){
|
||||||
|
log.info("Token requested for user {}", authentication.getName());
|
||||||
|
|
||||||
|
|
||||||
|
Account account = accountService.getByUsername(authentication.getName());
|
||||||
|
String token = tokenService.generateAccessToken(account);
|
||||||
|
log.debug("Token granted {}", token);
|
||||||
|
ObjectNode tokenNode = mapper.valueToTree(account);
|
||||||
|
tokenNode.put("token", token);
|
||||||
|
|
||||||
|
|
||||||
|
return tokenNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/refresh")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode refresh(@RequestBody ObjectNode refreshTokenNode){
|
||||||
|
log.info("Refreshing token");
|
||||||
|
|
||||||
|
|
||||||
|
UUID refreshToken = UUID.fromString(refreshTokenNode.get("refreshToken").asText());
|
||||||
|
log.debug("refreshToken: {}", refreshToken);
|
||||||
|
|
||||||
|
Account account = accountService.getByRefreshToken(refreshToken);
|
||||||
|
|
||||||
|
if(account == null){
|
||||||
|
throw new AuthorizationDeniedException("Refresh token is invalid", () -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update login date
|
||||||
|
account.setLoginDate(ZonedDateTime.now());
|
||||||
|
account = accountService.updateAccount(account);
|
||||||
|
|
||||||
|
String token = tokenService.generateAccessToken(account);
|
||||||
|
log.debug("new token: {}", token);
|
||||||
|
ObjectNode tokenNode = mapper.valueToTree(account);
|
||||||
|
tokenNode.put("token", token);
|
||||||
|
|
||||||
|
return tokenNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/signup")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode signup(@RequestBody Account account){
|
||||||
|
log.info("Creating account {}", account.getUsername());
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
|
||||||
|
//Verify account object
|
||||||
|
List<String> errors = verifyNewAccount(account);
|
||||||
|
if(errors.isEmpty()){
|
||||||
|
//Create the account
|
||||||
|
account = accountService.createAccount(account);
|
||||||
|
|
||||||
|
returnNode.put("accountId", account.getAccountId().toString());
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
|
||||||
|
//TODO: Send email
|
||||||
|
|
||||||
|
log.info("Successfully created account: {}", account.getAccountId());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errors.forEach(errorsNode::add);
|
||||||
|
returnNode.set("errors", errorsNode);
|
||||||
|
|
||||||
|
log.info("Error creating account: {}", errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/confirm")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode confirm(@RequestBody ObjectNode confirmNode){
|
||||||
|
UUID confirmToken = UUID.fromString(confirmNode.get("confirmToken").asText());
|
||||||
|
log.info("Confirming account with token {}", confirmToken);
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
|
||||||
|
//Verify token
|
||||||
|
Account account = accountService.getByRefreshToken(confirmToken);
|
||||||
|
log.debug("Found account: {}", account);
|
||||||
|
if((account != null) && (account.getAccountStatus() == AccountStatus.UNCONFIRMED) && (account.getRefreshTokenExpiration().isAfter(ZonedDateTime.now()))){
|
||||||
|
accountService.confirmAccount(account);
|
||||||
|
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add("Account is not unconfirmed");
|
||||||
|
returnNode.set("errors", errorsNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/forgot")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode forgot(@RequestParam("loginId") String loginId){
|
||||||
|
log.info("Setting up user that forgot their password: {}", loginId);
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
|
||||||
|
//Verify the account exists
|
||||||
|
Account account = accountService.getByUsername(loginId);
|
||||||
|
if(account != null){
|
||||||
|
//Setup token
|
||||||
|
UUID token = UUID.randomUUID();
|
||||||
|
account.setRefreshToken(token);
|
||||||
|
account.setRefreshTokenExpiration(ZonedDateTime.now().plusHours(1));
|
||||||
|
account.setAccountStatus(AccountStatus.LOCKED);
|
||||||
|
|
||||||
|
account = accountService.updateAccount(account);
|
||||||
|
|
||||||
|
//TODO: Send email
|
||||||
|
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add("Could not find account with login " + loginId);
|
||||||
|
returnNode.set("errors", errorsNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/forgot/reset")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode setNewPasswordForgot(@RequestBody ObjectNode forgotNode){
|
||||||
|
UUID forgotToken = UUID.fromString(forgotNode.get("forgotToken").asText());
|
||||||
|
String newPassword = forgotNode.get("password").asText();
|
||||||
|
log.info("Confirming user reset password (forget) with token {}", forgotToken);
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
|
||||||
|
|
||||||
|
//Verify the account exists
|
||||||
|
Account existingAccount = accountService.getByRefreshToken(forgotToken);
|
||||||
|
if(existingAccount != null){
|
||||||
|
existingAccount = accountService.updatePassword(existingAccount.getAccountId(), newPassword);
|
||||||
|
|
||||||
|
existingAccount.setRefreshToken(null);
|
||||||
|
existingAccount.setRefreshTokenExpiration(null);
|
||||||
|
existingAccount.setAccountStatus(AccountStatus.ACTIVE);
|
||||||
|
existingAccount = accountService.updateAccount(existingAccount);
|
||||||
|
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add("Invalid token");
|
||||||
|
returnNode.set("errors", errorsNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/resetPassword")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode resetPassword(Authentication authentication, @RequestBody ObjectNode requestNode){
|
||||||
|
log.info("Resetting password for {}", authentication.getName());
|
||||||
|
|
||||||
|
|
||||||
|
if((requestNode == null) || (!requestNode.has("password"))){
|
||||||
|
throw new IllegalArgumentException("Invalid request");
|
||||||
|
}
|
||||||
|
|
||||||
|
Account account = accountService.getByUsername(authentication.getName());
|
||||||
|
accountService.updatePassword(account.getAccountId(), requestNode.get("password").asText());
|
||||||
|
account.setForceReset(false);
|
||||||
|
accountService.updateAccount(account);
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
@AccountAuthorization(permissions = {})
|
||||||
|
public ObjectNode logout(Authentication authentication){
|
||||||
|
log.info("Logging out account {}", authentication.getName());
|
||||||
|
|
||||||
|
|
||||||
|
Account account = accountService.getByUsername(authentication.getName());
|
||||||
|
if(account != null){
|
||||||
|
account.setRefreshToken(null);
|
||||||
|
account.setRefreshTokenExpiration(null);
|
||||||
|
account = accountService.updateAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{accountId}/revokeRefreshToken")
|
||||||
|
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
|
||||||
|
public ObjectNode revokeRefreshToken(@PathVariable("accountId") UUID accountId){
|
||||||
|
log.info("Revoking refresh token for account {}", accountId);
|
||||||
|
|
||||||
|
|
||||||
|
Account account = accountService.getByAccountId(accountId);
|
||||||
|
account.setRefreshToken(null);
|
||||||
|
account.setRefreshTokenExpiration(null);
|
||||||
|
account = accountService.updateAccount(account);
|
||||||
|
|
||||||
|
ObjectNode returnNode = mapper.createObjectNode();
|
||||||
|
returnNode.put("status", "success");
|
||||||
|
returnNode.put("accountId", account.getAccountId().toString());
|
||||||
|
|
||||||
|
return returnNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<String> verifyNewAccount(Account account){
|
||||||
|
ArrayList<String> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
//Check ID
|
||||||
|
if(account.getAccountId() != null){
|
||||||
|
errors.add("Invalid Account ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check login exists in entity
|
||||||
|
if((account.getUsername() == null) || (account.getUsername().isEmpty())){
|
||||||
|
errors.add("Invalid Login ID");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//Check login doesn't exist in db
|
||||||
|
Account existingAccount = accountService.getByUsername(account.getUsername());
|
||||||
|
if(existingAccount != null){
|
||||||
|
errors.add("Login ID already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check password exists in entity
|
||||||
|
if((account.getPassword() == null) || (account.getPassword().isEmpty())){
|
||||||
|
errors.add("No password found");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check email exists in entity
|
||||||
|
if((account.getEmail() == null) || (account.getEmail().isEmpty())){
|
||||||
|
errors.add("Invalid email");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//Check email doesn't exist in db
|
||||||
|
Account existingAccount = accountService.getByEmail(account.getEmail());
|
||||||
|
if(existingAccount != null){
|
||||||
|
errors.add("Account with email already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> verifyUpdatedAccount(Account account){
|
||||||
|
ArrayList<String> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
//Check ID
|
||||||
|
if(account.getAccountId() == null){
|
||||||
|
errors.add("Invalid Account ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Verify account exists
|
||||||
|
Account existingAccount = accountService.getByAccountId(account.getAccountId());
|
||||||
|
if(existingAccount == null){
|
||||||
|
errors.add("Account not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check login exists in entity
|
||||||
|
if((account.getUsername() == null) || (account.getUsername().isEmpty())){
|
||||||
|
errors.add("Invalid Login ID");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//Check login doesn't exist in db, other than the object itself
|
||||||
|
Account existingAccountByLogin = accountService.getByUsername(account.getUsername());
|
||||||
|
if((existingAccountByLogin != null) && (!existingAccountByLogin.getAccountId().equals(account.getAccountId()))){
|
||||||
|
errors.add("Login ID already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check email exists in entity
|
||||||
|
if((account.getEmail() == null) || (account.getEmail().isEmpty())){
|
||||||
|
errors.add("Invalid email");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//Check email doesn't exist in db, other than the object itself
|
||||||
|
Account existingAccountByEmail = accountService.getByEmail(account.getEmail());
|
||||||
|
if((existingAccountByEmail != null) && (!existingAccountByEmail.getAccountId().equals(account.getAccountId()))){
|
||||||
|
errors.add("Account with email already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.mattrixwv.raidbuilder.exception.MissingAuthorizationException;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class ExceptionController{
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
@ResponseBody
|
||||||
|
public ObjectNode genericExceptionHandler(Exception error){
|
||||||
|
log.error(error.getMessage(), error);
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnJson = mapper.createObjectNode();
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add(error.getMessage());
|
||||||
|
returnJson.set("errors", errorsNode);
|
||||||
|
returnJson.put("status", "error");
|
||||||
|
|
||||||
|
|
||||||
|
return returnJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
@ResponseBody
|
||||||
|
public ObjectNode authorizationExceptionHandler(AuthorizationDeniedException error){
|
||||||
|
log.info(error.getMessage());
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnJson = mapper.createObjectNode();
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add(error.getMessage());
|
||||||
|
returnJson.set("errors", errorsNode);
|
||||||
|
returnJson.put("status", "error");
|
||||||
|
|
||||||
|
|
||||||
|
return returnJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MissingAuthorizationException.class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
@ResponseBody
|
||||||
|
public ObjectNode missingAuthorizationExceptionHandler(MissingAuthorizationException error){
|
||||||
|
log.info(error.getMessage());
|
||||||
|
|
||||||
|
|
||||||
|
ObjectNode returnJson = mapper.createObjectNode();
|
||||||
|
ArrayNode errorsNode = mapper.createArrayNode();
|
||||||
|
errorsNode.add("Server is misconfigured");
|
||||||
|
returnJson.set("errors", errorsNode);
|
||||||
|
returnJson.put("status", "error");
|
||||||
|
|
||||||
|
|
||||||
|
return returnJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/main/java/com/mattrixwv/raidbuilder/entity/Account.java
Normal file
56
src/main/java/com/mattrixwv/raidbuilder/entity/Account.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "account", schema = "raid_builder")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Account{
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private UUID accountId;
|
||||||
|
@Column(name = "username")
|
||||||
|
private String username;
|
||||||
|
@Column(name = "password")
|
||||||
|
private String password;
|
||||||
|
@Column(name = "login_date")
|
||||||
|
private ZonedDateTime loginDate;
|
||||||
|
@Column(name = "email")
|
||||||
|
private String email;
|
||||||
|
@Column(name = "force_reset")
|
||||||
|
private boolean forceReset;
|
||||||
|
@Column(name = "refresh_token")
|
||||||
|
private UUID refreshToken;
|
||||||
|
@Column(name = "refresh_token_expiration")
|
||||||
|
private ZonedDateTime refreshTokenExpiration;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "account_status")
|
||||||
|
private AccountStatus accountStatus;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public String getPassword(){ return password; }
|
||||||
|
@JsonProperty
|
||||||
|
public void setPassword(String password){ this.password = password; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "account_permission", schema = "raid_builder")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AccountPermission implements GrantedAuthority{
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Column(name = "account_permission_id")
|
||||||
|
private UUID accountPermissionId;
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private UUID accountId;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "account_permission_type")
|
||||||
|
private AccountPermissionType accountPermissionType;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority(){
|
||||||
|
return accountPermissionType.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.TutorialStatus;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "account_tutorial_status", schema = "raid_builder")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AccountTutorialStatus{
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Column(name = "account_tutorial_status_id")
|
||||||
|
private UUID accountTutorialStatusId;
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private UUID accountId;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "games_tutorial_status")
|
||||||
|
private TutorialStatus gamesTutorialStatus;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "game_tutorial_status")
|
||||||
|
private TutorialStatus gameTutorialStatus;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "raid_groups_tutorial_status")
|
||||||
|
private TutorialStatus raidGroupsTutorialStatus;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "raid_group_tutorial_status")
|
||||||
|
private TutorialStatus raidGroupTutorialStatus;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "instance_tutorial_status")
|
||||||
|
private TutorialStatus instanceTutorialStatus;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.data.annotation.CreatedBy;
|
||||||
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
|
import org.springframework.data.annotation.LastModifiedBy;
|
||||||
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public abstract class AuditableEntity{
|
||||||
|
@JsonIgnore
|
||||||
|
@LastModifiedBy
|
||||||
|
@Column(name = "modified_by")
|
||||||
|
protected UUID modifiedBy;
|
||||||
|
@JsonIgnore
|
||||||
|
@LastModifiedDate
|
||||||
|
@Column(name = "modified_date")
|
||||||
|
protected ZonedDateTime modifiedDate;
|
||||||
|
@JsonIgnore
|
||||||
|
@CreatedBy
|
||||||
|
@Column(name = "created_by", updatable = false)
|
||||||
|
protected UUID createdBy;
|
||||||
|
@JsonIgnore
|
||||||
|
@CreatedDate
|
||||||
|
@Column(name = "created_date", updatable = false)
|
||||||
|
protected ZonedDateTime createdDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.service.AccountService;
|
||||||
|
|
||||||
|
import jakarta.persistence.PrePersist;
|
||||||
|
import jakarta.persistence.PreUpdate;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuditableEntityListener{
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void prePersist(AuditableEntity entity){
|
||||||
|
entity.setCreatedBy(getCurrentUserId());
|
||||||
|
entity.setCreatedDate(ZonedDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
public void preUpdate(AuditableEntity entity){
|
||||||
|
entity.setModifiedBy(getCurrentUserId());
|
||||||
|
entity.setModifiedDate(ZonedDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private UUID getCurrentUserId(){
|
||||||
|
log.debug("Getting current auditor");
|
||||||
|
|
||||||
|
|
||||||
|
UUID returnUUID;
|
||||||
|
|
||||||
|
try{
|
||||||
|
UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
returnUUID = accountService.getByUsername(userDetails.getUsername()).getAccountId();
|
||||||
|
}
|
||||||
|
catch(Exception e){
|
||||||
|
returnUUID = UUID.fromString("382b1ed8-7d5a-4683-a25d-1f462e9cd921");
|
||||||
|
log.debug("No user logged in: {}", returnUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnUUID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.exception;
|
||||||
|
|
||||||
|
|
||||||
|
public class MissingAuthorizationException extends RuntimeException{
|
||||||
|
public MissingAuthorizationException(){
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissingAuthorizationException(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissingAuthorizationException(Throwable cause){
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissingAuthorizationException(String message, Throwable cause){
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountRepository extends AccountCustomRepository, JpaRepository<Account, UUID>{
|
||||||
|
public Account findByUsername(String username);
|
||||||
|
public Account findByRefreshToken(UUID refreshToken);
|
||||||
|
public Account findByEmail(String email);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class AccountRepositoryImpl implements AccountCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_permission;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountPermissionCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_permission;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountPermission;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountPermissionRepository extends AccountPermissionCustomRepository, JpaRepository<AccountPermission, UUID>{
|
||||||
|
public List<AccountPermission> findAllByAccountId(UUID accountId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_permission;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class AccountPermissionRepositoryImpl implements AccountPermissionCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountTutorialStatusCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountTutorialStatus;
|
||||||
|
|
||||||
|
|
||||||
|
public interface AccountTutorialStatusRepository extends AccountTutorialStatusCustomRepository, JpaRepository<AccountTutorialStatus, UUID>{
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class AccountTutorialStatusRepositoryImpl implements AccountTutorialStatusCustomRepository{
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.service;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountPermission;
|
||||||
|
import com.mattrixwv.raidbuilder.repository.account_permission.AccountPermissionRepository;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountPermissionService{
|
||||||
|
private final AccountPermissionRepository accountPermissionRepository;
|
||||||
|
|
||||||
|
|
||||||
|
public AccountPermission createAccountPermission(AccountPermission accountPermission){
|
||||||
|
return accountPermissionRepository.save(accountPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<AccountPermission> getByAccountId(UUID accountId){
|
||||||
|
return accountPermissionRepository.findAllByAccountId(accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.service;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountPermission;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountTutorialStatus;
|
||||||
|
import com.mattrixwv.raidbuilder.repository.account.AccountRepository;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
|
||||||
|
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.TutorialStatus;
|
||||||
|
import com.mattrixwv.raidbuilder.util.UserPrincipal;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountService implements UserDetailsService{
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final AccountRepository accountRepository;
|
||||||
|
private final AccountTutorialStatusService accountTutorialStatusService;
|
||||||
|
//Related services
|
||||||
|
private final AccountPermissionService accountPermissionService;
|
||||||
|
//Fields
|
||||||
|
@Value("${jwt.refreshTokenDuration}")
|
||||||
|
private Duration refreshTokenDuration;
|
||||||
|
|
||||||
|
|
||||||
|
//Write
|
||||||
|
public Account createAccount(Account account){
|
||||||
|
//Set default values
|
||||||
|
account.setAccountStatus(AccountStatus.UNCONFIRMED);
|
||||||
|
account.setPassword(passwordEncoder.encode(account.getPassword()));
|
||||||
|
account.setRefreshToken(UUID.randomUUID());
|
||||||
|
account.setRefreshTokenExpiration(ZonedDateTime.now().plus(refreshTokenDuration));
|
||||||
|
|
||||||
|
//Save account
|
||||||
|
account = accountRepository.save(account);
|
||||||
|
|
||||||
|
//Return the new account
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account confirmAccount(Account account){
|
||||||
|
//Setup the confirmed values
|
||||||
|
account.setRefreshToken(null);
|
||||||
|
account.setRefreshTokenExpiration(null);
|
||||||
|
account.setAccountStatus(AccountStatus.ACTIVE);
|
||||||
|
|
||||||
|
//Save the account
|
||||||
|
account = accountRepository.save(account);
|
||||||
|
|
||||||
|
//Give account default permissions
|
||||||
|
AccountPermission accountPermission = new AccountPermission();
|
||||||
|
accountPermission.setAccountId(account.getAccountId());
|
||||||
|
accountPermission.setAccountPermissionType(AccountPermissionType.USER);
|
||||||
|
accountPermission = accountPermissionService.createAccountPermission(accountPermission);
|
||||||
|
|
||||||
|
//Give account default tutorial actions
|
||||||
|
AccountTutorialStatus accountTutorialStatus = new AccountTutorialStatus();
|
||||||
|
accountTutorialStatus.setAccountId(account.getAccountId());
|
||||||
|
accountTutorialStatus.setGamesTutorialStatus(TutorialStatus.NOT_COMPLETED);
|
||||||
|
accountTutorialStatus.setGameTutorialStatus(TutorialStatus.NOT_COMPLETED);
|
||||||
|
accountTutorialStatus.setRaidGroupsTutorialStatus(TutorialStatus.NOT_COMPLETED);
|
||||||
|
accountTutorialStatus.setRaidGroupTutorialStatus(TutorialStatus.NOT_COMPLETED);
|
||||||
|
accountTutorialStatus.setInstanceTutorialStatus(TutorialStatus.NOT_COMPLETED);
|
||||||
|
accountTutorialStatus = accountTutorialStatusService.createAccountTutorialStatus(accountTutorialStatus);
|
||||||
|
|
||||||
|
//Return the account
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account updateAccount(Account account){
|
||||||
|
return accountRepository.save(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account updatePassword(UUID accountId, String password){
|
||||||
|
Account account = accountRepository.findById(accountId).orElse(null);
|
||||||
|
|
||||||
|
if(account != null){
|
||||||
|
account.setPassword(passwordEncoder.encode(password));
|
||||||
|
account = accountRepository.save(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Read
|
||||||
|
public Account getByAccountId(UUID accountId){
|
||||||
|
return accountRepository.findById(accountId).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getByUsername(String username){
|
||||||
|
return accountRepository.findByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getByEmail(String email){
|
||||||
|
return accountRepository.findByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getByRefreshToken(UUID refreshToken){
|
||||||
|
return accountRepository.findByRefreshToken(refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! UserDetailsService
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username){
|
||||||
|
Account account = accountRepository.findByUsername(username);
|
||||||
|
|
||||||
|
//If no account with that username exists, throw an exception
|
||||||
|
if(account == null){
|
||||||
|
throw new UsernameNotFoundException(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update the login timestamp and refresh token
|
||||||
|
account.setLoginDate(ZonedDateTime.now());
|
||||||
|
account.setRefreshToken(UUID.randomUUID());
|
||||||
|
account.setRefreshTokenExpiration(ZonedDateTime.now().plus(refreshTokenDuration));
|
||||||
|
account = accountRepository.save(account);
|
||||||
|
|
||||||
|
//Get the account permissions
|
||||||
|
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
|
||||||
|
|
||||||
|
return new UserPrincipal(account, accountPermissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.service;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountTutorialStatus;
|
||||||
|
import com.mattrixwv.raidbuilder.repository.account_tutorial_status.AccountTutorialStatusRepository;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountTutorialStatusService{
|
||||||
|
private final AccountTutorialStatusRepository accountTutorialStatusRepository;
|
||||||
|
|
||||||
|
|
||||||
|
public AccountTutorialStatus createAccountTutorialStatus(AccountTutorialStatus accountTutorialStatus){
|
||||||
|
return accountTutorialStatusRepository.save(accountTutorialStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.util;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class DatabaseTypeUtil{
|
||||||
|
public static enum AccountStatus {
|
||||||
|
ACTIVE,
|
||||||
|
LOCKED,
|
||||||
|
INACTIVE,
|
||||||
|
DELETED,
|
||||||
|
UNCONFIRMED
|
||||||
|
};
|
||||||
|
|
||||||
|
public static enum AccountPermissionType {
|
||||||
|
ADMIN,
|
||||||
|
USER
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum TutorialStatus {
|
||||||
|
COMPLETED,
|
||||||
|
NOT_COMPLETED
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.mattrixwv.raidbuilder.util;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import com.mattrixwv.raidbuilder.entity.Account;
|
||||||
|
import com.mattrixwv.raidbuilder.entity.AccountPermission;
|
||||||
|
|
||||||
|
|
||||||
|
public class UserPrincipal implements UserDetails{
|
||||||
|
private Account account;
|
||||||
|
private List<AccountPermission> accountPermissions;
|
||||||
|
|
||||||
|
|
||||||
|
public UserPrincipal(@NonNull Account account, @NonNull List<AccountPermission> accountPermissions){
|
||||||
|
this.account = account;
|
||||||
|
this.accountPermissions = accountPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername(){
|
||||||
|
return account.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword(){
|
||||||
|
return account.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities(){
|
||||||
|
return accountPermissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "jwt",
|
||||||
|
"description": "Properties for JWT management"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "allowed-origins",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Allowed CORS origins",
|
||||||
|
"defaultValue": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jwt.access-token-duration",
|
||||||
|
"type": "java.time.Duration",
|
||||||
|
"description": "The duration for which the access token is valid",
|
||||||
|
"defaultValue": "900s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jwt.refresh-token-duration",
|
||||||
|
"type": "java.time.Duration",
|
||||||
|
"description": "The duration for which the refresh token is valid",
|
||||||
|
"defaultValue": "30d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hints": [
|
||||||
|
{
|
||||||
|
"name": "allowed-origins",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"value": "http://localhost:3000",
|
||||||
|
"description": "Local development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "https://api.raidbuilder.mattrixwv.com",
|
||||||
|
"description": "Production"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
21
src/main/resources/application.properties
Normal file
21
src/main/resources/application.properties
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#Server options
|
||||||
|
server.error.include-stacktrace=always
|
||||||
|
server.port=8001
|
||||||
|
server.shutdown=graceful
|
||||||
|
spring.lifecycle.timeoutPerShutdownPhase=10s
|
||||||
|
|
||||||
|
#Database options
|
||||||
|
spring.jpa.openInView=false
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/db_name?stringtype=unspecified
|
||||||
|
spring.datasource.username=username
|
||||||
|
spring.datasource.password=password
|
||||||
|
|
||||||
|
#CORS
|
||||||
|
#allowedOrigins=http://localhost:3000
|
||||||
|
allowedOrigins=*
|
||||||
|
|
||||||
|
#JWT
|
||||||
|
rsa.privateKey=classpath:certs/private.pem
|
||||||
|
rsa.publicKey=classpath:certs/public.pem
|
||||||
|
jwt.accessTokenDuration=15m
|
||||||
|
jwt.refreshTokenDuration=30d
|
||||||
28
src/main/resources/certs/keypair.pem
Normal file
28
src/main/resources/certs/keypair.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTPsOQkPNJbPi4
|
||||||
|
B2dlDKunPOL1Ehf7DXLDSablGzsunJk366or5AI1QbiCGeKU/jhTjsbZxXfcBVgt
|
||||||
|
n4BWxnaY6Ypay344LI/YYuSRieENcvZZuz9+kFwACpbO8+NIW1pGiPczOpUzFcSn
|
||||||
|
4BD36RnukeFOyDCiDlK82PF37hU/v79DAdQ7UAwK7M5xuzUbdRGebI7wRr++qbz5
|
||||||
|
9vcqLRqPY8JkEn0dmQ/7kOwRvqFBoCCVoVHThKPNWKxfkjTGKhrUGvAeUwl0A8MP
|
||||||
|
KNWDnLQ2HRfAS5UYh7BZ0Pp3CJupU6j3EzP+LNiKV7tQhZcGMcLclxjLNU6vMkS8
|
||||||
|
aiQFW7LfAgMBAAECggEAXEqBFZGGgQ9XcMtnTFonSocK3yg7CueauqBchp8JkblA
|
||||||
|
JZLUA533qv3eFxUpDZAt2q+3x+ACmEFLf48+emr12KO72yQprnAlnlPCaaV0CjSu
|
||||||
|
VZC90lVOpIP71EnwhCXJQKTJX3vaQHnjs7Zso2sXdcgNSCalPMAGPNSJVqzRYspf
|
||||||
|
mJxJbDn1iaZVfvBTsfbJQ02TXNA47UkPrQD/pBDegT34e0niLFTD4KY02OHSFg80
|
||||||
|
uKY/pGvGpdGuJkrkdv6wL5q81zDNp7YtGxFbVdtEGY3vLJgWwobI9sspnJKJ00Fr
|
||||||
|
HwYWUXPZQSwsU+I7jYI3/e+MRozola+EQCNmNJhXyQKBgQDm8t6h3jqkUq96+wXz
|
||||||
|
NYdMKSfE4zOk3CnZoq+iLXltC8B7Sns+y0+kuyHwlBsRGu1fQJwt2io2Vbxr94Qd
|
||||||
|
3YypprHE2WYDUFHwrHejcon8TwuB0zSllZE/zI+tWgma+1/hpCn4N7J8P37dRHTY
|
||||||
|
FvFU6U6qED3lSARjJo/tTbud9wKBgQDqKMEAzLdUy5opliwwG4+WAgtg2oNdWxFL
|
||||||
|
SFv1ErpHrlOILaoPVKb3Mfv68Ku/rpe6lmaNqasVLx1c3roh2nJhk8++bY+Y+JZu
|
||||||
|
25rIGO1BpcXobYzIQ7bjTYuvkFLDBmSy29NUaVyRT8h8114FwxurRSCif0jMp66v
|
||||||
|
KEjxjhp4WQKBgQCtM6vf/YhBQHm2Y5gcxCJJ7fuTX0mV9D+2ppnNqQkNzOh4Dm3L
|
||||||
|
tDJwup9Di++YrncjHpOCl8FcqoP6/NAqjcM2YHulw90L0ysAsnevLvFpNebNYJZ1
|
||||||
|
MGyUSlfejE3z214XHUUUkMDdCcmdK//tJ5eqNKb4R+IDmDUiHwOF1uxEFQKBgQDk
|
||||||
|
yRbykhLaXehtk5XvFy6u0aaOZlINx+nY1YVLqZWqbdCd8IgFXJ+aTRM3dylIKu2C
|
||||||
|
2GqxJULMevFEiTXx178ESeij1eaE/vX0sMrFkV1XVAJPe6IfFdI+usitq+TBOqDv
|
||||||
|
BMux4RQZwotQNxldpemF6Q/e1WCq3XdXGpRSt5ZzWQKBgCkzJi9/Cs4qn00iyhFC
|
||||||
|
h3dXfq3Des/ZYxGBJpw2/SAOBIti+QKQS7/UlPAml789cEO+B9w+dDrIENf4VJF0
|
||||||
|
0Zar2hI85bEnpUy+XeRwSo9vhgaYIox4k7B/FET4plqM1A6TyTGRy987CW9ua7ny
|
||||||
|
rQ9EYSbPs55Z4Rcv4O+DxAAL
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
28
src/main/resources/certs/private.pem
Normal file
28
src/main/resources/certs/private.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTPsOQkPNJbPi4
|
||||||
|
B2dlDKunPOL1Ehf7DXLDSablGzsunJk366or5AI1QbiCGeKU/jhTjsbZxXfcBVgt
|
||||||
|
n4BWxnaY6Ypay344LI/YYuSRieENcvZZuz9+kFwACpbO8+NIW1pGiPczOpUzFcSn
|
||||||
|
4BD36RnukeFOyDCiDlK82PF37hU/v79DAdQ7UAwK7M5xuzUbdRGebI7wRr++qbz5
|
||||||
|
9vcqLRqPY8JkEn0dmQ/7kOwRvqFBoCCVoVHThKPNWKxfkjTGKhrUGvAeUwl0A8MP
|
||||||
|
KNWDnLQ2HRfAS5UYh7BZ0Pp3CJupU6j3EzP+LNiKV7tQhZcGMcLclxjLNU6vMkS8
|
||||||
|
aiQFW7LfAgMBAAECggEAXEqBFZGGgQ9XcMtnTFonSocK3yg7CueauqBchp8JkblA
|
||||||
|
JZLUA533qv3eFxUpDZAt2q+3x+ACmEFLf48+emr12KO72yQprnAlnlPCaaV0CjSu
|
||||||
|
VZC90lVOpIP71EnwhCXJQKTJX3vaQHnjs7Zso2sXdcgNSCalPMAGPNSJVqzRYspf
|
||||||
|
mJxJbDn1iaZVfvBTsfbJQ02TXNA47UkPrQD/pBDegT34e0niLFTD4KY02OHSFg80
|
||||||
|
uKY/pGvGpdGuJkrkdv6wL5q81zDNp7YtGxFbVdtEGY3vLJgWwobI9sspnJKJ00Fr
|
||||||
|
HwYWUXPZQSwsU+I7jYI3/e+MRozola+EQCNmNJhXyQKBgQDm8t6h3jqkUq96+wXz
|
||||||
|
NYdMKSfE4zOk3CnZoq+iLXltC8B7Sns+y0+kuyHwlBsRGu1fQJwt2io2Vbxr94Qd
|
||||||
|
3YypprHE2WYDUFHwrHejcon8TwuB0zSllZE/zI+tWgma+1/hpCn4N7J8P37dRHTY
|
||||||
|
FvFU6U6qED3lSARjJo/tTbud9wKBgQDqKMEAzLdUy5opliwwG4+WAgtg2oNdWxFL
|
||||||
|
SFv1ErpHrlOILaoPVKb3Mfv68Ku/rpe6lmaNqasVLx1c3roh2nJhk8++bY+Y+JZu
|
||||||
|
25rIGO1BpcXobYzIQ7bjTYuvkFLDBmSy29NUaVyRT8h8114FwxurRSCif0jMp66v
|
||||||
|
KEjxjhp4WQKBgQCtM6vf/YhBQHm2Y5gcxCJJ7fuTX0mV9D+2ppnNqQkNzOh4Dm3L
|
||||||
|
tDJwup9Di++YrncjHpOCl8FcqoP6/NAqjcM2YHulw90L0ysAsnevLvFpNebNYJZ1
|
||||||
|
MGyUSlfejE3z214XHUUUkMDdCcmdK//tJ5eqNKb4R+IDmDUiHwOF1uxEFQKBgQDk
|
||||||
|
yRbykhLaXehtk5XvFy6u0aaOZlINx+nY1YVLqZWqbdCd8IgFXJ+aTRM3dylIKu2C
|
||||||
|
2GqxJULMevFEiTXx178ESeij1eaE/vX0sMrFkV1XVAJPe6IfFdI+usitq+TBOqDv
|
||||||
|
BMux4RQZwotQNxldpemF6Q/e1WCq3XdXGpRSt5ZzWQKBgCkzJi9/Cs4qn00iyhFC
|
||||||
|
h3dXfq3Des/ZYxGBJpw2/SAOBIti+QKQS7/UlPAml789cEO+B9w+dDrIENf4VJF0
|
||||||
|
0Zar2hI85bEnpUy+XeRwSo9vhgaYIox4k7B/FET4plqM1A6TyTGRy987CW9ua7ny
|
||||||
|
rQ9EYSbPs55Z4Rcv4O+DxAAL
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
9
src/main/resources/certs/public.pem
Normal file
9
src/main/resources/certs/public.pem
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0z7DkJDzSWz4uAdnZQyr
|
||||||
|
pzzi9RIX+w1yw0mm5Rs7LpyZN+uqK+QCNUG4ghnilP44U47G2cV33AVYLZ+AVsZ2
|
||||||
|
mOmKWst+OCyP2GLkkYnhDXL2Wbs/fpBcAAqWzvPjSFtaRoj3MzqVMxXEp+AQ9+kZ
|
||||||
|
7pHhTsgwog5SvNjxd+4VP7+/QwHUO1AMCuzOcbs1G3URnmyO8Ea/vqm8+fb3Ki0a
|
||||||
|
j2PCZBJ9HZkP+5DsEb6hQaAglaFR04SjzVisX5I0xioa1BrwHlMJdAPDDyjVg5y0
|
||||||
|
Nh0XwEuVGIewWdD6dwibqVOo9xMz/izYile7UIWXBjHC3JcYyzVOrzJEvGokBVuy
|
||||||
|
3wIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
8
src/main/resources/generateCerts.sh
Normal file
8
src/main/resources/generateCerts.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Create RSA key pair
|
||||||
|
openssl genrsa -out keypair.pem 2048
|
||||||
|
|
||||||
|
#Extract public key
|
||||||
|
openssl rsa -in keypair.pem -pubout -out public.pem
|
||||||
|
|
||||||
|
#Create private key in PKCS#8 format
|
||||||
|
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out private.pem
|
||||||
39
src/main/resources/log4j2-spring.xml
Normal file
39
src/main/resources/log4j2-spring.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--<Configuration strict="true" xmlns="http://logging.apache.org/log4j/2.0/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://logging.apache.org/log4j/2.0/config https://raw.githubusercontent.com/apache/logging-log4j2/master/log4j-core/src/main/resources/Log4j-config.xsd">-->
|
||||||
|
<Configuration>
|
||||||
|
<Appenders>
|
||||||
|
<Console name="console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout disableAnsi="false">
|
||||||
|
<Pattern>
|
||||||
|
%style{%d{MM-dd-yyyy HH:mm:ss.SSSZ}}{bright, black} %highlight{%5p} %style{function=}{bright, black}%style{%50.50replace{%c{0}.%M}{}{}}{blue, bright} %style{requestId=%36.36X{requestId}}{bright, black} %style{username=%X{username}}{bright, black} %highlight{---} %message%n
|
||||||
|
</Pattern>
|
||||||
|
</PatternLayout>
|
||||||
|
</Console>
|
||||||
|
|
||||||
|
|
||||||
|
<RollingRandomAccessFile name="file" fileName="cipherStreamAPI.log" filePattern="%d{MM-dd-yyyy}-cipherStreamAPI.log.gz" immediateFlush="true">
|
||||||
|
<JsonTemplateLayout eventTemplateUri="classpath:template.json"></JsonTemplateLayout>
|
||||||
|
<Policies>
|
||||||
|
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||||
|
</Policies>
|
||||||
|
<DefaultRolloverStrategy max="20"/>
|
||||||
|
</RollingRandomAccessFile>
|
||||||
|
|
||||||
|
|
||||||
|
<Console name="consoleJSON" target="SYSTEM_OUT">
|
||||||
|
<JsonTemplateLayout eventTemplateUri="classpath:template.json"/>
|
||||||
|
</Console>
|
||||||
|
|
||||||
|
|
||||||
|
<Socket name="graylog" host="loggingpi.mattrixwv.com" port="1502">
|
||||||
|
<JsonTemplateLayout eventTemplateUri="classpath:template.json"/>
|
||||||
|
</Socket>
|
||||||
|
</Appenders>
|
||||||
|
|
||||||
|
<Loggers>
|
||||||
|
<Root level="info" includeLocation="true">
|
||||||
|
<AppenderRef ref="console"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
46
src/main/resources/template.json
Normal file
46
src/main/resources/template.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"msg_timestamp": {
|
||||||
|
"$resolver": "timestamp",
|
||||||
|
"pattern": {
|
||||||
|
"format": "yyyy-MM-dd HH:mm:ss.SSSZ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"$resolver": "level",
|
||||||
|
"field": "name"
|
||||||
|
},
|
||||||
|
"requestId": {
|
||||||
|
"$resolver": "mdc",
|
||||||
|
"key": "requestId"
|
||||||
|
},
|
||||||
|
"logger": {
|
||||||
|
"$resolver": "logger",
|
||||||
|
"field": "name"
|
||||||
|
},
|
||||||
|
"mdc":{
|
||||||
|
"$resolver": "mdc",
|
||||||
|
"flatten": false,
|
||||||
|
"stringified": false
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"$resolver": "message",
|
||||||
|
"stringified": true
|
||||||
|
},
|
||||||
|
"exception": {
|
||||||
|
"exception_class": {
|
||||||
|
"$resolver": "exception",
|
||||||
|
"field": "className"
|
||||||
|
},
|
||||||
|
"exception_message": {
|
||||||
|
"$resolver": "exception",
|
||||||
|
"field": "message"
|
||||||
|
},
|
||||||
|
"stacktrace": {
|
||||||
|
"$resolver": "exception",
|
||||||
|
"field": "stackTrace",
|
||||||
|
"stackTrace": {
|
||||||
|
"stringified": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
version-rules.xml
Normal file
17
version-rules.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ruleset xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" comparisonMethod="maven"
|
||||||
|
xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
|
||||||
|
<ignoreVersions>
|
||||||
|
<!-- Ignore Alpha's, Beta's, release candidates and milestones -->
|
||||||
|
<ignoreVersion type="regex">(?i).*Alpha(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*a(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*Beta(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*-B(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*RC(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*CR(?:-?\d+)?</ignoreVersion>
|
||||||
|
<ignoreVersion type="regex">(?i).*M(?:-?\d+)?</ignoreVersion>
|
||||||
|
</ignoreVersions>
|
||||||
|
<rules>
|
||||||
|
</rules>
|
||||||
|
</ruleset>
|
||||||
Reference in New Issue
Block a user