Compare commits
No commits in common. "main" and "#376-ras-fix-pom-downgrade-undertow-arcade-bug" have entirely different histories.
main
...
#376-ras-f
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
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,31 +30,6 @@
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
@ -120,10 +95,6 @@
|
||||
<artifactId>spring-rabbit-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
@ -159,20 +130,20 @@
|
||||
<version>3.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.tinkerpop/gremlin-server -->
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.tinkerpop/gremlin-server
|
||||
<dependency>
|
||||
<groupId>org.apache.tinkerpop</groupId>
|
||||
<artifactId>gremlin-server</artifactId>
|
||||
<version>3.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.opencypher/util-9.0 -->
|
||||
<!- https://mvnrepository.com/artifact/org.opencypher/util-9.0
|
||||
<dependency>
|
||||
<groupId>org.opencypher</groupId>
|
||||
<artifactId>util-9.0</artifactId>
|
||||
<version>9.0.1</version>
|
||||
</dependency>
|
||||
<!--
|
||||
|
||||
leaving these here for now, documentation apparently lied about needing them...
|
||||
-->
|
||||
|
||||
|
||||
63
src/main/java/com/stktrk/app/AppApplication.java
Normal file
63
src/main/java/com/stktrk/app/AppApplication.java
Normal file
@ -0,0 +1,63 @@
|
||||
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,6 +1,7 @@
|
||||
package com.stktrk.auth.integration.configuration.types;
|
||||
package com.stktrk.app.configuration;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ -12,15 +13,11 @@ import org.springframework.stereotype.Component;
|
||||
@ConfigurationProperties(prefix = "graph")
|
||||
|
||||
public class GraphDbConfig {
|
||||
@Nonnull
|
||||
String host;
|
||||
int httpPort;
|
||||
int postgresPort;
|
||||
@Nonnull
|
||||
String graphName;
|
||||
@Nonnull
|
||||
String user;
|
||||
@Nonnull
|
||||
String password;
|
||||
|
||||
}
|
||||
32
src/main/java/com/stktrk/app/db/ConnectionPool.java
Normal file
32
src/main/java/com/stktrk/app/db/ConnectionPool.java
Normal file
@ -0,0 +1,32 @@
|
||||
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,6 +1,5 @@
|
||||
package com.stktrk.auth.integration.flyway;
|
||||
package com.stktrk.app.db;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.migration.JavaMigration;
|
||||
@ -14,17 +13,13 @@ import java.sql.SQLException;
|
||||
|
||||
@Configuration
|
||||
public class FlywayConfig {
|
||||
// TODO move to properties
|
||||
// Custom migration history table name
|
||||
@Nullable
|
||||
private static final String SCHEMA_HISTORY_TABLE = "flyway_schema_history_auth_server";
|
||||
private static final String SCHEMA_HISTORY_TABLE = "flyway_schema_history";
|
||||
|
||||
@Autowired
|
||||
@Nullable
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
@Nullable
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@PostConstruct
|
||||
@ -40,7 +35,7 @@ public class FlywayConfig {
|
||||
Flyway flyway = Flyway.configure()
|
||||
.dataSource(dataSource) // No need to configure flyway JDBC URL by using the original DataSource
|
||||
.defaultSchema(dataSource.getConnection().getSchema())
|
||||
.locations("integration/flyway/migration") // Default migration script path
|
||||
.locations("db/migration") // Default migration script path
|
||||
.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
|
||||
.baselineVersion("0") // Default "1"
|
||||
@ -1,6 +1,6 @@
|
||||
package com.stktrk.auth.integration.flyway;
|
||||
package com.stktrk.app.db;
|
||||
|
||||
import com.stktrk.auth.integration.configuration.types.GraphDbConfig;
|
||||
import com.stktrk.app.configuration.GraphDbConfig;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
import java.sql.Connection;
|
||||
@ -9,18 +9,8 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* helper interface to reduce overhead on DB migration classes
|
||||
*/
|
||||
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 {
|
||||
|
||||
Class.forName("org.postgresql.Driver");
|
||||
@ -31,6 +21,8 @@ public interface MigrationWrapper {
|
||||
props.setProperty("ssl", "false");
|
||||
props.setProperty("sslmode", "disable");
|
||||
|
||||
System.out.println ( graphDbConfig.getHost());
|
||||
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + graphDbConfig.getHost() + ":" + graphDbConfig.getPostgresPort() + "/" + graphDbConfig.getGraphName(), props)) {
|
||||
Statement st = connection.createStatement();
|
||||
st.execute(query);
|
||||
@ -0,0 +1,21 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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!";
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
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
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,114 +0,0 @@
|
||||
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()) + "\"}";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
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!";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
package com.stktrk.auth.domain.account.types;
|
||||
|
||||
public enum Role {
|
||||
ADMIN,
|
||||
USER
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
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,51 +0,0 @@
|
||||
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,24 +0,0 @@
|
||||
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);");
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
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;");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
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,18 +1,25 @@
|
||||
spring.application.name=app
|
||||
server.port=9091
|
||||
server.port=9090
|
||||
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.url= jdbc:postgresql://192.168.178.50:7654/flyway_db
|
||||
spring.datasource.url= jdbc:postgresql://localhost:7654/flyway_db
|
||||
spring.datasource.username=flyway_user
|
||||
spring.datasource.password=7e7v55UcYGrY0e3UPYI0qtyMA4YJ1ZkTEaoyZ252GluFkiEMHVT9U5ULS7Rg2rGi
|
||||
spring.datasource.password=stdUNmu7KTUQV6KGMKjzHIiwQ2gbipArvg02
|
||||
spring.datasource.defaultSchema=flyway_db
|
||||
spring.datasource.hikari.schema=flyway_db
|
||||
|
||||
graph.user=root
|
||||
graph.password=playwithdata
|
||||
graph.connection=jdbc:postgresql://192.168.178.50:5432/auth
|
||||
graph.connection=jdbc:postgresql://192.168.178.29:5432/graph
|
||||
graph.http-port:2480
|
||||
graph.postgres-port:5432
|
||||
graph.host=192.168.178.50
|
||||
graph.graph-name=auth
|
||||
graph.host=192.168.178.29
|
||||
graph.graph-name=graph
|
||||
@ -1,5 +1,5 @@
|
||||
spring.application.name=auth
|
||||
server.port=9091
|
||||
spring.application.name=app
|
||||
server.port=9090
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
|
||||
#spring.flyway.user=flyway_user
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.stktrk.auth;
|
||||
package com.stktrk.app;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@ -1,11 +1,10 @@
|
||||
package com.stktrk.auth;
|
||||
package com.stktrk.app;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
|
||||
public class TestAppApplication {
|
||||
|
||||
public static void main(@Nonnull String[] args) {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.from(AppApplication::main).with(TestcontainersConfiguration.class).run(args);
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.stktrk.auth;
|
||||
package com.stktrk.app;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -12,7 +11,6 @@ class TestcontainersConfiguration {
|
||||
|
||||
@Bean
|
||||
@ServiceConnection
|
||||
@Nonnull
|
||||
RabbitMQContainer rabbitContainer() {
|
||||
return new RabbitMQContainer(DockerImageName.parse("rabbitmq:latest"));
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
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