diff --git a/pom.xml b/pom.xml
index 876537c..7abe857 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,12 @@
spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
org.springframework.boot
diff --git a/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java b/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java
index 2d61c22..b9047ad 100644
--- a/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java
+++ b/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java
@@ -3,6 +3,7 @@ package net.siegeln.cameleer.saas.config;
import net.siegeln.cameleer.saas.auth.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
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;
@@ -17,23 +18,39 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@EnableMethodSecurity
public class SecurityConfig {
- private final JwtAuthenticationFilter jwtAuthenticationFilter;
+ private final JwtAuthenticationFilter machineTokenFilter;
- public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
- this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+ public SecurityConfig(JwtAuthenticationFilter machineTokenFilter) {
+ this.machineTokenFilter = machineTokenFilter;
}
@Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ @Order(1)
+ public SecurityFilterChain machineAuthFilterChain(HttpSecurity http) throws Exception {
+ http
+ .securityMatcher("/api/agent/**", "/api/license/verify/**")
+ .csrf(csrf -> csrf.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
+ .addFilterBefore(machineTokenFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+ @Bean
+ @Order(2)
+ public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
+ .requestMatchers("/auth/verify").permitAll()
.anyRequest().authenticated()
)
- .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> {}))
+ .addFilterBefore(machineTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
index eed0bb4..308b82c 100644
--- a/src/main/resources/application-test.yml
+++ b/src/main/resources/application-test.yml
@@ -3,3 +3,8 @@ spring:
show-sql: false
flyway:
clean-disabled: false
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ issuer-uri: https://test-issuer.example.com/oidc
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 6b2a4d8..8de3917 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -8,6 +8,12 @@ spring:
flyway:
enabled: true
locations: classpath:db/migration
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ issuer-uri: ${LOGTO_ISSUER_URI:}
+ jwk-set-uri: ${LOGTO_JWK_SET_URI:}
management:
endpoints:
@@ -23,3 +29,7 @@ cameleer:
expiration: 86400 # 24 hours in seconds
private-key-path: ${CAMELEER_JWT_PRIVATE_KEY_PATH:}
public-key-path: ${CAMELEER_JWT_PUBLIC_KEY_PATH:}
+ identity:
+ logto-endpoint: ${LOGTO_ENDPOINT:}
+ m2m-client-id: ${LOGTO_M2M_CLIENT_ID:}
+ m2m-client-secret: ${LOGTO_M2M_CLIENT_SECRET:}
diff --git a/src/test/java/net/siegeln/cameleer/saas/CameleerSaasApplicationTest.java b/src/test/java/net/siegeln/cameleer/saas/CameleerSaasApplicationTest.java
index 4b31ec8..70bfba7 100644
--- a/src/test/java/net/siegeln/cameleer/saas/CameleerSaasApplicationTest.java
+++ b/src/test/java/net/siegeln/cameleer/saas/CameleerSaasApplicationTest.java
@@ -6,7 +6,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
-@Import(TestcontainersConfig.class)
+@Import({TestcontainersConfig.class, TestSecurityConfig.class})
@ActiveProfiles("test")
class CameleerSaasApplicationTest {
diff --git a/src/test/java/net/siegeln/cameleer/saas/TestSecurityConfig.java b/src/test/java/net/siegeln/cameleer/saas/TestSecurityConfig.java
new file mode 100644
index 0000000..b305ae9
--- /dev/null
+++ b/src/test/java/net/siegeln/cameleer/saas/TestSecurityConfig.java
@@ -0,0 +1,24 @@
+package net.siegeln.cameleer.saas;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+
+import java.time.Instant;
+import java.util.Map;
+
+@TestConfiguration
+public class TestSecurityConfig {
+
+ @Bean
+ public JwtDecoder jwtDecoder() {
+ return token -> Jwt.withTokenValue(token)
+ .header("alg", "RS256")
+ .claim("sub", "test-user")
+ .claim("iss", "https://test-issuer.example.com/oidc")
+ .issuedAt(Instant.now())
+ .expiresAt(Instant.now().plusSeconds(3600))
+ .build();
+ }
+}
diff --git a/src/test/java/net/siegeln/cameleer/saas/auth/AuthControllerTest.java b/src/test/java/net/siegeln/cameleer/saas/auth/AuthControllerTest.java
index d874094..d672c07 100644
--- a/src/test/java/net/siegeln/cameleer/saas/auth/AuthControllerTest.java
+++ b/src/test/java/net/siegeln/cameleer/saas/auth/AuthControllerTest.java
@@ -2,6 +2,7 @@ package net.siegeln.cameleer.saas.auth;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.siegeln.cameleer.saas.TestcontainersConfig;
+import net.siegeln.cameleer.saas.TestSecurityConfig;
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
import org.junit.jupiter.api.Test;
@@ -20,7 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@SpringBootTest
@AutoConfigureMockMvc
-@Import(TestcontainersConfig.class)
+@Import({TestcontainersConfig.class, TestSecurityConfig.class})
@ActiveProfiles("test")
class AuthControllerTest {
diff --git a/src/test/java/net/siegeln/cameleer/saas/license/LicenseControllerTest.java b/src/test/java/net/siegeln/cameleer/saas/license/LicenseControllerTest.java
index 6d89f68..b662626 100644
--- a/src/test/java/net/siegeln/cameleer/saas/license/LicenseControllerTest.java
+++ b/src/test/java/net/siegeln/cameleer/saas/license/LicenseControllerTest.java
@@ -2,6 +2,7 @@ package net.siegeln.cameleer.saas.license;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.siegeln.cameleer.saas.TestcontainersConfig;
+import net.siegeln.cameleer.saas.TestSecurityConfig;
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,7 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@SpringBootTest
@AutoConfigureMockMvc
-@Import(TestcontainersConfig.class)
+@Import({TestcontainersConfig.class, TestSecurityConfig.class})
@ActiveProfiles("test")
class LicenseControllerTest {
diff --git a/src/test/java/net/siegeln/cameleer/saas/tenant/TenantControllerTest.java b/src/test/java/net/siegeln/cameleer/saas/tenant/TenantControllerTest.java
index a663583..ad36932 100644
--- a/src/test/java/net/siegeln/cameleer/saas/tenant/TenantControllerTest.java
+++ b/src/test/java/net/siegeln/cameleer/saas/tenant/TenantControllerTest.java
@@ -2,6 +2,7 @@ package net.siegeln.cameleer.saas.tenant;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.siegeln.cameleer.saas.TestcontainersConfig;
+import net.siegeln.cameleer.saas.TestSecurityConfig;
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,7 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@SpringBootTest
@AutoConfigureMockMvc
-@Import(TestcontainersConfig.class)
+@Import({TestcontainersConfig.class, TestSecurityConfig.class})
@ActiveProfiles("test")
class TenantControllerTest {