diff --git a/README.md b/README.md index bfdfeaec0ed787d554eac03fa1d871db3d2e2834..c4027497c93d8d8ba7ae87cd304c407c49987b4b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ occmd.occmd-path=${OCCMD_PATH} security.api-key=${API_KEY} security.admin-username=${ADMIN_PASSWORD} security.admin-password=${ADMIN_PASSWORD} +security.cors-origin=${CORS_ORIGIN:} // a local repository to which the git repositories are temporarily cloned. // they are automatically deleted after every tool run. directories.git-clone-target-directory=${GIT_CLONE_TARGET_DIRECTORY} @@ -50,4 +51,4 @@ This file is automatically loaded by the IDE. ### Important Note for the dev deployment and testing. The dev ORT API doesn't have results for all projects. Thus make sure to edit the `MetricsService.kt` to -point to the repository with id 106 for testing purposes. \ No newline at end of file +point to the repository with id 106 for testing purposes. diff --git a/kubernetes/configmap-dev.yaml b/kubernetes/configmap-dev.yaml index 0e23bdab381394db804a1b35743e9632582e3072..e3090bf34d2f5336446d245e53067d0f4b5a14e0 100644 --- a/kubernetes/configmap-dev.yaml +++ b/kubernetes/configmap-dev.yaml @@ -8,3 +8,4 @@ data: PORT: "5000" MANAGEMENT_PORT: "5001" XDG_CONFIG_HOME: "/app/.config" + CORS_ORIGIN: "https://dev-kpi.o4oe.de" diff --git a/kubernetes/configmap.yaml b/kubernetes/configmap.yaml index 39948c4ff4a6eee944848a2bb73730316a70b335..6c3bc2a12dc4a76e43ccb02388d75297d7a7bb4e 100644 --- a/kubernetes/configmap.yaml +++ b/kubernetes/configmap.yaml @@ -8,3 +8,4 @@ data: PORT: "5000" MANAGEMENT_PORT: "5001" XDG_CONFIG_HOME: "/app/.config" + CORS_ORIGIN: "https://dev-kpi.o4oe.de" diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/ApiKeyService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/ApiKeyService.kt index dd6f83985d49f28b8d33d211c796213004c315d3..907257dbb88786957ac6e3ea4db7fef2673eb973 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/ApiKeyService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/ApiKeyService.kt @@ -2,6 +2,7 @@ package de.fraunhofer.iem.dataprovider.configuration.security import org.springframework.http.HttpStatus import org.springframework.stereotype.Component +import org.springframework.web.cors.reactive.CorsUtils.isCorsRequest import org.springframework.web.server.ServerWebExchange import org.springframework.web.server.WebFilter import org.springframework.web.server.WebFilterChain @@ -15,6 +16,12 @@ class ApiKeyFilter(private val securityProperties: SecurityProperties) : WebFilt override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> { val apiKey = exchange.request.headers.getFirst(API_KEY_HEADER) + + // If the request is associated with cors, we will just return it instead of checking the API key + if (isCorsRequest(exchange.request)) { + return chain.filter(exchange) + } + return if (apiKey == null) { exchange.response.setStatusCode(HttpStatus.BAD_REQUEST) val buffer = exchange.response.bufferFactory().wrap(API_HEADER_NOT_FOUND_MSG.encodeToByteArray()) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/SecurityProperties.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/SecurityProperties.kt index da264d37982d089c5d1a209d689b177db92cc59f..733fd8075d36c3e4c8c82ec63ee3ff715a5bb3a9 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/SecurityProperties.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/SecurityProperties.kt @@ -16,7 +16,11 @@ data class SecurityProperties( @field:Length(min = 5) val adminUsername: String, + @field:NotBlank @field:Length(min = 30) - val apiKey: String + val apiKey: String, + + @field:NotBlank + val corsOrigin: String ) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebFluxConfiguration.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebFluxConfiguration.kt deleted file mode 100644 index 24e5bf2280b8dc9d547266c0ab9f6ed8004c9ad2..0000000000000000000000000000000000000000 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebFluxConfiguration.kt +++ /dev/null @@ -1,17 +0,0 @@ -package de.fraunhofer.iem.dataprovider.configuration.security - -import org.springframework.context.annotation.Configuration -import org.springframework.web.reactive.config.CorsRegistry -import org.springframework.web.reactive.config.EnableWebFlux -import org.springframework.web.reactive.config.WebFluxConfigurer - -@Configuration -@EnableWebFlux -class WebFluxConfiguration : WebFluxConfigurer { - override fun addCorsMappings(registry: CorsRegistry) { - registry.addMapping("/**") - .allowedOrigins("*") // any host or put domain(s) here - .allowedMethods("*") // put the http verbs you want allow - .allowedHeaders("*") // put the http headers you want allow - } -} diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebSecurityConfiguration.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebSecurityConfiguration.kt index 95d11841eef47e7cdad376102239ac0a8d52fad9..72027ec2c5bb6fd5549644ab63c11d1e14e188a0 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebSecurityConfiguration.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/security/WebSecurityConfiguration.kt @@ -13,6 +13,10 @@ import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.reactive.CorsWebFilter +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource + const val ADMIN_ROLE: String = "ADMIN" @@ -30,6 +34,23 @@ class SecurityConfiguration(val apiKeyFilter: ApiKeyFilter, private val security return MapReactiveUserDetailsService(user) } + /** + * This filter is for local development to allow CORS preflight requests. + */ + @Bean + fun corsFilter(): CorsWebFilter { + val config = CorsConfiguration() + config.allowCredentials = false + config.addAllowedOrigin(securityProperties.corsOrigin) + config.addAllowedHeader("*") + config.addAllowedMethod("*") + + val source = UrlBasedCorsConfigurationSource().apply { + registerCorsConfiguration("/**", config) + } + return CorsWebFilter(source) + } + /** * This filter chain first checks whether this server's api key is present in the * request header in the api-key header. @@ -41,6 +62,8 @@ class SecurityConfiguration(val apiKeyFilter: ApiKeyFilter, private val security @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http { + cors { + } addFilterAt(apiKeyFilter, SecurityWebFiltersOrder.FIRST) authorizeExchange { authorize(ApiPaths.ACTUATOR, permitAll) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d0a13474a0a0d1292c184264f36003ed055273c1..b0bf835a5abd8649cb921fc904f3b03891045df4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -14,6 +14,7 @@ opencode.api.ort=/cve-result security.api-key=${API_KEY:} security.admin-password=${ADMIN_PASSWORD:} security.admin-username=${ADMIN_USERNAME:} +security.cors-origin=${CORS_ORIGIN:} # OCCMD specific settings # path to the occmd tool executable # this can e.g, be the occmd.sh script in this project