diff --git a/pom.xml b/pom.xml
index 4a303074..f271e0ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,12 +24,15 @@
jacoco
java
UTF-8
+
+ src/main/java/fr/insee/genesis/configuration/**/*.java
+
true
1.17.2
1.2.1
2.18.2
- 1.0.3
+ 1.0.4
@@ -44,6 +47,11 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
org.springframework.boot
spring-boot-starter-test
@@ -163,6 +171,11 @@
org.jacoco
jacoco-maven-plugin
${jacoco.version}
+
+
+ src/main/java/fr/insee/genesis/configuration/**/*
+
+
default-prepare-agent
diff --git a/src/main/java/fr/insee/genesis/configuration/Config.java b/src/main/java/fr/insee/genesis/configuration/Config.java
index 14650205..ed5812e4 100644
--- a/src/main/java/fr/insee/genesis/configuration/Config.java
+++ b/src/main/java/fr/insee/genesis/configuration/Config.java
@@ -20,6 +20,21 @@ public class Config {
@Value("${fr.insee.genesis.sourcefolder.specifications}")
private String specFolderSource;
+ @Value("${fr.insee.genesis.oidc.auth-server-url}")
+ private String authServerUrl;
+
+ @Value("${fr.insee.genesis.oidc.realm}")
+ private String realm;
+
+ @Value("${fr.insee.genesis.security.token.oidc-claim-role}")
+ private String oidcClaimRole;
+
+ @Value("${fr.insee.genesis.security.token.oidc-claim-username}")
+ private String oidcClaimUsername;
+
+ @Value("#{'${fr.insee.genesis.security.whitelist-matchers}'.split(',')}")
+ private String[] whiteList;
+
private final String logFolder;
//Extract log folder from log filename property
diff --git a/src/main/java/fr/insee/genesis/configuration/SpringDocConfiguration.java b/src/main/java/fr/insee/genesis/configuration/SpringDocConfiguration.java
index b0b3de9b..d5e27cd5 100644
--- a/src/main/java/fr/insee/genesis/configuration/SpringDocConfiguration.java
+++ b/src/main/java/fr/insee/genesis/configuration/SpringDocConfiguration.java
@@ -1,28 +1,76 @@
package fr.insee.genesis.configuration;
+import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
-import io.swagger.v3.oas.models.servers.Server;
+
+import io.swagger.v3.oas.models.security.OAuthFlow;
+import io.swagger.v3.oas.models.security.OAuthFlows;
+import io.swagger.v3.oas.models.security.Scopes;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringDocConfiguration {
- @Value("${fr.insee.genesis.version}")
- private String projectVersion;
+ @Value("${fr.insee.genesis.version}")
+ private String projectVersion;
+ public static final String BEARERSCHEME = "bearerAuth";
+ public static final String OAUTH2SCHEME = "oauth2";
+
+ @Bean
+ @ConditionalOnProperty(name = "fr.insee.genesis.authentication", havingValue = "NONE")
+ public OpenAPI noAuthOpenAPI() {
+ return generateOpenAPI();
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "fr.insee.genesis.authentication", havingValue = "OIDC")
+ public OpenAPI oidcOpenAPI(Config config) {
+ String authUrl = config.getAuthServerUrl() + "/realms/" + config.getRealm() + "/protocol/openid-connect";
+ return generateOpenAPI()
+ .addSecurityItem(new SecurityRequirement().addList(OAUTH2SCHEME))
+ .addSecurityItem(new SecurityRequirement().addList(BEARERSCHEME))
+ .components(
+ new Components()
+ .addSecuritySchemes(OAUTH2SCHEME,
+ new SecurityScheme()
+ .name(OAUTH2SCHEME)
+ .type(SecurityScheme.Type.OAUTH2)
+ .flows(getFlows(authUrl))
+ )
+ .addSecuritySchemes(BEARERSCHEME,
+ new SecurityScheme()
+ .name(BEARERSCHEME)
+ .type(SecurityScheme.Type.HTTP)
+ .scheme("bearer")
+ .bearerFormat("JWT")
+ )
+ );
+ }
- @Bean
- public OpenAPI customOpenAPI() {
- return new OpenAPI()
- .addServersItem(new Server().url("/"))
- .info(new Info()
- .title("Genesis API")
- .description("Rest Endpoints and services to communicate with Genesis database")
- .version(projectVersion)
- );
- }
+ private OpenAPI generateOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("Genesis API")
+ .description("Rest Endpoints and services to communicate with Genesis database")
+ .version(projectVersion)
+ );
+ }
+ private OAuthFlows getFlows(String authUrl) {
+ OAuthFlows flows = new OAuthFlows();
+ OAuthFlow flow = new OAuthFlow();
+ Scopes scopes = new Scopes();
+ flow.setAuthorizationUrl(authUrl + "/auth");
+ flow.setTokenUrl(authUrl + "/token");
+ flow.setRefreshUrl(authUrl + "/token");
+ flow.setScopes(scopes);
+ return flows.authorizationCode(flow);
+ }
}
diff --git a/src/main/java/fr/insee/genesis/configuration/auth/security/DefaultSecurityConfig.java b/src/main/java/fr/insee/genesis/configuration/auth/security/DefaultSecurityConfig.java
index 25c7487b..af4b5648 100644
--- a/src/main/java/fr/insee/genesis/configuration/auth/security/DefaultSecurityConfig.java
+++ b/src/main/java/fr/insee/genesis/configuration/auth/security/DefaultSecurityConfig.java
@@ -1,6 +1,6 @@
package fr.insee.genesis.configuration.auth.security;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -10,7 +10,7 @@
@Configuration
@EnableWebSecurity
-@ConditionalOnMissingBean(OIDCSecurityConfig.class)
+@ConditionalOnProperty(name = "fr.insee.genesis.authentication", havingValue = "NONE")
public class DefaultSecurityConfig {
@Bean
diff --git a/src/main/java/fr/insee/genesis/configuration/auth/security/OIDCSecurityConfig.java b/src/main/java/fr/insee/genesis/configuration/auth/security/OIDCSecurityConfig.java
index b0f625a0..ac524ce0 100644
--- a/src/main/java/fr/insee/genesis/configuration/auth/security/OIDCSecurityConfig.java
+++ b/src/main/java/fr/insee/genesis/configuration/auth/security/OIDCSecurityConfig.java
@@ -1,22 +1,48 @@
package fr.insee.genesis.configuration.auth.security;
+import fr.insee.genesis.configuration.Config;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
+@Slf4j
@ConditionalOnProperty(name = "fr.insee.genesis.authentication", havingValue = "OIDC")
public class OIDCSecurityConfig {
- @Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
- return http.build();
+ Config config;
+ @Autowired
+ public OIDCSecurityConfig(Config config) {
+ this.config = config;
}
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(AbstractHttpConfigurer::disable)
+ .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
+ for (var pattern : config.getWhiteList()) {
+ http.authorizeHttpRequests(authorize ->
+ authorize
+ .requestMatchers(AntPathRequestMatcher.antMatcher(pattern)).permitAll()
+ );
+ }
+ http
+ .authorizeHttpRequests(configurer -> configurer
+ .anyRequest().authenticated()
+ )
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
+ return http.build();
+ }
+
}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index f29b9618..6588b136 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -5,4 +5,11 @@ fr.insee.genesis.persistence.database.mongodb.port=27017
fr.insee.genesis.persistence.database.mongodb.database=CollectedDataRepository
fr.insee.genesis.persistence.database.mongodb.username=user
-#fr.insee.genesis.persistence.database.mongodb.password in Vault
\ No newline at end of file
+#fr.insee.genesis.persistence.database.mongodb.password in Vault
+
+#--------------------------------------------------------------------------
+# Keycloak configuration
+#--------------------------------------------------------------------------
+fr.insee.genesis.oidc.auth-server-url=***
+fr.insee.genesis.oidc.realm=***
+springdoc.swagger-ui.oauth.client-id=***
\ No newline at end of file
diff --git a/src/main/resources/application-preprod.properties b/src/main/resources/application-preprod.properties
index be37b15a..c0192fce 100644
--- a/src/main/resources/application-preprod.properties
+++ b/src/main/resources/application-preprod.properties
@@ -5,4 +5,11 @@ fr.insee.genesis.persistence.database.mongodb.port=27017
fr.insee.genesis.persistence.database.mongodb.database=CollectedDataRepository
fr.insee.genesis.persistence.database.mongodb.username=user
-#fr.insee.genesis.persistence.database.mongodb.password in Vault
\ No newline at end of file
+#fr.insee.genesis.persistence.database.mongodb.password in Vault
+
+#--------------------------------------------------------------------------
+# Keycloak configuration
+#--------------------------------------------------------------------------
+fr.insee.genesis.oidc.auth-server-url=***
+fr.insee.genesis.oidc.realm=***
+springdoc.swagger-ui.oauth.client-id=***
\ No newline at end of file
diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties
index 8193e963..a28c41aa 100644
--- a/src/main/resources/application-prod.properties
+++ b/src/main/resources/application-prod.properties
@@ -5,4 +5,11 @@ fr.insee.genesis.persistence.database.mongodb.port=27017
fr.insee.genesis.persistence.database.mongodb.database=CollectedDataRepository
fr.insee.genesis.persistence.database.mongodb.username=user
-#fr.insee.genesis.persistence.database.mongodb.password in Vault
\ No newline at end of file
+#fr.insee.genesis.persistence.database.mongodb.password in Vault
+
+#--------------------------------------------------------------------------
+# Keycloak configuration
+#--------------------------------------------------------------------------
+fr.insee.genesis.oidc.auth-server-url=***
+fr.insee.genesis.oidc.realm=***
+springdoc.swagger-ui.oauth.client-id=***
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index e9e5c4dc..8d75fa68 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -6,7 +6,7 @@ spring.profiles.active=local
#--------------------------------------------------------------------------
# Global configuration
#--------------------------------------------------------------------------
-fr.insee.genesis.authentication = NONE
+fr.insee.genesis.authentication = OIDC
#--------------------------------------------------------------------------
# Configuration for springdoc / swagger
@@ -14,6 +14,17 @@ fr.insee.genesis.authentication = NONE
fr.insee.genesis.version=@project.version@
#To make swagger-ui display the actuator endpoints
springdoc.show-actuator=true
+springdoc.swagger-ui.oauth2RedirectUrl=${fr.insee.genesis.application.host.url}/swagger-ui/oauth2-redirect.html
+# To deal with http/https issues in swagger
+server.forward-headers-strategy=framework
+
+#--------------------------------------------------------------------------
+# Security
+#--------------------------------------------------------------------------
+fr.insee.genesis.security.token.oidc-claim-role=realm_access.roles
+fr.insee.genesis.security.token.oidc-claim-username=name
+spring.security.oauth2.resourceserver.jwt.issuer-uri=${fr.insee.genesis.oidc.auth-server-url}/realms/${fr.insee.genesis.oidc.realm}
+fr.insee.genesis.security.whitelist-matchers=/v3/api-docs/**,/swagger-ui/**,/swagger-ui.html,/actuator/**,/error,/,/health-check/**
#--------------------------------------------------------------------------
# Actuator