Compare commits
8 Commits
#376-ras-f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 43473ebffc | |||
| c5f37a9b5b | |||
| 371ba904e2 | |||
| 977187e0bb | |||
| 41ec2517a4 | |||
| bc804bedbc | |||
| d4eaad7361 | |||
| b072de9eca |
33
.gitignore
vendored
33
.gitignore
vendored
@ -1,33 +0,0 @@
|
|||||||
HELP.md
|
|
||||||
target/
|
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
|
||||||
!**/src/main/**/target/
|
|
||||||
!**/src/test/**/target/
|
|
||||||
|
|
||||||
### STS ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
.idea
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
build/
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
||||||
35
pom.xml
35
pom.xml
@ -30,6 +30,31 @@
|
|||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- FIXME these three are from the sample code-->
|
||||||
|
<!-- Spring Security and JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.9.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- OAuth2 Client -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web and JPA -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- FIXME these three are from the sample code-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
@ -95,6 +120,10 @@
|
|||||||
<artifactId>spring-rabbit-test</artifactId>
|
<artifactId>spring-rabbit-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testcontainers</groupId>
|
<groupId>org.testcontainers</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
@ -130,20 +159,20 @@
|
|||||||
<version>3.7.2</version>
|
<version>3.7.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.tinkerpop/gremlin-server
|
<!-- https://mvnrepository.com/artifact/org.apache.tinkerpop/gremlin-server -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.tinkerpop</groupId>
|
<groupId>org.apache.tinkerpop</groupId>
|
||||||
<artifactId>gremlin-server</artifactId>
|
<artifactId>gremlin-server</artifactId>
|
||||||
<version>3.7.2</version>
|
<version>3.7.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!- https://mvnrepository.com/artifact/org.opencypher/util-9.0
|
<!-- https://mvnrepository.com/artifact/org.opencypher/util-9.0 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.opencypher</groupId>
|
<groupId>org.opencypher</groupId>
|
||||||
<artifactId>util-9.0</artifactId>
|
<artifactId>util-9.0</artifactId>
|
||||||
<version>9.0.1</version>
|
<version>9.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--
|
||||||
leaving these here for now, documentation apparently lied about needing them...
|
leaving these here for now, documentation apparently lied about needing them...
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
package com.stktrk.app;
|
|
||||||
|
|
||||||
import com.arcadedb.gremlin.ArcadeGraph;
|
|
||||||
|
|
||||||
import com.github.javafaker.Faker;
|
|
||||||
import com.stktrk.app.configuration.GraphDbConfig;
|
|
||||||
import com.stktrk.app.db.ConnectionPool;
|
|
||||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
|
||||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/51221777/failed-to-configure-a-datasource-url-attribute-is-not-specified-and-no-embedd
|
|
||||||
@SpringBootApplication(exclude = {FlywayAutoConfiguration.class})
|
|
||||||
@RestController
|
|
||||||
public class AppApplication extends SpringBootServletInitializer {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(AppApplication.class, args);
|
|
||||||
}
|
|
||||||
// klkljgh
|
|
||||||
@GetMapping("/hello")
|
|
||||||
public String sayHello(@RequestParam(value = "myName", defaultValue = "World") String name) {
|
|
||||||
|
|
||||||
|
|
||||||
List<Vertex> x = List.of();
|
|
||||||
ArcadeGraph g = ConnectionPool.getGraph();
|
|
||||||
x = g.traversal().V().toList();
|
|
||||||
Vertex target = !x.isEmpty() ? x.get(new Random().nextInt(x.size())) : null;
|
|
||||||
Faker faker = new Faker();
|
|
||||||
Vertex res = g.traversal().addV("Profile").property("name", faker.name().firstName()).property("lastName", faker.name().lastName())
|
|
||||||
.next();
|
|
||||||
if (target != null) {
|
|
||||||
g.traversal().V(res.id()).addE("friend").to(target).iterate();
|
|
||||||
}
|
|
||||||
g.close();
|
|
||||||
|
|
||||||
x.add(res);
|
|
||||||
x.sort(Comparator.comparing(a -> a.id().toString()));
|
|
||||||
StringJoiner sj = new StringJoiner("<br />\n");
|
|
||||||
x.forEach(v -> sj.add(v.id().toString() + " - " + (String) v.property("name").value() + " " + (String) v.property("lastName").value()));
|
|
||||||
return "Hello, " + name + "! Added " + res.property("name").value() + ". There are " + x.size() + " vertices.<br/>\n" + sj;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/delete")
|
|
||||||
public String delete() {
|
|
||||||
ArcadeGraph g = ConnectionPool.getGraph();
|
|
||||||
g.traversal().V().hasLabel("Profile").drop().iterate();
|
|
||||||
g.close();
|
|
||||||
return "Done.";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package com.stktrk.app.db;
|
|
||||||
|
|
||||||
import com.arcadedb.gremlin.ArcadeGraph;
|
|
||||||
import com.arcadedb.gremlin.ArcadeGraphFactory;
|
|
||||||
import com.stktrk.app.configuration.GraphDbConfig;
|
|
||||||
import org.apache.commons.configuration2.Configuration;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class ConnectionPool {
|
|
||||||
static ArcadeGraphFactory pool;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
GraphDbConfig graphDbConfig;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
void init () {
|
|
||||||
try {
|
|
||||||
pool = ArcadeGraphFactory.withRemote(graphDbConfig.getHost(), graphDbConfig.getHttpPort(), graphDbConfig.getGraphName(), graphDbConfig.getUser(), graphDbConfig.getPassword());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ArcadeGraph getGraph() {
|
|
||||||
return pool.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
package com.stktrk.app.db.migrations;
|
|
||||||
|
|
||||||
import com.stktrk.app.configuration.GraphDbConfig;
|
|
||||||
import com.stktrk.app.db.MigrationWrapper;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
|
||||||
import org.flywaydb.core.api.migration.Context;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Data
|
|
||||||
public class V1__CreateTest extends BaseJavaMigration implements MigrationWrapper {
|
|
||||||
@Autowired
|
|
||||||
GraphDbConfig graphDbConfig;
|
|
||||||
|
|
||||||
public void migrate(Context context) throws Exception {
|
|
||||||
|
|
||||||
this.executeQuery(graphDbConfig,"CREATE VERTEX TYPE test");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
package com.stktrk.app.db.migrations;
|
|
||||||
|
|
||||||
import com.stktrk.app.configuration.GraphDbConfig;
|
|
||||||
import com.stktrk.app.db.MigrationWrapper;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
|
||||||
import org.flywaydb.core.api.migration.Context;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Data
|
|
||||||
public class V2__CreateUser extends BaseJavaMigration implements MigrationWrapper {
|
|
||||||
@Autowired
|
|
||||||
GraphDbConfig graphDbConfig;
|
|
||||||
|
|
||||||
public void migrate(Context context) throws Exception {
|
|
||||||
|
|
||||||
this.executeQuery(graphDbConfig,"CREATE VERTEX TYPE user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
src/main/java/com/stktrk/auth/AppApplication.java
Normal file
18
src/main/java/com/stktrk/auth/AppApplication.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/51221777/failed-to-configure-a-datasource-url-attribute-is-not-specified-and-no-embedd
|
||||||
|
@SpringBootApplication(exclude = {FlywayAutoConfiguration.class})
|
||||||
|
|
||||||
|
public class AppApplication extends SpringBootServletInitializer {
|
||||||
|
|
||||||
|
public static void main(@Nullable String[] args) {
|
||||||
|
SpringApplication.run(AppApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/main/java/com/stktrk/auth/CustomGrantedAuthority.java
Normal file
17
src/main/java/com/stktrk/auth/CustomGrantedAuthority.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
public class CustomGrantedAuthority implements GrantedAuthority {
|
||||||
|
|
||||||
|
private final String role;
|
||||||
|
|
||||||
|
public CustomGrantedAuthority(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return "ROLE_" + role.toUpperCase(); // Converts to Spring-standard role format
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/main/java/com/stktrk/auth/CustomOAuth2UserService.java
Normal file
54
src/main/java/com/stktrk/auth/CustomOAuth2UserService.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import com.stktrk.auth.domain.account.AccountService;
|
||||||
|
import com.stktrk.auth.domain.account.types.Account;
|
||||||
|
import com.stktrk.auth.domain.account.types.Role;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||||
|
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
@Nonnull public CustomOAuth2UserService(@Nonnull AccountService accountService) {
|
||||||
|
this.accountService = accountService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nonnull public OAuth2User loadUser(@Nonnull OAuth2UserRequest userRequest) {
|
||||||
|
OAuth2User oAuth2User = super.loadUser(userRequest);
|
||||||
|
|
||||||
|
Map<String, Object> attributes = oAuth2User.getAttributes();
|
||||||
|
String email = (String) attributes.get("email");
|
||||||
|
|
||||||
|
// Check if user exists, else create a new user
|
||||||
|
Account existingAccount = accountService.findByEmail(email);
|
||||||
|
if (existingAccount == null) {
|
||||||
|
try {
|
||||||
|
accountService.registerUser(email, "generated-password", List.of(Role.USER));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultOAuth2User(
|
||||||
|
Collections.singleton(() -> "ROLE_USER"),
|
||||||
|
attributes,
|
||||||
|
"id"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
47
src/main/java/com/stktrk/auth/JwtConfig.java
Normal file
47
src/main/java/com/stktrk/auth/JwtConfig.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
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 org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
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 java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class JwtConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeyPair keyPair() throws Exception {
|
||||||
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||||
|
keyPairGenerator.initialize(2048);
|
||||||
|
return keyPairGenerator.generateKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JWKSource<SecurityContext> jwkSource(KeyPair keyPair) {
|
||||||
|
RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
|
||||||
|
.privateKey((RSAPrivateKey) keyPair.getPrivate())
|
||||||
|
.keyID("my-key-id")
|
||||||
|
.build();
|
||||||
|
return new ImmutableJWKSet<>(new com.nimbusds.jose.jwk.JWKSet(rsaKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {
|
||||||
|
return new NimbusJwtEncoder(jwkSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtDecoder jwtDecoder(KeyPair keyPair) {
|
||||||
|
return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/com/stktrk/auth/RBACConfig.java
Normal file
18
src/main/java/com/stktrk/auth/RBACConfig.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RBACConfig {
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public String adminOnlyOperation() {
|
||||||
|
return "Admin operation success!";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
public String userOperation() {
|
||||||
|
return "User operation success!";
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/java/com/stktrk/auth/RoleAuthentication.java
Normal file
64
src/main/java/com/stktrk/auth/RoleAuthentication.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class RoleAuthentication implements Authentication {
|
||||||
|
private final List<String> roles;
|
||||||
|
private boolean authenticated = false;
|
||||||
|
|
||||||
|
public RoleAuthentication(List<String> roles) {
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "roles";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean implies(Subject subject) {
|
||||||
|
return Authentication.super.implies(subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return roles.stream()
|
||||||
|
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDetails() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAuthenticated() {
|
||||||
|
return authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
|
||||||
|
this.authenticated = isAuthenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement other methods as required
|
||||||
|
}
|
||||||
86
src/main/java/com/stktrk/auth/SecurityConfig.java
Normal file
86
src/main/java/com/stktrk/auth/SecurityConfig.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
// **SecurityConfig.java**
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
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.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity(prePostEnabled = true)
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Nonnull
|
||||||
|
private final JwtDecoder jwtDecoder;
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/public/**", "/auth/**", "/test/signup").permitAll()
|
||||||
|
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**").permitAll()
|
||||||
|
//.requestMatchers("/bearer/**").authenticated() ANNOTATION SEEMS TO WORK?
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
|
||||||
|
.addFilterBefore(new TokenValidatorFilter(jwtDecoder), UsernamePasswordAuthenticationFilter.class)
|
||||||
|
|
||||||
|
.oauth2Login(oauth2 -> oauth2
|
||||||
|
.defaultSuccessUrl("/auth/success")
|
||||||
|
.failureUrl("/auth/failure")
|
||||||
|
)
|
||||||
|
// FiXME !!!! this is a security risk.
|
||||||
|
|
||||||
|
.sessionManagement(session -> session
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
)
|
||||||
|
.csrf(csrf -> csrf.disable());
|
||||||
|
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||||
|
ClientRegistration facebookRegistration = ClientRegistration.withRegistrationId("facebook")
|
||||||
|
.clientId("your-facebook-client-id")
|
||||||
|
.clientSecret("your-facebook-client-secret")
|
||||||
|
.redirectUri("{baseUrl}/auth/login/oauth2/code/facebook")
|
||||||
|
.authorizationUri("https://www.facebook.com/v11.0/dialog/oauth")
|
||||||
|
.tokenUri("https://graph.facebook.com/v11.0/oauth/access_token")
|
||||||
|
.userInfoUri("https://graph.facebook.com/me?fields=id,name,email")
|
||||||
|
.userNameAttributeName("id")
|
||||||
|
.clientName("Facebook")
|
||||||
|
.scope("email", "public_profile")
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new InMemoryClientRegistrationRepository(facebookRegistration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
63
src/main/java/com/stktrk/auth/TokenValidatorFilter.java
Normal file
63
src/main/java/com/stktrk/auth/TokenValidatorFilter.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TokenValidatorFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtDecoder jwtDecoder;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String authHeader = request.getHeader("Authorization");
|
||||||
|
|
||||||
|
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||||
|
String token = authHeader.substring(7);
|
||||||
|
try {
|
||||||
|
// Decode and validate the JWT token
|
||||||
|
Jwt jwt = jwtDecoder.decode(token);
|
||||||
|
|
||||||
|
// Extract claims (e.g., role and username)
|
||||||
|
String username = jwt.getSubject();
|
||||||
|
List<String> roles = jwt.getClaim("role"); // Assumes your JWT has a 'role' claim
|
||||||
|
|
||||||
|
// Set up Spring Security's authentication
|
||||||
|
if (roles != null && !roles.isEmpty()) {
|
||||||
|
RoleAuthentication roleAuth = new RoleAuthentication(roles);
|
||||||
|
roleAuth.setAuthenticated(true);
|
||||||
|
SecurityContextHolder.getContext()
|
||||||
|
.setAuthentication(roleAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Log error and send unauthorized response
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with the filter chain
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/main/java/com/stktrk/auth/application/AuthController.java
Normal file
114
src/main/java/com/stktrk/auth/application/AuthController.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package com.stktrk.auth.application;
|
||||||
|
|
||||||
|
import com.stktrk.auth.domain.account.AccountService;
|
||||||
|
import com.stktrk.auth.domain.account.types.Account;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/auth")
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class AuthController {
|
||||||
|
@Nonnull
|
||||||
|
@Autowired
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Nonnull
|
||||||
|
private final JwtEncoder jwtEncoder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Nonnull
|
||||||
|
private final KeyPair keyPair;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success notification for social login
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@GetMapping("/success")
|
||||||
|
public String success() {
|
||||||
|
return "Login successful!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return failure notification for social login
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@GetMapping("/failure")
|
||||||
|
public String failure() {
|
||||||
|
return "Login failed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME this creates valid tokens on the fly, should be removed from production, or protected for local and dev use only
|
||||||
|
@PostMapping("/token")
|
||||||
|
public String generateToken(@RequestParam String username, @RequestParam String role) {
|
||||||
|
JwtClaimsSet claims = JwtClaimsSet.builder()
|
||||||
|
.issuer("auth-service")
|
||||||
|
.issuedAt(Instant.now())
|
||||||
|
.expiresAt(Instant.now().plusSeconds(3600))
|
||||||
|
.subject(username)
|
||||||
|
.claim("role", role)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Login to local account
|
||||||
|
*
|
||||||
|
* @param username usanername
|
||||||
|
* @param password password
|
||||||
|
* @return JWT token
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@PostMapping("/login")
|
||||||
|
public String login(@RequestParam String username, @RequestParam String password) {
|
||||||
|
Account account = accountService.findByEmail(username);
|
||||||
|
if (account == null || !accountService.verifyPassword(password, account.getPassword())) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Generate JWT
|
||||||
|
JwtClaimsSet claims = JwtClaimsSet.builder()
|
||||||
|
.issuer("auth-service")
|
||||||
|
.issuedAt(Instant.now())
|
||||||
|
.expiresAt(Instant.now().plusSeconds(3600))
|
||||||
|
.subject(username)
|
||||||
|
.claim("role", account.getRoles().stream().map ( r-> r.name()).toList())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
|
||||||
|
}
|
||||||
|
// check result in https://fusionauth.io/dev-tools/jwt-decoder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offers the possibility for other severs to get to the public key
|
||||||
|
* FIXME should probably not be a thing in production?
|
||||||
|
* @return public key used by the server
|
||||||
|
*/
|
||||||
|
@GetMapping("/publicKey")
|
||||||
|
public String publicKey() {
|
||||||
|
|
||||||
|
|
||||||
|
return "{\"public\":\"" + HexFormat.of().formatHex(keyPair.getPublic().getEncoded()) + "\"}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package com.stktrk.auth.application;
|
||||||
|
|
||||||
|
import com.stktrk.auth.domain.account.types.Role;
|
||||||
|
import com.stktrk.auth.domain.account.AccountService;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/test")
|
||||||
|
public class LocalSignupController {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
public LocalSignupController(@Nullable AccountService accountService) {
|
||||||
|
this.accountService = accountService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@PostMapping("/signup")
|
||||||
|
public String signUp(@Nonnull @RequestParam String email, @Nonnull @RequestParam String password, @Nonnull @RequestParam List<Role> roles) {
|
||||||
|
|
||||||
|
|
||||||
|
// Register the user
|
||||||
|
try {
|
||||||
|
accountService.registerUser(email, password, roles);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
return e.getMessage();
|
||||||
|
}
|
||||||
|
return "User registered successfully!";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.stktrk.auth.application;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/bearer")
|
||||||
|
public class TestController {
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
@Nonnull
|
||||||
|
@GetMapping ("/user")
|
||||||
|
public String isUser () {
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.stktrk.auth.domain.account;
|
||||||
|
|
||||||
|
import com.stktrk.auth.domain.account.types.Role;
|
||||||
|
import com.stktrk.auth.domain.account.types.Account;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
/**
|
||||||
|
* Profile repository
|
||||||
|
*/
|
||||||
|
public interface AccountRepository {
|
||||||
|
/**
|
||||||
|
* Check if a user profile exists, by email
|
||||||
|
* @param email account email
|
||||||
|
* @return account exits
|
||||||
|
*/
|
||||||
|
boolean accountExists(@Nonnull String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a single account, with roles
|
||||||
|
* @param email account email
|
||||||
|
* @return account, incl Roles.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Account findByEmail (@Nonnull String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new user account
|
||||||
|
*
|
||||||
|
* @param email account email serves as unique id
|
||||||
|
* @param password password.
|
||||||
|
* @param roles roles the account is created with.
|
||||||
|
*/
|
||||||
|
void createAccount(@Nonnull String email, @Nonnull String password, List<Role> roles);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package com.stktrk.auth.domain.account;
|
||||||
|
|
||||||
|
import com.stktrk.auth.domain.account.types.Role;
|
||||||
|
import com.stktrk.auth.domain.account.types.Account;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* account service
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AccountService {
|
||||||
|
@Nonnull
|
||||||
|
private final AccountRepository accountRepository;
|
||||||
|
@Nonnull
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* register a new user account
|
||||||
|
* @param email account email, serves as uniwue ID
|
||||||
|
* @param password password
|
||||||
|
* @param roles roles to be assigned to the account
|
||||||
|
* @throws SQLException thrown on duplicate account creation FIXME
|
||||||
|
*/
|
||||||
|
public void registerUser(@Nonnull String email, @Nonnull String password,@Nonnull List<Role> roles) throws SQLException {
|
||||||
|
|
||||||
|
String encodedPassword = passwordEncoder.encode(password);
|
||||||
|
if (accountRepository.accountExists(email)) {
|
||||||
|
throw new SQLException("User already exists.");
|
||||||
|
}
|
||||||
|
accountRepository.createAccount (email,encodedPassword, roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used during login to check password validity FIXME we should not expose the password to anywhere should we?
|
||||||
|
* @param rawPassword
|
||||||
|
* @param encodedPassword
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean verifyPassword(@Nonnull String rawPassword, @Nonnull String encodedPassword) {
|
||||||
|
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search account by email
|
||||||
|
* @param email account email
|
||||||
|
* @return account
|
||||||
|
*/
|
||||||
|
public Account findByEmail (@Nonnull String email) {
|
||||||
|
return accountRepository.findByEmail (email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.stktrk.auth.domain.account.types;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class Account {
|
||||||
|
@Nonnull
|
||||||
|
private final String email;
|
||||||
|
@Nullable
|
||||||
|
private final String password;
|
||||||
|
@Nullable private final List<Role> roles;
|
||||||
|
@Nullable private final String id;
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.stktrk.auth.domain.account.types;
|
||||||
|
|
||||||
|
public enum Role {
|
||||||
|
ADMIN,
|
||||||
|
USER
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
package com.stktrk.auth.integration.account;
|
||||||
|
|
||||||
|
import com.arcadedb.gremlin.ArcadeGraph;
|
||||||
|
import com.stktrk.auth.domain.account.types.Role;
|
||||||
|
import com.stktrk.auth.domain.account.AccountRepository;
|
||||||
|
import com.stktrk.auth.domain.account.types.Account;
|
||||||
|
import com.stktrk.auth.integration.flyway.ConnectionPool;
|
||||||
|
import com.stktrk.auth.utils.EncryptId;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
|
||||||
|
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profile repository
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public class AccountRepositoryImpl implements AccountRepository {
|
||||||
|
/**
|
||||||
|
* Check if a user profile exists, by email
|
||||||
|
* @param email account email
|
||||||
|
* @return account exits
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accountExists(@Nonnull String email) {
|
||||||
|
ArcadeGraph g = ConnectionPool.getGraph();
|
||||||
|
List<? extends Vertex> accounts = g.traversal().V().hasLabel("Account").has("email", email).toList();
|
||||||
|
g.close();
|
||||||
|
|
||||||
|
return !accounts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a single account, with roles
|
||||||
|
* @param email account email
|
||||||
|
* @return account, incl Roles.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Account findByEmail(@Nonnull String email) {
|
||||||
|
ArcadeGraph g = ConnectionPool.getGraph();
|
||||||
|
List<Map<String, Object>> accounts = g.traversal().V().hasLabel("Account").has("email", email) .project("vertex" ,"roles") // removed "edges"
|
||||||
|
.by() // Include the vertex itself
|
||||||
|
//.by(outE("hasRole").fold()) // Include outgoing edges with "hasRole"
|
||||||
|
.by(outE("hasRole").inV().fold()) // Include target vertices
|
||||||
|
.toList();
|
||||||
|
g.close();
|
||||||
|
|
||||||
|
return accounts.isEmpty() ? null : buildAccount(accounts.getFirst());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new user account
|
||||||
|
*
|
||||||
|
* @param email account email serves as unique id
|
||||||
|
* @param password password.
|
||||||
|
* @param roles roles the account is created with.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void createAccount(@Nonnull String email, @Nonnull String password, List<Role> roles) {
|
||||||
|
ArcadeGraph g = ConnectionPool.getGraph();
|
||||||
|
|
||||||
|
// TODO remove inject, see if it still works
|
||||||
|
// FIXME we need to deal with roles properly.
|
||||||
|
GraphTraversal<Integer, Vertex> ts = g.traversal().inject(0)
|
||||||
|
.addV("Account")
|
||||||
|
.as("a")
|
||||||
|
.property("email", email)
|
||||||
|
.property("password", password);
|
||||||
|
|
||||||
|
roles.forEach(r -> {
|
||||||
|
ts.V().hasLabel("Role").has("name", r.name()).as(r.name())
|
||||||
|
.addE("hasRole").from ("a"). to (r.name());
|
||||||
|
});
|
||||||
|
|
||||||
|
ts.iterate();
|
||||||
|
g.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an account from the DB
|
||||||
|
* @param entry Map with the vettices for account and roles
|
||||||
|
* @return account
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private Account buildAccount(@Nonnull Map<String, Object> entry) {
|
||||||
|
|
||||||
|
Iterable<Vertex> targets = (Iterable<Vertex>) entry.get("roles");
|
||||||
|
List<Role> roles = StreamSupport.stream(targets.spliterator(), false)
|
||||||
|
.map (e -> e.property("name"))
|
||||||
|
.map (p ->(String) p.value())
|
||||||
|
.map (Role::valueOf)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
Vertex v = (Vertex) entry.get("vertex");
|
||||||
|
|
||||||
|
// FIXMe we should probably ot return the password?
|
||||||
|
return Account.builder()
|
||||||
|
.email((String) v.property("email").value())
|
||||||
|
.password((String) v.property("password").value())
|
||||||
|
.id(EncryptId.encrypt((String) v.id()))
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package com.stktrk.app.configuration;
|
package com.stktrk.auth.integration.configuration.types;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
@ -13,11 +12,15 @@ import org.springframework.stereotype.Component;
|
|||||||
@ConfigurationProperties(prefix = "graph")
|
@ConfigurationProperties(prefix = "graph")
|
||||||
|
|
||||||
public class GraphDbConfig {
|
public class GraphDbConfig {
|
||||||
String host;
|
@Nonnull
|
||||||
int httpPort;
|
String host;
|
||||||
int postgresPort;
|
int httpPort;
|
||||||
String graphName;
|
int postgresPort;
|
||||||
String user;
|
@Nonnull
|
||||||
String password;
|
String graphName;
|
||||||
|
@Nonnull
|
||||||
|
String user;
|
||||||
|
@Nonnull
|
||||||
|
String password;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.stktrk.auth.integration.flyway;
|
||||||
|
|
||||||
|
import com.arcadedb.database.Database;
|
||||||
|
import com.arcadedb.database.DatabaseFactory;
|
||||||
|
import com.arcadedb.event.BeforeRecordCreateListener;
|
||||||
|
import com.arcadedb.gremlin.ArcadeGraph;
|
||||||
|
import com.arcadedb.gremlin.ArcadeGraphFactory;
|
||||||
|
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* provides connection pool for arcade DB
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ConnectionPool {
|
||||||
|
@Nullable
|
||||||
|
static ArcadeGraphFactory pool;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Nullable
|
||||||
|
GraphDbConfig graphDbConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* runautomatically after construction of bean, to late-grab config parameters
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
void init() {
|
||||||
|
pool = ArcadeGraphFactory.withRemote(graphDbConfig.getHost(),
|
||||||
|
graphDbConfig.getHttpPort(),
|
||||||
|
graphDbConfig.getGraphName(),
|
||||||
|
graphDbConfig.getUser(),
|
||||||
|
graphDbConfig.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides DB connection from pool
|
||||||
|
*
|
||||||
|
* @return graphFactory to run queries against
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static ArcadeGraph getGraph() {
|
||||||
|
return pool.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.stktrk.app.db;
|
package com.stktrk.auth.integration.flyway;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
import org.flywaydb.core.api.migration.JavaMigration;
|
import org.flywaydb.core.api.migration.JavaMigration;
|
||||||
@ -13,13 +14,17 @@ import java.sql.SQLException;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class FlywayConfig {
|
public class FlywayConfig {
|
||||||
|
// TODO move to properties
|
||||||
// Custom migration history table name
|
// Custom migration history table name
|
||||||
private static final String SCHEMA_HISTORY_TABLE = "flyway_schema_history";
|
@Nullable
|
||||||
|
private static final String SCHEMA_HISTORY_TABLE = "flyway_schema_history_auth_server";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Nullable
|
||||||
private DataSource dataSource;
|
private DataSource dataSource;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Nullable
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -35,7 +40,7 @@ public class FlywayConfig {
|
|||||||
Flyway flyway = Flyway.configure()
|
Flyway flyway = Flyway.configure()
|
||||||
.dataSource(dataSource) // No need to configure flyway JDBC URL by using the original DataSource
|
.dataSource(dataSource) // No need to configure flyway JDBC URL by using the original DataSource
|
||||||
.defaultSchema(dataSource.getConnection().getSchema())
|
.defaultSchema(dataSource.getConnection().getSchema())
|
||||||
.locations("db/migration") // Default migration script path
|
.locations("integration/flyway/migration") // Default migration script path
|
||||||
.table(SCHEMA_HISTORY_TABLE) // Default migration history table is `flyway_schema_history`
|
.table(SCHEMA_HISTORY_TABLE) // Default migration history table is `flyway_schema_history`
|
||||||
.baselineOnMigrate(true) // Default false, must be set to true for initial migration on existing databases
|
.baselineOnMigrate(true) // Default false, must be set to true for initial migration on existing databases
|
||||||
.baselineVersion("0") // Default "1"
|
.baselineVersion("0") // Default "1"
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.stktrk.app.db;
|
package com.stktrk.auth.integration.flyway;
|
||||||
|
|
||||||
import com.stktrk.app.configuration.GraphDbConfig;
|
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@ -9,8 +9,18 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper interface to reduce overhead on DB migration classes
|
||||||
|
*/
|
||||||
public interface MigrationWrapper {
|
public interface MigrationWrapper {
|
||||||
|
/**
|
||||||
|
* default method to allow easy application of arbitrary SQL
|
||||||
|
*
|
||||||
|
* @param graphDbConfig DB configuration
|
||||||
|
* @param query SQL query
|
||||||
|
* @throws ClassNotFoundException Driver missing
|
||||||
|
* @throws SQLException SQL error
|
||||||
|
*/
|
||||||
default void executeQuery(@Nonnull GraphDbConfig graphDbConfig, @Nonnull String query) throws ClassNotFoundException, SQLException {
|
default void executeQuery(@Nonnull GraphDbConfig graphDbConfig, @Nonnull String query) throws ClassNotFoundException, SQLException {
|
||||||
|
|
||||||
Class.forName("org.postgresql.Driver");
|
Class.forName("org.postgresql.Driver");
|
||||||
@ -21,8 +31,6 @@ public interface MigrationWrapper {
|
|||||||
props.setProperty("ssl", "false");
|
props.setProperty("ssl", "false");
|
||||||
props.setProperty("sslmode", "disable");
|
props.setProperty("sslmode", "disable");
|
||||||
|
|
||||||
System.out.println ( graphDbConfig.getHost());
|
|
||||||
|
|
||||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + graphDbConfig.getHost() + ":" + graphDbConfig.getPostgresPort() + "/" + graphDbConfig.getGraphName(), props)) {
|
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + graphDbConfig.getHost() + ":" + graphDbConfig.getPostgresPort() + "/" + graphDbConfig.getGraphName(), props)) {
|
||||||
Statement st = connection.createStatement();
|
Statement st = connection.createStatement();
|
||||||
st.execute(query);
|
st.execute(query);
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.stktrk.auth.integration.flyway.migrations;
|
||||||
|
|
||||||
|
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||||
|
import com.stktrk.auth.integration.flyway.MigrationWrapper;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
||||||
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Component
|
||||||
|
@Value
|
||||||
|
public class V1__CreateAccount extends BaseJavaMigration implements MigrationWrapper {
|
||||||
|
@Nonnull
|
||||||
|
@Autowired
|
||||||
|
GraphDbConfig graphDbConfig;
|
||||||
|
|
||||||
|
public void migrate(@Nonnull Context context) throws Exception {
|
||||||
|
this.executeQuery(graphDbConfig,"CREATE VERTEX TYPE Account IF NOT EXISTS; CREATE PROPERTY Account.email STRING (MANDATORY true, notnull true); CREATE PROPERTY Account.password STRING (MANDATORY true, notnull true);");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.stktrk.auth.integration.flyway.migrations;
|
||||||
|
|
||||||
|
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||||
|
import com.stktrk.auth.integration.flyway.MigrationWrapper;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
||||||
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Component
|
||||||
|
@Value
|
||||||
|
public class V2__CreateRoles extends BaseJavaMigration implements MigrationWrapper {
|
||||||
|
@Nonnull
|
||||||
|
@Autowired
|
||||||
|
GraphDbConfig graphDbConfig;
|
||||||
|
|
||||||
|
public void migrate(@Nonnull Context context) throws Exception {
|
||||||
|
this.executeQuery(graphDbConfig,"CREATE VERTEX TYPE Role IF NOT EXISTS; CREATE PROPERTY Role.name STRING (MANDATORY true, notnull true); CREATE VERTEX Role SET name = 'USER'; CREATE VERTEX Role SET name = 'ADMIN'; CREATE EDGE TYPE hasRole IF NOT EXISTS; create index on hasRole (`@in`, `@out`) UNIQUE ; create property hasRole.`@out` LINK OF Account; create property hasRole.`@in` LINK OF Role;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.stktrk.auth.integration.flyway.migrations;
|
||||||
|
|
||||||
|
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||||
|
import com.stktrk.auth.integration.flyway.MigrationWrapper;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.flywaydb.core.api.migration.BaseJavaMigration;
|
||||||
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Component
|
||||||
|
@Value
|
||||||
|
public class V3__427__BaseTypes extends BaseJavaMigration implements MigrationWrapper {
|
||||||
|
@Nonnull
|
||||||
|
@Autowired
|
||||||
|
GraphDbConfig graphDbConfig;
|
||||||
|
|
||||||
|
public void migrate(@Nonnull Context context) throws Exception {
|
||||||
|
this.executeQuery(graphDbConfig,"CREATE VERTEX TYPE V IF NOT EXISTS; CREATE PROPERTY V.createdOn DATETIME (MANDATORY true, notnull true); CREATE PROPERTY V.createdBy STRING (MANDATORY true); CREATE PROPERTY V.changedOn DATETIME; CREATE PROPERTY V.changedBy STRING; CREATE EDGE TYPE E IF NOT EXISTS; CREATE PROPERTY E.createdOn DATETIME (MANDATORY true, notnull true); CREATE PROPERTY E.createdBy STRING (MANDATORY true); CREATE PROPERTY E.changedOn DATETIME; CREATE PROPERTY E.changedBy STRING; ALTER TYPE Account SUPERTYPE +V; Alter Type Role SUPERTYPE +V; Alter Type hasRole SUPERTYPE +E");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
44
src/main/java/com/stktrk/auth/utils/EncryptId.java
Normal file
44
src/main/java/com/stktrk/auth/utils/EncryptId.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.stktrk.auth.utils;
|
||||||
|
|
||||||
|
import com.arcadedb.database.RID;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import org.hashids.Hashids;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
|
||||||
|
public class EncryptId {
|
||||||
|
|
||||||
|
// TODO move salt into properties
|
||||||
|
@Nonnull
|
||||||
|
private static final String SALT = "yG8kWVG0kei4wqwXGgt99AXxJWD7K2fnK4xv3BDR0M0AHchvFFCjFJK4VH1nLvm1";
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static String encrypt(@Nonnull RID rid) {
|
||||||
|
return new Hashids(SALT).encode(rid.getBucketId(), rid.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO illegal argument exception thrown from RID - if we only allow this to come from the DB, we should be good.
|
||||||
|
@Nonnull
|
||||||
|
public static String encrypt(@NotEmpty String rid) {
|
||||||
|
RID obj = new RID(rid);
|
||||||
|
return new Hashids(SALT).encode(obj.getBucketId(), obj.getPosition());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static RID decrypt(@NotEmpty String hash) throws InvalidKeyException {
|
||||||
|
long[] res = new Hashids(SALT).decode(hash);
|
||||||
|
if (res == null || res.length != 2 ) {
|
||||||
|
throw new InvalidKeyException("invalid id");
|
||||||
|
}
|
||||||
|
return new RID((int) res[0], res[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private Boolean isValidRid (String rid) {
|
||||||
|
return rid.matches("#\\d+\\.\\d+");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,25 +1,18 @@
|
|||||||
spring.application.name=app
|
spring.application.name=app
|
||||||
server.port=9090
|
server.port=9091
|
||||||
springdoc.swagger-ui.path=/swagger-ui.html
|
springdoc.swagger-ui.path=/swagger-ui.html
|
||||||
|
|
||||||
#spring.flyway.user=flyway_user
|
|
||||||
#spring.flyway.password=7e7v55UcYGrY0e3UPYI0qtyMA4YJ1ZkTEaoyZ252GluFkiEMHVT9U5ULS7Rg2rGi
|
|
||||||
#spring.flyway.schemas=flyway_db
|
|
||||||
#spring.flyway.url=jdbc:postgresql://192.168.178.50:7654/flyway_db
|
|
||||||
#spring.flyway.locations=classpath:com/stktrk/app/db/migrations
|
|
||||||
|
|
||||||
|
|
||||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||||
spring.datasource.url= jdbc:postgresql://localhost:7654/flyway_db
|
spring.datasource.url= jdbc:postgresql://192.168.178.50:7654/flyway_db
|
||||||
spring.datasource.username=flyway_user
|
spring.datasource.username=flyway_user
|
||||||
spring.datasource.password=stdUNmu7KTUQV6KGMKjzHIiwQ2gbipArvg02
|
spring.datasource.password=7e7v55UcYGrY0e3UPYI0qtyMA4YJ1ZkTEaoyZ252GluFkiEMHVT9U5ULS7Rg2rGi
|
||||||
spring.datasource.defaultSchema=flyway_db
|
spring.datasource.defaultSchema=flyway_db
|
||||||
spring.datasource.hikari.schema=flyway_db
|
spring.datasource.hikari.schema=flyway_db
|
||||||
|
|
||||||
graph.user=root
|
graph.user=root
|
||||||
graph.password=playwithdata
|
graph.password=playwithdata
|
||||||
graph.connection=jdbc:postgresql://192.168.178.29:5432/graph
|
graph.connection=jdbc:postgresql://192.168.178.50:5432/auth
|
||||||
graph.http-port:2480
|
graph.http-port:2480
|
||||||
graph.postgres-port:5432
|
graph.postgres-port:5432
|
||||||
graph.host=192.168.178.29
|
graph.host=192.168.178.50
|
||||||
graph.graph-name=graph
|
graph.graph-name=auth
|
||||||
@ -1,5 +1,5 @@
|
|||||||
spring.application.name=app
|
spring.application.name=auth
|
||||||
server.port=9090
|
server.port=9091
|
||||||
springdoc.swagger-ui.path=/swagger-ui.html
|
springdoc.swagger-ui.path=/swagger-ui.html
|
||||||
|
|
||||||
#spring.flyway.user=flyway_user
|
#spring.flyway.user=flyway_user
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.stktrk.app;
|
package com.stktrk.auth;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
@ -1,10 +1,11 @@
|
|||||||
package com.stktrk.app;
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
|
|
||||||
public class TestAppApplication {
|
public class TestAppApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(@Nonnull String[] args) {
|
||||||
SpringApplication.from(AppApplication::main).with(TestcontainersConfiguration.class).run(args);
|
SpringApplication.from(AppApplication::main).with(TestcontainersConfiguration.class).run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.stktrk.app;
|
package com.stktrk.auth;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import org.springframework.boot.test.context.TestConfiguration;
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -11,6 +12,7 @@ class TestcontainersConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ServiceConnection
|
@ServiceConnection
|
||||||
|
@Nonnull
|
||||||
RabbitMQContainer rabbitContainer() {
|
RabbitMQContainer rabbitContainer() {
|
||||||
return new RabbitMQContainer(DockerImageName.parse("rabbitmq:latest"));
|
return new RabbitMQContainer(DockerImageName.parse("rabbitmq:latest"));
|
||||||
}
|
}
|
||||||
22
src/test/java/com/stktrk/auth/utils/EncryptIdTest.java
Normal file
22
src/test/java/com/stktrk/auth/utils/EncryptIdTest.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package com.stktrk.auth.utils;
|
||||||
|
|
||||||
|
import com.arcadedb.database.RID;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
|
||||||
|
class EncryptIdTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void encrypt() {
|
||||||
|
Assertions.assertEquals("nOua", EncryptId.encrypt(new RID(1,2L)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void decrypt() throws InvalidKeyException {
|
||||||
|
Assertions.assertEquals(new RID(1,2L), EncryptId.decrypt("nOua"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user