diff --git a/docs/en/security/authentication.md b/docs/en/security/authentication.md
index b595ea3e..55fadd69 100644
--- a/docs/en/security/authentication.md
+++ b/docs/en/security/authentication.md
@@ -5,5 +5,5 @@ Connexion to the identity provider (an Open LDAP directory) is made in this clas
### SecurityContext.java
-[include](../../../src/main/java/fr/insee/pogues/config/SecurityContext.java)
+[include](../../../src/main/java/fr/insee/pogues/configuration/SecurityContext.java)
diff --git a/docs/fr/security/authentication.md b/docs/fr/security/authentication.md
index e0dd79d9..cec04d4f 100644
--- a/docs/fr/security/authentication.md
+++ b/docs/fr/security/authentication.md
@@ -5,5 +5,5 @@ Les informations de configuration d'annuaire sont récupérée dans le fichier p
### Le fichier SecurityContext.java
-[include](../../../src/main/java/fr/insee/pogues/config/SecurityContext.java)
+[include](../../../src/main/java/fr/insee/pogues/configuration/SecurityContext.java)
diff --git a/pom.xml b/pom.xml
index 35580681..d71a1fd3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.7.18
+ 3.2.4
@@ -13,7 +13,7 @@
fr.insee
Pogues-BO
jar
- 4.4.3
+ 4.4.4
Pogues-BO
@@ -28,9 +28,6 @@
0.8.11
12.4
2.15.1
-
- 2.2
-
jacoco
jacoco
reuseReports
@@ -62,6 +59,12 @@
spring-boot-starter-jersey
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
com.fasterxml.jackson.jaxrs
@@ -118,14 +121,6 @@
json-simple
${json-simple.version}
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpmime
-
org.apache.xmlgraphics
fop
@@ -153,8 +148,6 @@
provided
-
-
org.springdoc
@@ -181,7 +174,7 @@
org.eclipse.persistence
org.eclipse.persistence.moxy
- 2.7.14
+ 4.0.2
javax.xml.bind
diff --git a/src/main/java/fr/insee/pogues/config/ClientConfig.java b/src/main/java/fr/insee/pogues/config/ClientConfig.java
deleted file mode 100644
index 395f1cb1..00000000
--- a/src/main/java/fr/insee/pogues/config/ClientConfig.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package fr.insee.pogues.config;
-
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.ssl.SSLContexts;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import javax.net.ssl.SSLContext;
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-
-@Configuration
-public class ClientConfig {
-
- @Bean
- public HttpClientBuilder httpClientBuilder() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
- SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
- SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
- return HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).setSSLSocketFactory(sslsf);
- }
-
-}
diff --git a/src/main/java/fr/insee/pogues/config/OpenApiConfiguration.java b/src/main/java/fr/insee/pogues/config/OpenApiConfiguration.java
deleted file mode 100644
index 225d45bb..00000000
--- a/src/main/java/fr/insee/pogues/config/OpenApiConfiguration.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package fr.insee.pogues.config;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
-import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.oas.models.info.Info;
-import io.swagger.v3.oas.annotations.security.SecurityScheme;
-import io.swagger.v3.oas.models.servers.Server;
-
-@Configuration
-@SecurityScheme(
- name = "bearerAuth",
- type = SecuritySchemeType.HTTP,
- bearerFormat = "JWT",
- scheme = "bearer"
- )
-public class OpenApiConfiguration {
-
- @Value("${fr.insee.pogues.api.scheme}")
- private String apiScheme;
-
- @Value("${fr.insee.pogues.api.host}")
- private String apiHost;
-
- @Value("${fr.insee.pogues.api.name}")
- private String apiName;
-
- @Value("${fr.insee.pogues.model.version}")
- private String poguesModelVersion;
-
- @Value("${fr.insee.pogues.version}")
- private String projectVersion;
-
- @Bean
- public OpenAPI customOpenAPI() {
- Server server = new Server();
- server.setUrl(String.format("%s://%s%s", apiScheme, apiHost, apiName));
- return new OpenAPI()
- .addServersItem(server)
- .info(new Info()
- .title("Pogues API")
- .description(
- "Rest Endpoints and services used by Pogues"
- + "Pogues-Model version : " + poguesModelVersion + "
")
- .version(projectVersion)
-
- );
- }
-}
diff --git a/src/main/java/fr/insee/pogues/config/PropertiesLog.java b/src/main/java/fr/insee/pogues/config/PropertiesLog.java
deleted file mode 100644
index 4b81b8dd..00000000
--- a/src/main/java/fr/insee/pogues/config/PropertiesLog.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package fr.insee.pogues.config;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.logging.Logger;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.core.env.AbstractEnvironment;
-import org.springframework.core.env.EnumerablePropertySource;
-import org.springframework.core.env.Environment;
-import org.springframework.stereotype.Component;
-
-@Component
-public class PropertiesLog {
-
- private static final Logger log = Logger.getLogger(PropertiesLog.class.getName());
- private final Environment environment;
-
- public PropertiesLog(Environment environment) {
- Objects.requireNonNull(environment);
- this.environment=environment;
-
- log.info("===============================================================================================");
- log.info(" Properties values ");
- log.info("===============================================================================================");
-
- ((AbstractEnvironment) environment).getPropertySources().stream()
- .map(propertySource -> {
- if (propertySource instanceof EnumerablePropertySource){
- return ((EnumerablePropertySource>)propertySource).getPropertyNames();
- }else{
- log.warning(propertySource+ " is not EnumerablePropertySource : listing impossible");
- return new String[] {};
- }
- }
- )
- .flatMap(Arrays::stream)
- .distinct()
- .forEach(key->log.info(key+" = "+printValueWithoutPassword(key)));
- }
-
- private String printValueWithoutPassword(String key) {
- if (StringUtils.containsAny(key, "password", "pwd", "keycloak.key", "jeton", "secret")) {
- return "******";
- }
- return environment.getProperty(key);
- }
-
-}
diff --git a/src/main/java/fr/insee/pogues/config/auth/security/DefaultSecurityContext.java b/src/main/java/fr/insee/pogues/config/auth/security/DefaultSecurityContext.java
deleted file mode 100644
index a412cf84..00000000
--- a/src/main/java/fr/insee/pogues/config/auth/security/DefaultSecurityContext.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package fr.insee.pogues.config.auth.security;
-
-import fr.insee.pogues.config.auth.UserProvider;
-import fr.insee.pogues.config.auth.user.User;
-
-import static org.springframework.security.config.Customizer.withDefaults;
-
-import java.util.Arrays;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-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.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.CorsConfigurationSource;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-
-@Configuration
-@EnableWebSecurity
-@ConditionalOnExpression("!'OIDC'.equals('${fr.insee.pogues.authentication}')")
-public class DefaultSecurityContext extends WebSecurityConfigurerAdapter{
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.csrf().disable();
- http.cors(withDefaults())
- .authorizeRequests().anyRequest().permitAll();
- }
-
- @Bean
- public UserProvider getUserProvider() {
- return auth->new User();
- }
-
- @Bean
- public CorsConfigurationSource corsConfigurationSource() {
- CorsConfiguration configuration = new CorsConfiguration();
- configuration.setAllowedOrigins(Arrays.asList("*"));
- configuration.setAllowedMethods(Arrays.asList("*"));
- configuration.setAllowedHeaders(Arrays.asList("*"));
- configuration.addExposedHeader("Content-Disposition");
-
- UrlBasedCorsConfigurationSource source = new
- UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/**", configuration);
- return source;
- }
-
-}
diff --git a/src/main/java/fr/insee/pogues/config/auth/security/OpenIDConnectSecurityContext.java b/src/main/java/fr/insee/pogues/config/auth/security/OpenIDConnectSecurityContext.java
deleted file mode 100644
index 537ab69a..00000000
--- a/src/main/java/fr/insee/pogues/config/auth/security/OpenIDConnectSecurityContext.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package fr.insee.pogues.config.auth.security;
-
-import static org.springframework.security.config.Customizer.withDefaults;
-
-import java.util.Arrays;
-import java.util.Optional;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-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;
-import org.springframework.http.HttpMethod;
-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.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.CorsConfigurationSource;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-
-import fr.insee.pogues.config.auth.UserProvider;
-import fr.insee.pogues.config.auth.user.User;
-
-@Configuration
-@EnableWebSecurity
-@ConditionalOnProperty(name = "fr.insee.pogues.authentication", havingValue = "OIDC")
-public class OpenIDConnectSecurityContext extends WebSecurityConfigurerAdapter {
-
- static final Logger logger = LogManager.getLogger(OpenIDConnectSecurityContext.class);
-
- @Value("${fr.insee.pogues.force.ssl}")
- boolean requireSSL;
-
- @Value("${jwt.stamp-claim}")
- private String stampClaim;
-
- @Value("${jwt.username-claim}")
- private String nameClaim;
-
- @Value("${fr.insee.pogues.cors.allowedOrigin}")
- private Optional allowedOrigin;
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- //TODO : variabiliser path /api...
- http.sessionManagement().disable();
- http.cors(withDefaults())
- .authorizeRequests()
- .antMatchers("/api/init", "/api/healthcheck").permitAll()
- .antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
- .antMatchers("/api/persistence/questionnaire/json-lunatic/**").permitAll()
- .antMatchers(HttpMethod.OPTIONS).permitAll()
- .anyRequest().authenticated()
- .and()
- .oauth2ResourceServer()
- .jwt();
- if (requireSSL)
- http.antMatcher("/**").requiresChannel().anyRequest().requiresSecure();
- }
-
- @Bean
- public UserProvider getUserProvider() {
- return auth -> {
- final Jwt jwt = (Jwt) auth.getPrincipal();
- return new User(jwt.getClaimAsString(stampClaim), jwt.getClaimAsString(nameClaim));
- };
- }
-
- @Bean
- public CorsConfigurationSource corsConfigurationSource() {
- CorsConfiguration configuration = new CorsConfiguration();
- configuration.setAllowedOrigins(Arrays.asList(allowedOrigin.get()));
- configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));
- configuration.setAllowedHeaders(Arrays.asList("*"));
- configuration.addExposedHeader("Content-Disposition");
- UrlBasedCorsConfigurationSource source = new
- UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/**", configuration);
- return source;
- }
-
-}
diff --git a/src/main/java/fr/insee/pogues/configuration/AppConfiguration.java b/src/main/java/fr/insee/pogues/configuration/AppConfiguration.java
new file mode 100644
index 00000000..8fa14465
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/AppConfiguration.java
@@ -0,0 +1,40 @@
+package fr.insee.pogues.configuration;
+
+import fr.insee.pogues.configuration.rest.AuthenticationHelper;
+import fr.insee.pogues.configuration.rest.WebClientTokenInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+@EnableConfigurationProperties
+@ComponentScan(basePackages = { "fr.insee.pogues" })
+@EnableTransactionManagement
+@EnableCaching
+@Slf4j
+public class AppConfiguration {
+
+ @Autowired
+ private AuthenticationHelper authenticationHelper;
+
+ @Bean
+ public WebClient webClient(
+ @Value("${feature.oidc.enabled}") boolean oidcEnabled,
+ WebClient.Builder builder) {
+ builder
+ .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
+ if(oidcEnabled) builder.filter(new WebClientTokenInterceptor(authenticationHelper));
+ return builder.build();
+ }
+
+}
diff --git a/src/main/java/fr/insee/pogues/configuration/CorsConfig.java b/src/main/java/fr/insee/pogues/configuration/CorsConfig.java
new file mode 100644
index 00000000..8da264a3
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/CorsConfig.java
@@ -0,0 +1,29 @@
+package fr.insee.pogues.configuration;
+
+import fr.insee.pogues.configuration.properties.ApplicationProperties;
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+/** Cors configuration */
+@Configuration
+@AllArgsConstructor
+public class CorsConfig {
+ @Bean
+ protected CorsConfigurationSource corsConfigurationSource(ApplicationProperties applicationProperties) {
+ org.springframework.web.cors.CorsConfiguration configuration = new org.springframework.web.cors.CorsConfiguration();
+ configuration.setAllowedOriginPatterns(applicationProperties.corsOrigins());
+ configuration.setAllowedMethods(List.of("GET", "PUT", "POST", "DELETE", "OPTIONS"));
+ configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
+ configuration.addExposedHeader("Content-Disposition");
+ configuration.setMaxAge(3600L);
+ configuration.setAllowCredentials(true);
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", configuration);
+ return source;
+ }
+}
diff --git a/src/main/java/fr/insee/pogues/configuration/PropertiesLogger.java b/src/main/java/fr/insee/pogues/configuration/PropertiesLogger.java
new file mode 100644
index 00000000..dd7afada
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/PropertiesLogger.java
@@ -0,0 +1,59 @@
+package fr.insee.pogues.configuration;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.AbstractEnvironment;
+import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Listener Spring loads when Spring Boot is starting on
+ * ApplicationEnvironmentPreparedEvent event
+ * Display props in logs
+ *
+ */
+@Slf4j
+public class PropertiesLogger implements ApplicationListener {
+
+ private static final Set hiddenWords = Set.of("password", "pwd", "jeton", "token", "secret");
+
+ @Override
+ public void onApplicationEvent(@NonNull ApplicationEnvironmentPreparedEvent event) {
+ Environment environment = event.getEnvironment();
+
+ log.info("===============================================================================================");
+ log.info(" Properties ");
+ log.info("===============================================================================================");
+
+ ((AbstractEnvironment) environment).getPropertySources().stream()
+ .map(propertySource -> {
+ if (propertySource instanceof EnumerablePropertySource) {
+ return ((EnumerablePropertySource>) propertySource).getPropertyNames();
+ } else {
+ log.warn(propertySource + " is not an EnumerablePropertySource : impossible to display");
+ return new String[] {};
+ }
+ })
+ .flatMap(Arrays::stream)
+ .distinct()
+ .filter(Objects::nonNull)
+ .forEach(key -> log.info(key + " = " + resolveValueWithSecretAttribute(key, environment)));
+ log.info("============================================================================");
+
+ }
+
+ private static Object resolveValueWithSecretAttribute(String key, Environment environment) {
+ if (hiddenWords.stream().anyMatch(key::contains)) {
+ return "******";
+ }
+ return environment.getProperty(key);
+
+ }
+
+}
diff --git a/src/main/java/fr/insee/pogues/config/RemoteMetadata.java b/src/main/java/fr/insee/pogues/configuration/RemoteMetadata.java
similarity index 94%
rename from src/main/java/fr/insee/pogues/config/RemoteMetadata.java
rename to src/main/java/fr/insee/pogues/configuration/RemoteMetadata.java
index 576fad9f..294a6b6e 100644
--- a/src/main/java/fr/insee/pogues/config/RemoteMetadata.java
+++ b/src/main/java/fr/insee/pogues/configuration/RemoteMetadata.java
@@ -1,4 +1,4 @@
-package fr.insee.pogues.config;
+package fr.insee.pogues.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
diff --git a/src/main/java/fr/insee/pogues/config/StaticResourcesForFOPConfig.java b/src/main/java/fr/insee/pogues/configuration/StaticResourcesForFOPConfig.java
similarity index 80%
rename from src/main/java/fr/insee/pogues/config/StaticResourcesForFOPConfig.java
rename to src/main/java/fr/insee/pogues/configuration/StaticResourcesForFOPConfig.java
index 2f634a93..e2535c6f 100644
--- a/src/main/java/fr/insee/pogues/config/StaticResourcesForFOPConfig.java
+++ b/src/main/java/fr/insee/pogues/configuration/StaticResourcesForFOPConfig.java
@@ -1,4 +1,4 @@
-package fr.insee.pogues.config;
+package fr.insee.pogues.configuration;
import lombok.Getter;
import lombok.NonNull;
@@ -20,10 +20,16 @@
@Component
public class StaticResourcesForFOPConfig {
- final static Logger logger = LogManager.getLogger(StaticResourcesForFOPConfig.class);
+ final static Logger LOG = LogManager.getLogger(StaticResourcesForFOPConfig.class);
+
@Value("${fr.insee.pogues.pdf.temp.dir}")
private Path staticResourcesTempDir;
+
+ public StaticResourcesForFOPConfig(){
+ LOG.info("Init, copy assest to tempDir");
+ copyAssetResourcesToTempDir();
+ }
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
private URI imgFolderUri=null;
@@ -39,9 +45,9 @@ public URI getImgFolderUri() {
return imgFolderUri;
}
- @PostConstruct
+
public void copyAssetResourcesToTempDir() {
- logger.info("Copying static files from "+resolver.getResource(ResourceLoader.CLASSPATH_URL_PREFIX+"/pdf/")+" to "+staticResourcesTempDir);
+ LOG.info("Copying static files from "+resolver.getResource(ResourceLoader.CLASSPATH_URL_PREFIX+"/pdf/")+" to "+staticResourcesTempDir);
copyFromClasspath(ResourceLoader.CLASSPATH_URL_PREFIX+"/pdf/img/*", "img");
copyFromClasspath(ResourceLoader.CLASSPATH_URL_PREFIX+"/pdf/fonts/*", "fonts");
@@ -49,7 +55,7 @@ public void copyAssetResourcesToTempDir() {
}
private void copyFromClasspath(String locationPattern, String destinationDirectory){
- logger.debug("Process "+locationPattern+ " to "+destinationDirectory);
+ LOG.debug("Process "+locationPattern+ " to "+destinationDirectory);
Resource[] resources =null;
try {
resources = resolver.getResources(locationPattern);
@@ -58,7 +64,7 @@ private void copyFromClasspath(String locationPattern, String destinationDirecto
}
var destination = staticResourcesTempDir.resolve(destinationDirectory);
for (Resource resource : resources) {
- logger.debug("Process "+resource);
+ LOG.debug("Process "+resource);
try {
FileUtils.copyInputStreamToFile(resource.getInputStream(), destination.resolve(resource.getFilename()).toFile());
} catch (IOException e) {
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/AuthConstants.java b/src/main/java/fr/insee/pogues/configuration/auth/AuthConstants.java
new file mode 100644
index 00000000..ac7a4065
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/AuthConstants.java
@@ -0,0 +1,9 @@
+package fr.insee.pogues.configuration.auth;
+
+public class AuthConstants {
+ private AuthConstants() {
+ throw new IllegalStateException("Constants class");
+ }
+
+ public static final String ROLE_PREFIX = "ROLE_";
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRole.java b/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRole.java
new file mode 100644
index 00000000..061be288
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRole.java
@@ -0,0 +1,11 @@
+package fr.insee.pogues.configuration.auth;
+
+public class AuthorityRole {
+ private AuthorityRole() {
+ throw new IllegalArgumentException("Constant class");
+ }
+
+ public static final String HAS_ROLE_DESIGNER = "hasRole('DESIGNER')";
+ public static final String HAS_ANY_ROLE = "hasAnyRole('DESIGNER', 'ADMIN')";
+ public static final String HAS_ADMIN_PRIVILEGES = "hasAnyRole('ADMIN')";
+}
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRoleEnum.java b/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRoleEnum.java
new file mode 100644
index 00000000..16a40184
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/AuthorityRoleEnum.java
@@ -0,0 +1,6 @@
+package fr.insee.pogues.configuration.auth;
+
+public enum AuthorityRoleEnum {
+ ADMIN,
+ DESIGNER
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/GrantedAuthorityConverter.java b/src/main/java/fr/insee/pogues/configuration/auth/GrantedAuthorityConverter.java
new file mode 100644
index 00000000..7da55587
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/GrantedAuthorityConverter.java
@@ -0,0 +1,55 @@
+package fr.insee.pogues.configuration.auth;
+
+import fr.insee.pogues.configuration.properties.OidcProperties;
+import fr.insee.pogues.configuration.properties.RoleProperties;
+import lombok.AllArgsConstructor;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+
+import java.util.*;
+import java.util.stream.Collectors;
+@AllArgsConstructor
+public class GrantedAuthorityConverter implements Converter> {
+ private final OidcProperties oidcProperties;
+ private final RoleProperties roleProperties;
+
+ /**
+ *
+ * @param map: Map that represent JWT token
+ * @param keyPath : jsonPath to wanted value, ex: realm_access.roles
+ * @return the value of keyPath inside Map
+ * @param
+ */
+ public T getDeepPropsOfMapForRoles(Map map, String keyPath){
+ Map subMap = (Map) map;
+ String[] propertyPath = keyPath.toString().split("\\.");
+ for (int i = 0; i < propertyPath.length -1; i++) {
+ subMap = (Map) subMap.get(propertyPath[i]);
+ }
+ return (T) subMap.get(propertyPath[propertyPath.length -1]);
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection convert(Jwt jwt) {
+ Map claims = jwt.getClaims();
+
+ List roles = getDeepPropsOfMapForRoles(claims, oidcProperties.roleClaim());
+
+ return roles.stream()
+ .map(role -> {
+ if (role.equals(roleProperties.designer())) {
+ return new SimpleGrantedAuthority(AuthConstants.ROLE_PREFIX + AuthorityRoleEnum.DESIGNER);
+ }
+ if (role.equals(roleProperties.admin())) {
+ return new SimpleGrantedAuthority(AuthConstants.ROLE_PREFIX + AuthorityRoleEnum.ADMIN);
+ }
+ return null;
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toCollection(ArrayList::new));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/NoAuthSecurityConfiguration.java b/src/main/java/fr/insee/pogues/configuration/auth/NoAuthSecurityConfiguration.java
new file mode 100644
index 00000000..439dc947
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/NoAuthSecurityConfiguration.java
@@ -0,0 +1,65 @@
+package fr.insee.pogues.configuration.auth;
+
+
+import fr.insee.pogues.configuration.auth.user.User;
+import fr.insee.pogues.configuration.properties.ApplicationProperties;
+import lombok.AllArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+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.web.SecurityFilterChain;
+import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
+
+@ConditionalOnProperty(name = "feature.oidc.enabled", havingValue = "false")
+@Configuration
+@EnableWebSecurity
+@AllArgsConstructor
+public class NoAuthSecurityConfiguration {
+ private final PublicSecurityFilterChain publicSecurityFilterChainConfiguration;
+
+ /**
+ * Configure spring security filter chain when no authentication
+ *
+ * @param http Http Security Object
+ * @return the spring security filter
+ * @throws Exception exception
+ */
+ @Bean
+ @Order(2)
+ protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ return http
+ .securityMatcher("/**")
+ .csrf(AbstractHttpConfigurer::disable)
+ .cors(Customizer.withDefaults())
+ .headers(headers -> headers
+ .xssProtection(xssConfig -> xssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED))
+ .contentSecurityPolicy(cspConfig -> cspConfig
+ .policyDirectives("default-src 'none'")
+ )
+ .referrerPolicy(referrerPolicy ->
+ referrerPolicy
+ .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)
+ ))
+ .anonymous(anonymousConfig -> anonymousConfig
+ .authorities("ROLE_ADMIN"))
+ .authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())
+ .build();
+ }
+
+ @Bean
+ @Order(1)
+ protected SecurityFilterChain filterPublicUrlsChain(HttpSecurity http, ApplicationProperties applicationProperties) throws Exception {
+ return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, applicationProperties.publicUrls());
+ }
+
+ @Bean
+ public UserProvider getUserProvider() {
+ return auth-> new User();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/OidcSecurityConfiguration.java b/src/main/java/fr/insee/pogues/configuration/auth/OidcSecurityConfiguration.java
new file mode 100644
index 00000000..8877b79e
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/OidcSecurityConfiguration.java
@@ -0,0 +1,109 @@
+package fr.insee.pogues.configuration.auth;
+
+import fr.insee.pogues.configuration.auth.user.User;
+import fr.insee.pogues.configuration.properties.ApplicationProperties;
+import fr.insee.pogues.configuration.properties.OidcProperties;
+import fr.insee.pogues.configuration.properties.RoleProperties;
+import lombok.AllArgsConstructor;
+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;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.config.Customizer;
+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.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
+
+import java.util.Collection;
+
+/**
+ * Spring security configuration when using OIDC auth
+ */
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+@ConditionalOnProperty(name = "feature.oidc.enabled", havingValue = "true")
+@AllArgsConstructor
+public class OidcSecurityConfiguration {
+ private final PublicSecurityFilterChain publicSecurityFilterChainConfiguration;
+
+ /**
+ * Configure spring security filter chain to handle OIDC authentication
+ *
+ * @param http Http Security Object
+ * @return the spring security filter
+ * @throws Exception exception
+ */
+
+ @Value("${jwt.stamp-claim}")
+ private String stampClaim;
+
+ @Value("${jwt.username-claim}")
+ private String nameClaim;
+
+ @Bean
+ @Order(2)
+ protected SecurityFilterChain filterChain(HttpSecurity http,
+ OidcProperties oidcProperties, RoleProperties roleProperties) throws Exception {
+ return http
+ .securityMatcher("/**")
+ .csrf(AbstractHttpConfigurer::disable)
+ .cors(Customizer.withDefaults())
+ .headers(headers -> headers
+ .xssProtection(xssConfig -> xssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED))
+ .contentSecurityPolicy(cspConfig -> cspConfig
+ .policyDirectives("default-src 'none'")
+ )
+ .referrerPolicy(referrerPolicy ->
+ referrerPolicy
+ .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)
+ ))
+ .authorizeHttpRequests(configurer -> configurer
+ .anyRequest()
+ .authenticated()
+ )
+ .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter(oidcProperties, roleProperties))))
+ .build();
+ }
+
+ @Bean
+ @Order(1)
+ protected SecurityFilterChain filterPublicUrlsChain(HttpSecurity http, ApplicationProperties applicationProperties,
+ OidcProperties oidcProperties) throws Exception {
+ String authorizedConnectionHost = oidcProperties.enabled() ?
+ " " + oidcProperties.authServerHost() : "";
+ return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, applicationProperties.publicUrls(), authorizedConnectionHost);
+ }
+
+ @Bean
+ protected JwtAuthenticationConverter jwtAuthenticationConverter(OidcProperties oidcProperties, RoleProperties roleProperties) {
+ JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
+ jwtAuthenticationConverter.setPrincipalClaimName(oidcProperties.principalAttribute());
+ jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter(oidcProperties, roleProperties));
+ return jwtAuthenticationConverter;
+ }
+
+ Converter> jwtGrantedAuthoritiesConverter(OidcProperties oidcProperties, RoleProperties roleProperties) {
+ return new GrantedAuthorityConverter(oidcProperties, roleProperties);
+ }
+
+ @Bean
+ public UserProvider getUserProvider() {
+ return auth -> {
+ final Jwt jwt = (Jwt) auth.getPrincipal();
+ return new User(jwt.getClaimAsString(stampClaim), jwt.getClaimAsString(nameClaim));
+ };
+ }
+
+}
diff --git a/src/main/java/fr/insee/pogues/configuration/auth/PublicSecurityFilterChain.java b/src/main/java/fr/insee/pogues/configuration/auth/PublicSecurityFilterChain.java
new file mode 100644
index 00000000..0acf9ee2
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/auth/PublicSecurityFilterChain.java
@@ -0,0 +1,44 @@
+package fr.insee.pogues.configuration.auth;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
+import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
+
+@Configuration
+public class PublicSecurityFilterChain {
+ SecurityFilterChain buildSecurityPublicFilterChain(HttpSecurity http, String[] publicUrls) throws Exception {
+ return buildSecurityPublicFilterChain(http, publicUrls, "");
+ }
+
+ SecurityFilterChain buildSecurityPublicFilterChain(HttpSecurity http, String[] publicUrls, String authorizedConnectionHost) throws Exception {
+ return http
+ .securityMatcher(publicUrls)
+ .cors(Customizer.withDefaults())
+ .headers(headers -> headers
+ .xssProtection(xssConfig -> xssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED))
+ .contentSecurityPolicy(cspConfig -> cspConfig
+ .policyDirectives("default-src 'none'; " +
+ "connect-src 'self'" + authorizedConnectionHost + "; " +
+ "img-src 'self' data:; " +
+ "style-src 'self'; " +
+ "script-src 'self' 'unsafe-inline'")
+ )
+ .referrerPolicy(referrerPolicy ->
+ referrerPolicy
+ .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)
+ ))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(HttpMethod.OPTIONS).permitAll()
+ .requestMatchers(publicUrls).permitAll()
+ .anyRequest()
+ .authenticated()
+ )
+ .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/config/auth/UserProvider.java b/src/main/java/fr/insee/pogues/configuration/auth/UserProvider.java
similarity index 62%
rename from src/main/java/fr/insee/pogues/config/auth/UserProvider.java
rename to src/main/java/fr/insee/pogues/configuration/auth/UserProvider.java
index 561ba636..70b839bb 100644
--- a/src/main/java/fr/insee/pogues/config/auth/UserProvider.java
+++ b/src/main/java/fr/insee/pogues/configuration/auth/UserProvider.java
@@ -1,6 +1,6 @@
-package fr.insee.pogues.config.auth;
+package fr.insee.pogues.configuration.auth;
-import fr.insee.pogues.config.auth.user.User;
+import fr.insee.pogues.configuration.auth.user.User;
import org.springframework.security.core.Authentication;
@FunctionalInterface
diff --git a/src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsService.java b/src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsService.java
similarity index 58%
rename from src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsService.java
rename to src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsService.java
index 2d9731f1..0841edc5 100644
--- a/src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsService.java
+++ b/src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsService.java
@@ -1,6 +1,6 @@
-package fr.insee.pogues.config.auth.security.restrictions;
+package fr.insee.pogues.configuration.auth.security.restrictions;
-import fr.insee.pogues.config.auth.user.User;
+import fr.insee.pogues.configuration.auth.user.User;
public interface StampsRestrictionsService {
diff --git a/src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsServiceImpl.java b/src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsServiceImpl.java
similarity index 85%
rename from src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsServiceImpl.java
rename to src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsServiceImpl.java
index b42c56cf..cc86774e 100644
--- a/src/main/java/fr/insee/pogues/config/auth/security/restrictions/StampsRestrictionsServiceImpl.java
+++ b/src/main/java/fr/insee/pogues/configuration/auth/security/restrictions/StampsRestrictionsServiceImpl.java
@@ -1,4 +1,4 @@
-package fr.insee.pogues.config.auth.security.restrictions;
+package fr.insee.pogues.configuration.auth.security.restrictions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -7,8 +7,8 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
-import fr.insee.pogues.config.auth.UserProvider;
-import fr.insee.pogues.config.auth.user.User;
+import fr.insee.pogues.configuration.auth.UserProvider;
+import fr.insee.pogues.configuration.auth.user.User;
@Service
public class StampsRestrictionsServiceImpl implements StampsRestrictionsService{
diff --git a/src/main/java/fr/insee/pogues/config/auth/user/User.java b/src/main/java/fr/insee/pogues/configuration/auth/user/User.java
similarity index 83%
rename from src/main/java/fr/insee/pogues/config/auth/user/User.java
rename to src/main/java/fr/insee/pogues/configuration/auth/user/User.java
index 0d646270..6b21d123 100644
--- a/src/main/java/fr/insee/pogues/config/auth/user/User.java
+++ b/src/main/java/fr/insee/pogues/configuration/auth/user/User.java
@@ -1,4 +1,4 @@
-package fr.insee.pogues.config.auth.user;
+package fr.insee.pogues.configuration.auth.user;
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/src/main/java/fr/insee/pogues/configuration/properties/ApplicationProperties.java b/src/main/java/fr/insee/pogues/configuration/properties/ApplicationProperties.java
new file mode 100644
index 00000000..881d652b
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/properties/ApplicationProperties.java
@@ -0,0 +1,48 @@
+package fr.insee.pogues.configuration.properties;
+
+import jakarta.validation.constraints.NotEmpty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@Validated
+@ConfigurationProperties(prefix = "application")
+public record ApplicationProperties(
+ String host,
+ String title,
+ String description,
+ String[] publicUrls,
+ @NotEmpty(message = "cors origins must be specified")
+ List corsOrigins) {
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ApplicationProperties that = (ApplicationProperties) o;
+ return Objects.equals(host, that.host)
+ && Objects.equals(title, that.title)
+ && Objects.equals(description, that.description)
+ && Arrays.equals(publicUrls, that.publicUrls);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(host, title, description);
+ result = 31 * result + Arrays.hashCode(publicUrls);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationProperties{" +
+ "host='" + host + '\'' +
+ ", title='" + title + '\'' +
+ ", description='" + description + '\'' +
+ ", publicUrls=" + Arrays.toString(publicUrls) +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/properties/OidcProperties.java b/src/main/java/fr/insee/pogues/configuration/properties/OidcProperties.java
new file mode 100644
index 00000000..d940471a
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/properties/OidcProperties.java
@@ -0,0 +1,15 @@
+package fr.insee.pogues.configuration.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+
+@ConfigurationProperties(prefix = "feature.oidc")
+public record OidcProperties(
+ boolean enabled,
+ String authServerHost,
+ String authServerUrl,
+ String realm,
+ String principalAttribute,
+ String roleClaim,
+ String clientId) {
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/properties/RoleProperties.java b/src/main/java/fr/insee/pogues/configuration/properties/RoleProperties.java
new file mode 100644
index 00000000..047d0d7f
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/properties/RoleProperties.java
@@ -0,0 +1,10 @@
+package fr.insee.pogues.configuration.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "application.roles")
+public record RoleProperties(
+ String designer,
+ String admin
+) {
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationHelper.java b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationHelper.java
new file mode 100644
index 00000000..b83723b0
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationHelper.java
@@ -0,0 +1,19 @@
+package fr.insee.pogues.configuration.rest;
+
+import org.springframework.security.core.Authentication;
+
+public interface AuthenticationHelper {
+ /**
+ * Retrieve the auth token of the current user
+ *
+ * @return auth token
+ */
+ String getUserToken();
+
+ /**
+ * Retrieve the authentication principal for current user
+ *
+ * @return {@link Authentication} the authentication user object
+ */
+ Authentication getAuthenticationPrincipal();
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationTokenException.java b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationTokenException.java
new file mode 100644
index 00000000..bdf8e68d
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationTokenException.java
@@ -0,0 +1,7 @@
+package fr.insee.pogues.configuration.rest;
+
+public class AuthenticationTokenException extends RuntimeException {
+ public AuthenticationTokenException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationUserHelper.java b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationUserHelper.java
new file mode 100644
index 00000000..4eb7ff50
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/rest/AuthenticationUserHelper.java
@@ -0,0 +1,26 @@
+package fr.insee.pogues.configuration.rest;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AuthenticationUserHelper implements AuthenticationHelper {
+ @Override
+ public String getUserToken() {
+ if(getAuthenticationPrincipal() instanceof JwtAuthenticationToken auth) {
+ return auth.getToken().getTokenValue();
+ }
+ throw new AuthenticationTokenException("Cannot retrieve token for the user.");
+ }
+
+ @Override
+ public Authentication getAuthenticationPrincipal() {
+ return SecurityContextHolder.getContext().getAuthentication();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/rest/WebClientTokenInterceptor.java b/src/main/java/fr/insee/pogues/configuration/rest/WebClientTokenInterceptor.java
new file mode 100644
index 00000000..696e70bc
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/rest/WebClientTokenInterceptor.java
@@ -0,0 +1,25 @@
+package fr.insee.pogues.configuration.rest;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.ExchangeFunction;
+import reactor.core.publisher.Mono;
+
+@RequiredArgsConstructor
+@Slf4j
+public class WebClientTokenInterceptor implements ExchangeFilterFunction {
+
+ private final AuthenticationHelper authenticationHelper;
+
+ @Override
+ public Mono filter(ClientRequest request, ExchangeFunction next) {
+ String jwt = authenticationHelper.getUserToken();
+ ClientRequest newRequest = ClientRequest.from(request)
+ .headers(h -> h.setBearerAuth(jwt))
+ .build();
+ return next.exchange(newRequest);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/swagger/SpringDocConfiguration.java b/src/main/java/fr/insee/pogues/configuration/swagger/SpringDocConfiguration.java
new file mode 100644
index 00000000..98fdef30
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/swagger/SpringDocConfiguration.java
@@ -0,0 +1,78 @@
+package fr.insee.pogues.configuration.swagger;
+
+import fr.insee.pogues.configuration.properties.OidcProperties;
+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.security.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@Configuration
+@ConditionalOnProperty(value="feature.swagger.enabled", havingValue = "true")
+public class SpringDocConfiguration {
+
+ @Value("${fr.insee.pogues.model.version}")
+ private String poguesModelVersion;
+ @Bean
+ @ConditionalOnProperty(name = "feature.oidc.enabled", havingValue = "false")
+ protected OpenAPI noAuthOpenAPI(BuildProperties buildProperties) {
+ return generateOpenAPI(buildProperties);
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "feature.oidc.enabled", havingValue = "true")
+ protected OpenAPI oidcOpenAPI(OidcProperties oidcProperties, BuildProperties buildProperties) {
+ String authUrl = oidcProperties.authServerUrl() + "/realms/" + oidcProperties.realm() + "/protocol/openid-connect";
+ String securitySchemeName = "oauth2";
+
+ return generateOpenAPI(buildProperties)
+ .addSecurityItem(new SecurityRequirement().addList(securitySchemeName, Arrays.asList("read", "write")))
+ .components(
+ new Components()
+ .addSecuritySchemes(securitySchemeName,
+ new SecurityScheme()
+ .name(securitySchemeName)
+ .type(SecurityScheme.Type.OAUTH2)
+ .flows(getFlows(authUrl))
+ )
+ );
+
+ }
+
+ public SpringDocConfiguration(MappingJackson2HttpMessageConverter converter) {
+ var supportedMediaTypes = new ArrayList<>(converter.getSupportedMediaTypes());
+ supportedMediaTypes.add(new MediaType("application", "octet-stream"));
+ converter.setSupportedMediaTypes(supportedMediaTypes);
+ }
+
+ private OpenAPI generateOpenAPI(BuildProperties buildProperties) {
+ return new OpenAPI().info(
+ new Info()
+ .title(buildProperties.getName())
+ .description(
+ "Rest Endpoints and services used by Pogues"
+ + "Pogues-Model version : " +poguesModelVersion + "
")
+ .version(buildProperties.getVersion())
+ );
+ }
+
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/swagger/role/DisplayRolesOnSwaggerUI.java b/src/main/java/fr/insee/pogues/configuration/swagger/role/DisplayRolesOnSwaggerUI.java
new file mode 100644
index 00000000..b3791272
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/swagger/role/DisplayRolesOnSwaggerUI.java
@@ -0,0 +1,43 @@
+package fr.insee.pogues.configuration.swagger.role;
+
+import io.swagger.v3.oas.models.Operation;
+import org.springdoc.core.customizers.OperationCustomizer;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+
+@Component
+public class DisplayRolesOnSwaggerUI implements OperationCustomizer {
+ public static final String AUTHORIZED_ROLES = "Authorized roles: ";
+
+ /**
+ * Display roles allowed to use an endpoint in the description field
+ * @param operation spring doc operation endpoint
+ * @param handlerMethod method handler used to retrieve annotations
+ * @return the operation with role description for UI
+ */
+ @Override
+ public Operation customize(Operation operation, HandlerMethod handlerMethod) {
+ var preAuthorizeAnnotation = handlerMethod.getMethodAnnotation(PreAuthorize.class);
+ StringBuilder description = new StringBuilder();
+ if(preAuthorizeAnnotation == null) {
+ return operation;
+ }
+ if(operation.getDescription() != null) {
+ description
+ .append(operation.getDescription())
+ .append("\n");
+ }
+ description.append(AUTHORIZED_ROLES);
+ String roles = preAuthorizeAnnotation.value();
+ for(RoleUIMapper roleUIMapper : RoleUIMapper.values()) {
+ if(roles.contains(roleUIMapper.getRoleExpression())) {
+ description
+ .append(roleUIMapper.name())
+ .append(" / ");
+ }
+ }
+ operation.setDescription(description.toString());
+ return operation;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/configuration/swagger/role/RoleUIMapper.java b/src/main/java/fr/insee/pogues/configuration/swagger/role/RoleUIMapper.java
new file mode 100644
index 00000000..aa83bcff
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/configuration/swagger/role/RoleUIMapper.java
@@ -0,0 +1,19 @@
+package fr.insee.pogues.configuration.swagger.role;
+
+import fr.insee.pogues.configuration.auth.AuthorityRole;
+
+public enum RoleUIMapper {
+ ADMIN(AuthorityRole.HAS_ADMIN_PRIVILEGES),
+ AUTHENTICATED(AuthorityRole.HAS_ANY_ROLE),
+ INTERVIEWER(AuthorityRole.HAS_ROLE_DESIGNER);
+
+ private final String roleExpression;
+
+ RoleUIMapper(String roleExpression) {
+ this.roleExpression = roleExpression;
+ }
+
+ public String getRoleExpression() {
+ return roleExpression;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/metadata/client/MetadataClientImpl.java b/src/main/java/fr/insee/pogues/metadata/client/MetadataClientImpl.java
index b44fa878..5682df64 100644
--- a/src/main/java/fr/insee/pogues/metadata/client/MetadataClientImpl.java
+++ b/src/main/java/fr/insee/pogues/metadata/client/MetadataClientImpl.java
@@ -1,24 +1,21 @@
package fr.insee.pogues.metadata.client;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.http.entity.ContentType;
+import fr.insee.pogues.configuration.RemoteMetadata;
+import fr.insee.pogues.metadata.model.ColecticaItem;
+import fr.insee.pogues.metadata.model.ColecticaItemRefList;
+import fr.insee.pogues.metadata.model.Unit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
-import fr.insee.pogues.config.RemoteMetadata;
-import fr.insee.pogues.metadata.model.ColecticaItem;
-import fr.insee.pogues.metadata.model.ColecticaItemRefList;
-import fr.insee.pogues.metadata.model.Unit;
+import java.util.Arrays;
+import java.util.List;
+// TODO: change for webclient
@Service
public class MetadataClientImpl implements MetadataClient {
@@ -42,7 +39,7 @@ public ColecticaItem getItem(String id) throws Exception {
public List getItems(ColecticaItemRefList query) throws Exception {
String url = String.format("%s/meta-data/items", remoteMetadata.getUrl());
MultiValueMap headers = new LinkedMultiValueMap<>();
- headers.add("Content-type", ContentType.APPLICATION_JSON.getMimeType());
+ headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
HttpEntity request = new HttpEntity<>(query, headers);
ResponseEntity response = restTemplate
.exchange(url, HttpMethod.POST, request, ColecticaItem[].class);
diff --git a/src/main/java/fr/insee/pogues/persistence/query/QuestionnairesServiceQueryPostgresqlImpl.java b/src/main/java/fr/insee/pogues/persistence/query/QuestionnairesServiceQueryPostgresqlImpl.java
index eb1d9c90..8f4abc22 100644
--- a/src/main/java/fr/insee/pogues/persistence/query/QuestionnairesServiceQueryPostgresqlImpl.java
+++ b/src/main/java/fr/insee/pogues/persistence/query/QuestionnairesServiceQueryPostgresqlImpl.java
@@ -17,7 +17,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
-import fr.insee.pogues.config.auth.security.restrictions.StampsRestrictionsService;
+import fr.insee.pogues.configuration.auth.security.restrictions.StampsRestrictionsService;
import fr.insee.pogues.webservice.rest.PoguesException;
/**
diff --git a/src/main/java/fr/insee/pogues/search/repository/PoguesItemRepositoryImpl.java b/src/main/java/fr/insee/pogues/search/repository/PoguesItemRepositoryImpl.java
index 89efbc6f..43c1e4c4 100644
--- a/src/main/java/fr/insee/pogues/search/repository/PoguesItemRepositoryImpl.java
+++ b/src/main/java/fr/insee/pogues/search/repository/PoguesItemRepositoryImpl.java
@@ -1,6 +1,6 @@
package fr.insee.pogues.search.repository;
-import fr.insee.pogues.config.RemoteMetadata;
+import fr.insee.pogues.configuration.RemoteMetadata;
import fr.insee.pogues.search.model.DDIItem;
import fr.insee.pogues.search.model.DataCollectionContext;
import fr.insee.pogues.search.model.PoguesQuery;
diff --git a/src/main/java/fr/insee/pogues/transforms/visualize/FOToPDFImpl.java b/src/main/java/fr/insee/pogues/transforms/visualize/FOToPDFImpl.java
index 7b55a5a0..d65c71f5 100644
--- a/src/main/java/fr/insee/pogues/transforms/visualize/FOToPDFImpl.java
+++ b/src/main/java/fr/insee/pogues/transforms/visualize/FOToPDFImpl.java
@@ -1,6 +1,6 @@
package fr.insee.pogues.transforms.visualize;
-import fr.insee.pogues.config.StaticResourcesForFOPConfig;
+import fr.insee.pogues.configuration.StaticResourcesForFOPConfig;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.fop.apps.Fop;
diff --git a/src/main/java/fr/insee/pogues/utils/jersey/ObjectMapperProvider.java b/src/main/java/fr/insee/pogues/utils/jersey/ObjectMapperProvider.java
deleted file mode 100644
index 0e53bd48..00000000
--- a/src/main/java/fr/insee/pogues/utils/jersey/ObjectMapperProvider.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package fr.insee.pogues.utils.jersey;
-
-
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-
-import javax.ws.rs.ext.ContextResolver;
-import javax.ws.rs.ext.Provider;
-
-import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
-
-/**
- * Created by acordier on 13/07/17.
- */
-@Provider
-public class ObjectMapperProvider implements ContextResolver {
-
- @Override
- public ObjectMapper getContext(Class> aClass) {
- return createObjectMapper();
- }
-
- private ObjectMapper createObjectMapper() {
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
- objectMapper.setVisibility(PropertyAccessor.FIELD, ANY);
-
- return objectMapper;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/fr/insee/pogues/utils/xsl/XSLTransformation.java b/src/main/java/fr/insee/pogues/utils/xsl/XSLTransformation.java
deleted file mode 100644
index 0a88e302..00000000
--- a/src/main/java/fr/insee/pogues/utils/xsl/XSLTransformation.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package fr.insee.pogues.utils.xsl;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import javax.xml.transform.Transformer;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.transform.stream.StreamSource;
-import java.io.File;
-
-/**
- * Main Saxon Service used to perform XSLT transformations
- *
- * @author I6VWID
- *
- */
-public class XSLTransformation {
-
- final static Logger logger = LogManager.getLogger(XSLTransformation.class);
-
- /**
- * Main Saxon transformation method
- *
- * @param transformer
- * : The defined transformer with his embedded parameters
- * (defined in the other methods of this class)
- * @param xmlInput
- * : The input xml file where the XSLT will be applied
- * @param xmlOutput
- * : The output xml file after the transformation
- * @throws Exception
- * : Mainly if the input/output files path are incorrect
- */
- public void xslTransform(Transformer transformer, String xmlInput, String xmlOutput) throws Exception {
- logger.debug("Starting xsl transformation -Input : " + xmlInput + " -Output : " + xmlOutput);
- transformer.transform(new StreamSource(new File(xmlInput)), new StreamResult(new File(xmlOutput)));
- }
-
-}
diff --git a/src/main/java/fr/insee/pogues/webservice/rest/PoguesPersistence.java b/src/main/java/fr/insee/pogues/webservice/rest/PoguesPersistence.java
index a6998d36..de023b27 100644
--- a/src/main/java/fr/insee/pogues/webservice/rest/PoguesPersistence.java
+++ b/src/main/java/fr/insee/pogues/webservice/rest/PoguesPersistence.java
@@ -1,10 +1,9 @@
package fr.insee.pogues.webservice.rest;
-import fr.insee.pogues.config.auth.UserProvider;
-import fr.insee.pogues.config.auth.user.User;
+import fr.insee.pogues.configuration.auth.UserProvider;
+import fr.insee.pogues.configuration.auth.user.User;
import fr.insee.pogues.persistence.service.QuestionnairesService;
import fr.insee.pogues.persistence.service.VariablesService;
-import fr.insee.pogues.transforms.visualize.PoguesJSONToPoguesJSONDeref;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -12,17 +11,10 @@
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
-import java.util.*;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;