diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f88f586..d10f3bc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,11 @@ # Release-Notes -## Sprint 7 (05.03.2024 - 26.03.2024) +## Sprint 9 (26.03.2024 - 16.04.2024) +### Hinzugefügt +- Security Absicherung +- Deployment + +## Sprint 8 (05.03.2024 - 26.03.2024) ### Hinzugefügt - Archivierung - Url vorsignierte Adresse erstellen diff --git a/docs/README.md b/docs/README.md index 3c60596..f8fb905 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,7 +49,19 @@ Das Profil erzeugt die Openapi Java Source Dateien im Maven _target_ Ordner. Die Openapi Quelle kann mit dem [Swagger Editor](https://editor.swagger.io) bearbeitet werden. -## REST Schnittstelle +## Security +Wird die EAI im Security Modus gestartet, muss der Aufrufer der REST Schnittstelle ein gültigen OAuth 2.0 Token mitliefern, sonst wird die Anfrage mit dem HTTP Status Code 401 "Unauthorized" abgelehnt. +Das gilt auch für einen abgelaufenen Token. + +Zu Testzwecken kann ein Token bsp.weise mit curl vom SSO Provider bezogen werden : + +curl \ +-d "client_id=[client_id]" \ +-d "client_secret=[client_secret]" \ +-d "grant_type=client_credentials" \ +"https://..." + +# REST Schnittstelle Mit dem [Swagger Editor](https://editor.swagger.io) kann die komplette [Openapi REST Beschreibung](https://github.com/it-at-m/mobidam-s3-eai/blob/sprint/src/main/resources/openapi_rest_s3_v1.yaml) angezeigt werden. Der Workflow für den Import von Dateien in FME sieht folgende Schritt vor: - Anzeigen von Inhalten eines S3 Buckets. diff --git a/pom.xml b/pom.xml index 6afa35f..fa4810f 100644 --- a/pom.xml +++ b/pom.xml @@ -168,6 +168,24 @@ 1.7.0 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.security + spring-security-oauth2-jose + + org.springframework.boot @@ -247,6 +265,14 @@ jakarta.validation-api 3.0.2 + + + + net.logstash.logback + logstash-logback-encoder + 7.0.1 + + diff --git a/src/main/java/de/muenchen/mobidam/config/NoSecurityConfiguration.java b/src/main/java/de/muenchen/mobidam/config/NoSecurityConfiguration.java new file mode 100644 index 0000000..d62862e --- /dev/null +++ b/src/main/java/de/muenchen/mobidam/config/NoSecurityConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik + * der Landeshauptstadt München, 2022 + */ +package de.muenchen.mobidam.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +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.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@Profile("no-security") +@EnableWebSecurity +public class NoSecurityConfiguration { + + /** + * Disable security. + */ + @Bean + public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { + http + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + .authorizeHttpRequests(request -> request.requestMatchers(AntPathRequestMatcher.antMatcher("/**")).permitAll().anyRequest().permitAll()) + .csrf(AbstractHttpConfigurer::disable); + return http.build(); + } +} diff --git a/src/main/java/de/muenchen/mobidam/config/SecurityConfiguration.java b/src/main/java/de/muenchen/mobidam/config/SecurityConfiguration.java new file mode 100644 index 0000000..6387d27 --- /dev/null +++ b/src/main/java/de/muenchen/mobidam/config/SecurityConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik + * der Landeshauptstadt München, 2022 + */ +package de.muenchen.mobidam.config; + +import static org.springframework.security.config.Customizer.withDefaults; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +/** + * The central class for configuration of all security aspects. + */ +@Configuration +@Profile("!no-security") +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +@RequiredArgsConstructor +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { + + return http + .authorizeHttpRequests((requests) -> requests.requestMatchers(AntPathRequestMatcher.antMatcher("/**"), + // allow access to /actuator/info + AntPathRequestMatcher.antMatcher("/actuator/info"), + // allow access to /actuator/health for OpenShift Health Check + AntPathRequestMatcher.antMatcher("/actuator/health"), + // allow access to /actuator/health/liveness for OpenShift Liveness Check + AntPathRequestMatcher.antMatcher("/actuator/health/liveness"), + // allow access to /actuator/health/readiness for OpenShift Readiness Check + AntPathRequestMatcher.antMatcher("/actuator/health/readiness"), + // allow access to /actuator/metrics for Prometheus monitoring in OpenShift + AntPathRequestMatcher.antMatcher("/actuator/metrics")) + .permitAll()) + .authorizeHttpRequests((requests) -> requests.requestMatchers(AntPathRequestMatcher.antMatcher("/**")) + .authenticated()) + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(withDefaults())) + .build(); + + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d934591..b9d776e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,11 +23,11 @@ mobidam: s3: bucket-credential-config: x-itmkm82k: - access-key-env-var: MOBIDAM_X-ITMKM82K_ACCESS_KEY - secret-key-env-var: MOBIDAM_X-ITMKM82K_SECRET_KEY + access-key-env-var: MOBIDAM_ACCESS_KEY + secret-key-env-var: MOBIDAM_SECRET_KEY int-mdasc-mdasdev: - access-key-env-var: MOBIDAM_INT-MDASC-MDASDEV_ACCESS_KEY - secret-key-env-var: MOBIDAM_INT-MDASC-MDASDEV_SECRET_KEY + access-key-env-var: MOBIDAM_ACCESS_KEY + secret-key-env-var: MOBIDAM_SECRET_KEY spring: application: @@ -51,6 +51,15 @@ spring: hibernate: format_sql: true dialect: ... + security: + oauth2: + resource-server: + jwt: + issuer-uri: ${keycloak.auth-server-url}/realms/${realm} + jwk-set-uri: ${keycloak.auth-server-url}/realms/${realm}/protocol/openid-connect/certs + +realm: ... +keycloak.auth-server-url: ... # https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html management: diff --git a/src/test/java/de/muenchen/mobidam/TestConstants.java b/src/test/java/de/muenchen/mobidam/TestConstants.java new file mode 100644 index 0000000..ae35b5d --- /dev/null +++ b/src/test/java/de/muenchen/mobidam/TestConstants.java @@ -0,0 +1,13 @@ +/* + * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik + * der Landeshauptstadt München, 2022 + */ +package de.muenchen.mobidam; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class TestConstants { + public static final String SPRING_NO_SECURITY_PROFILE = "no-security"; +} diff --git a/src/test/java/de/muenchen/mobidam/rest/S3ApiArchiveTest.java b/src/test/java/de/muenchen/mobidam/rest/S3ApiArchiveTest.java index 26c416a..bc2f4c2 100644 --- a/src/test/java/de/muenchen/mobidam/rest/S3ApiArchiveTest.java +++ b/src/test/java/de/muenchen/mobidam/rest/S3ApiArchiveTest.java @@ -6,6 +6,7 @@ import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import org.apache.camel.EndpointInject; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; @@ -16,6 +17,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @CamelSpringBootTest @@ -26,6 +28,7 @@ @EnableAutoConfiguration @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @TestPropertySource(properties = { "camel.route.common=mock:common" }) +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ApiArchiveTest { @Produce("http:127.0.0.1:8081/api") diff --git a/src/test/java/de/muenchen/mobidam/rest/S3ApiFilesInFolderTest.java b/src/test/java/de/muenchen/mobidam/rest/S3ApiFilesInFolderTest.java index f193010..734c54a 100644 --- a/src/test/java/de/muenchen/mobidam/rest/S3ApiFilesInFolderTest.java +++ b/src/test/java/de/muenchen/mobidam/rest/S3ApiFilesInFolderTest.java @@ -6,6 +6,7 @@ import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import org.apache.camel.EndpointInject; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; @@ -16,6 +17,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @CamelSpringBootTest @@ -26,6 +28,7 @@ @EnableAutoConfiguration @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @TestPropertySource(properties = { "camel.route.common=mock:common" }) +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ApiFilesInFolderTest { @Produce("http:127.0.0.1:8081/api") diff --git a/src/test/java/de/muenchen/mobidam/rest/S3ApiPresignedUrlTest.java b/src/test/java/de/muenchen/mobidam/rest/S3ApiPresignedUrlTest.java index e4fd66a..4aa8336 100644 --- a/src/test/java/de/muenchen/mobidam/rest/S3ApiPresignedUrlTest.java +++ b/src/test/java/de/muenchen/mobidam/rest/S3ApiPresignedUrlTest.java @@ -6,6 +6,7 @@ import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import org.apache.camel.EndpointInject; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; @@ -16,6 +17,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @CamelSpringBootTest @@ -26,6 +28,7 @@ @EnableAutoConfiguration @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @TestPropertySource(properties = { "camel.route.common=mock:common" }) +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ApiPresignedUrlTest { @Produce("http:127.0.0.1:8081/api") diff --git a/src/test/java/de/muenchen/mobidam/s3/S3ArchiveTest.java b/src/test/java/de/muenchen/mobidam/s3/S3ArchiveTest.java index ae36bed..d89f0a9 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3ArchiveTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3ArchiveTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.repository.ArchiveRepository; import java.io.File; import java.net.URI; @@ -28,6 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -48,6 +50,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ArchiveTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3BucketTest.java b/src/test/java/de/muenchen/mobidam/s3/S3BucketTest.java index 49d717c..dbe0870 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3BucketTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3BucketTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.ErrorResponse; import java.math.BigDecimal; import java.net.URI; @@ -26,6 +27,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpStatus; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -51,6 +53,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3BucketTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3FileLimitTest.java b/src/test/java/de/muenchen/mobidam/s3/S3FileLimitTest.java index 4a7aaf6..1b5b83c 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3FileLimitTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3FileLimitTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.BucketContentInner; import java.io.File; import java.net.URI; @@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -48,6 +50,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3FileLimitTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3ObjectTest.java b/src/test/java/de/muenchen/mobidam/s3/S3ObjectTest.java index fc6c0e8..2c3bd62 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3ObjectTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3ObjectTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.BucketContentInner; import java.io.File; import java.net.URI; @@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -47,6 +49,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ObjectTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3PrefixTest.java b/src/test/java/de/muenchen/mobidam/s3/S3PrefixTest.java index 128c7e7..a84fc2c 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3PrefixTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3PrefixTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.BucketContentInner; import java.io.File; import java.net.URI; @@ -27,6 +28,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -47,6 +49,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3PrefixTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3PresignedUrlTest.java b/src/test/java/de/muenchen/mobidam/s3/S3PresignedUrlTest.java index b3c318d..cd764a0 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3PresignedUrlTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3PresignedUrlTest.java @@ -8,6 +8,7 @@ import com.robothy.s3.rest.bootstrap.LocalS3Mode; import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.ErrorResponse; import de.muenchen.mobidam.rest.PresignedUrl; import java.io.File; @@ -28,6 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -48,6 +50,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3PresignedUrlTest { @Produce diff --git a/src/test/java/de/muenchen/mobidam/s3/S3ServletContextPathNotFoundTest.java b/src/test/java/de/muenchen/mobidam/s3/S3ServletContextPathNotFoundTest.java index ca9bbb6..d85c51e 100644 --- a/src/test/java/de/muenchen/mobidam/s3/S3ServletContextPathNotFoundTest.java +++ b/src/test/java/de/muenchen/mobidam/s3/S3ServletContextPathNotFoundTest.java @@ -6,6 +6,7 @@ import de.muenchen.mobidam.Application; import de.muenchen.mobidam.Constants; +import de.muenchen.mobidam.TestConstants; import de.muenchen.mobidam.rest.ErrorResponse; import org.apache.camel.CamelContext; import org.apache.camel.Produce; @@ -18,6 +19,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; @CamelSpringBootTest @SpringBootTest( @@ -26,6 +28,7 @@ ) @EnableAutoConfiguration @DirtiesContext +@ActiveProfiles(TestConstants.SPRING_NO_SECURITY_PROFILE) class S3ServletContextPathNotFoundTest { @Produce