timetable/module-server/src/main/kotlin/de/twomartens/timetable/support/configuration/WebSecurityConfiguration.kt

96 lines
4.3 KiB
Kotlin

package de.twomartens.timetable.support.configuration
import de.twomartens.timetable.support.security.SpringPolicyEnforcerFilter
import org.keycloak.adapters.authorization.spi.ConfigurationResolver
import org.keycloak.adapters.authorization.spi.HttpRequest
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig
import org.keycloak.util.JsonSerialization
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
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.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter
import org.springframework.security.web.SecurityFilterChain
import java.io.IOException
@Configuration
@EnableWebSecurity
open class WebSecurityConfiguration {
@Value("\${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
private lateinit var jwkSetUri: String
@Value("#{environment.CLIENT_SECRET}")
private lateinit var clientSecret: String
@Bean
@Throws(Exception::class)
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.authorizeHttpRequests { it.requestMatchers(*PERMITTED_PATHS.toTypedArray<String>()).permitAll() }
.authorizeHttpRequests { it.requestMatchers(HttpMethod.OPTIONS).permitAll() }
.authorizeHttpRequests { it.anyRequest().authenticated() }
.oauth2ResourceServer { obj: OAuth2ResourceServerConfigurer<HttpSecurity?> -> obj.jwt(Customizer.withDefaults()) }
.addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter::class.java)
return http.build()
}
@Bean
open fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()
}
private fun createPolicyEnforcerFilter(): SpringPolicyEnforcerFilter {
return SpringPolicyEnforcerFilter(object : ConfigurationResolver {
override fun resolve(request: HttpRequest): PolicyEnforcerConfig {
return try {
val policyEnforcerConfig = JsonSerialization.readValue(
javaClass.getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig::class.java
)
policyEnforcerConfig.credentials = mapOf(Pair("secret", clientSecret))
if (request.method == HttpMethod.OPTIONS.name()) {
// always allow options request
policyEnforcerConfig.enforcementMode = EnforcementMode.DISABLED
} else {
policyEnforcerConfig.paths = PATHS
}
policyEnforcerConfig
} catch (e: IOException) {
throw RuntimeException(e)
}
}
})
}
companion object {
private val PERMITTED_PATHS: Collection<String> = listOf(
"/timetable/v1/healthCheck",
"/actuator/**",
"/timetable/v1/doc/**",
"/timetable/v1/api-docs/**",
"/error",
"/timetable/version",
)
private val PATHS = buildPathConfigs()
private fun buildPathConfigs(): List<PathConfig> {
val paths: MutableList<PathConfig> = mutableListOf()
for (path in PERMITTED_PATHS) {
val pathConfig = PathConfig()
pathConfig.path = path.replace("**", "*")
pathConfig.enforcementMode = EnforcementMode.DISABLED
paths.add(pathConfig)
}
return paths
}
}
}