diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 656d918..5e365b9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -126,8 +126,6 @@ spring-boot-server = [ "spring-ui", ] spring-boot-security = [ - "keycloak-core", - "keycloak-policy-enforcer", "spring-boot-oauth2-resource-server", "spring-boot-security", ] diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt index da453c7..d3c679a 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/auth/UserController.kt @@ -14,6 +14,9 @@ import io.swagger.v3.oas.annotations.tags.Tag import org.mapstruct.factory.Mappers import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.security.authorization.AuthorizationDeniedException +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.oauth2.jwt.Jwt import org.springframework.web.bind.annotation.* @RestController @@ -60,6 +63,9 @@ class UserController( ) ]) body: User ): ResponseEntity { + val authentication = SecurityContextHolder.getContext().authentication + val jwt = authentication as Jwt + val authorizedParty = jwt.getClaimAsString("azp") var created = false val userIdConverted = UserId.of(NonEmptyString(userId)) @@ -67,9 +73,11 @@ class UserController( if (user == null) { created = true user = mapper.mapToDB(body) - } else { + } else if (authorizedParty == userId) { user.name = NonEmptyString(body.name) user.email = Email.of(NonEmptyString(body.email)) + } else { + throw AuthorizationDeniedException("The user is not authorized to update this user") } userRepository.save(user) diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/AuthorizationManagerConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/AuthorizationManagerConfiguration.kt new file mode 100644 index 0000000..008695e --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/AuthorizationManagerConfiguration.kt @@ -0,0 +1,24 @@ +package de.twomartens.timetable.configuration + +import de.twomartens.timetable.configuration.roles.CheckParty +import de.twomartens.timetable.configuration.roles.PartyAuthorizationManager +import org.springframework.aop.support.annotation.AnnotationMatchingPointcut +import org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Role +import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder +import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor + +@Configuration +class AuthorizationManagerConfiguration { + + @Bean + @Role(ROLE_INFRASTRUCTURE) + fun checkPartyInterceptor(): AuthorizationManagerBeforeMethodInterceptor { + val pointcut = AnnotationMatchingPointcut(null, CheckParty::class.java, true) + val interceptor = AuthorizationManagerBeforeMethodInterceptor(pointcut, PartyAuthorizationManager()) + interceptor.order = AuthorizationInterceptorsOrder.PRE_AUTHORIZE.order + return interceptor + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/CorsConfig.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/CorsConfig.kt new file mode 100644 index 0000000..91e1ee5 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/CorsConfig.kt @@ -0,0 +1,26 @@ +package de.twomartens.timetable.configuration + +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.CorsConfigurationSource +import org.springframework.web.cors.UrlBasedCorsConfigurationSource + +@Configuration +class CorsConfig { + + @Bean + @Qualifier("corsApplication") + fun corsConfigurationSource(): CorsConfigurationSource { + val configuration = CorsConfiguration() + configuration.allowedOrigins = listOf("http://localhost:8100", "https://timetable.2martens.de") // frontend origin + configuration.allowedMethods = listOf("GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS") + configuration.allowedHeaders = listOf("*") + configuration.allowCredentials = true + + val source = UrlBasedCorsConfigurationSource() + source.registerCorsConfiguration("/**", configuration) + return source + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt index b17e72c..a638f67 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/WebSecurityConfiguration.kt @@ -1,73 +1,36 @@ package de.twomartens.timetable.configuration -import de.twomartens.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.beans.factory.annotation.Qualifier 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.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.oauth2.server.resource.OAuth2ResourceServerConfigurer -import org.springframework.security.oauth2.jwt.JwtDecoder -import org.springframework.security.oauth2.jwt.NimbusJwtDecoder +import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.web.SecurityFilterChain -import java.io.IOException +import org.springframework.web.cors.CorsConfigurationSource @Configuration @EnableWebSecurity +@EnableMethodSecurity 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 { + open fun securityFilterChain(http: HttpSecurity, + @Qualifier("corsApplication") corsConfiguration: CorsConfigurationSource): SecurityFilterChain { http + .cors { it.configurationSource(corsConfiguration) } .csrf { it.disable() } + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } .authorizeHttpRequests { it.requestMatchers(*PERMITTED_PATHS.toTypedArray()).permitAll() } .authorizeHttpRequests { it.requestMatchers(HttpMethod.OPTIONS).permitAll() } .authorizeHttpRequests { it.anyRequest().authenticated() } - .oauth2ResourceServer { obj: OAuth2ResourceServerConfigurer -> obj.jwt(Customizer.withDefaults()) } + .oauth2ResourceServer { it.jwt(Customizer.withDefaults()) } 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 = listOf( "/timetable/healthCheck", @@ -77,17 +40,5 @@ open class WebSecurityConfiguration { "/error", "/timetable/version", ) - private val PATHS = buildPathConfigs() - - private fun buildPathConfigs(): List { - val paths: MutableList = mutableListOf() - for (path in PERMITTED_PATHS) { - val pathConfig = PathConfig() - pathConfig.path = path.replace("**", "*") - pathConfig.enforcementMode = EnforcementMode.DISABLED - paths.add(pathConfig) - } - return paths - } } } diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/CheckParty.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/CheckParty.kt new file mode 100644 index 0000000..74e5ce8 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/CheckParty.kt @@ -0,0 +1,11 @@ +package de.twomartens.timetable.configuration.roles + +import java.lang.annotation.Inherited + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) +@Retention(AnnotationRetention.RUNTIME) +@Inherited +@MustBeDocumented +annotation class CheckParty( + val partyParamName: String // Name of method argument holding the party ID +) \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/PartyAuthorizationManager.kt b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/PartyAuthorizationManager.kt new file mode 100644 index 0000000..5a05208 --- /dev/null +++ b/module-server/src/main/kotlin/de/twomartens/timetable/configuration/roles/PartyAuthorizationManager.kt @@ -0,0 +1,43 @@ +package de.twomartens.timetable.configuration.roles + +import org.aopalliance.intercept.MethodInvocation +import org.springframework.security.authorization.AuthorizationDecision +import org.springframework.security.authorization.AuthorizationDeniedException +import org.springframework.security.authorization.AuthorizationManager +import org.springframework.security.authorization.AuthorizationResult +import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler +import org.springframework.security.core.Authentication +import java.util.function.Supplier + +class PartyAuthorizationManager : AuthorizationManager, MethodAuthorizationDeniedHandler { + @Deprecated("Deprecated in Spring Security") + override fun check(authentication: Supplier?, invocation: MethodInvocation?): AuthorizationDecision { + if (authentication == null || authentication.get() == null || !authentication.get()!!.isAuthenticated) { + return AuthorizationDecision(false) + } + + val annotation = invocation!!.method.getAnnotation(CheckParty::class.java) + ?: invocation.method.declaringClass.getAnnotation(CheckParty::class.java) + ?: return AuthorizationDecision(true) // No annotation, allow + + val authentication = authentication.get()!! + val partyParamName = annotation.partyParamName + + // 1. Authorized party check + val methodParams = invocation.method.parameters + val argIndex = methodParams.indexOfFirst { it.name == partyParamName } + if (argIndex == -1) { + return AuthorizationDecision(false) + } + + val requestedPartyId = invocation.arguments[argIndex]?.toString() + val tokenPartyId = (authentication.details as? Map<*, *>)?.get("azp")?.toString() + ?: authentication.name // fallback + + return AuthorizationDecision(requestedPartyId == tokenPartyId) + } + + override fun handleDeniedInvocation(methodInvocation: MethodInvocation?, authorizationResult: AuthorizationResult?): Any? { + throw AuthorizationDeniedException("The requested resources are not accessible to the authorized party") + } +} \ No newline at end of file diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt index 280d4fc..2c123bb 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/formation/FormationController.kt @@ -1,6 +1,7 @@ package de.twomartens.timetable.formation import de.twomartens.timetable.auth.UserRepository +import de.twomartens.timetable.configuration.roles.CheckParty import de.twomartens.timetable.model.common.FormationId import de.twomartens.timetable.model.common.UserId import de.twomartens.timetable.model.dto.Formation @@ -44,6 +45,7 @@ class FormationController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @GetMapping("/{userId}/") fun getFormations( @PathVariable @Parameter(description = "The id of the user", @@ -96,6 +98,7 @@ class FormationController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @GetMapping("/{userId}/{id}") fun getFormation( @PathVariable @Parameter(description = "The id of the user", @@ -144,6 +147,7 @@ class FormationController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @PutMapping("/{userId}/{id}") fun putFormation( @PathVariable @Parameter(description = "The id of the user", @@ -236,6 +240,7 @@ class FormationController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @DeleteMapping("/{userId}/{id}") fun deleteFormation( @PathVariable @Parameter(description = "The id of the user", diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt index 34f6de5..3b17111 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/route/RouteController.kt @@ -1,6 +1,7 @@ package de.twomartens.timetable.route import de.twomartens.timetable.auth.UserRepository +import de.twomartens.timetable.configuration.roles.CheckParty import de.twomartens.timetable.model.common.RouteId import de.twomartens.timetable.model.common.UserId import de.twomartens.timetable.model.dto.TswRoute @@ -43,6 +44,7 @@ class RouteController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @GetMapping("/{userId}/") fun getRoutes( @PathVariable @Parameter(description = "The id of the user", @@ -87,6 +89,7 @@ class RouteController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @GetMapping("/{userId}/{id}") fun getRoute( @PathVariable @Parameter(description = "The id of the user", @@ -130,6 +133,7 @@ class RouteController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @PutMapping("/{userId}/{id}") fun putRoute( @PathVariable @Parameter(description = "The id of the user", @@ -195,6 +199,7 @@ class RouteController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @CheckParty(partyParamName = "userId") @DeleteMapping("/{userId}/{id}") fun deleteRoute( @PathVariable @Parameter(description = "The id of the user", diff --git a/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt b/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt index 6b07186..bdf26d9 100644 --- a/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt +++ b/module-server/src/main/kotlin/de/twomartens/timetable/station/StationController.kt @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement import io.swagger.v3.oas.annotations.tags.Tag import org.mapstruct.factory.Mappers import org.springframework.http.ResponseEntity +import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.* @RestController @@ -87,6 +88,7 @@ class StationController( ) @SecurityRequirement(name = "bearer") @SecurityRequirement(name = "oauth2") + @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping("/update") fun updateStations(): ResponseEntity { val stations = bahnApiService.fetchStations("*") diff --git a/module-server/src/main/resources/application.yaml b/module-server/src/main/resources/application.yaml index 9d695c9..91e3ada 100644 --- a/module-server/src/main/resources/application.yaml +++ b/module-server/src/main/resources/application.yaml @@ -38,7 +38,9 @@ spring: oauth2: resourceserver: jwt: + jws-algorithms: ES256 jwk-set-uri: ${KEYCLOAK_URL:https://id.2martens.de}/realms/${KEYCLOAK_REALM:twomartens}/protocol/openid-connect/certs + issuer-uri: ${KEYCLOAK_URL:https://id.2martens.de}/realms/${KEYCLOAK_REALM:twomartens} application: name: timetable @@ -79,10 +81,8 @@ de.twomartens.timetable: bahn-api: client-id: ${DB_CLIENT_ID} client-secret: ${DB_CLIENT_SECRET} - greeting: "Hello %s, on millenium" statusprobe: scheduleDuration: 60 - maxKafkaFailureDuration: 5 maxBlobFailureCount: 5 maxFailurePercent: 50 de.twomartens.support.health.greeting: "Good morning" diff --git a/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt b/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt index 33cd0ff..b9e5bb1 100644 --- a/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt +++ b/module-support/src/main/kotlin/de/twomartens/support/controller/ExceptionController.kt @@ -5,6 +5,7 @@ import de.twomartens.support.model.dto.ErrorMessage import mu.KotlinLogging import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.security.authorization.AuthorizationDeniedException import org.springframework.web.bind.annotation.ControllerAdvice import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestController @@ -23,6 +24,12 @@ class ExceptionController : ResponseEntityExceptionHandler() { .body(ErrorMessage(e.message!!)) } + @ExceptionHandler(AuthorizationDeniedException::class) + fun handleDeniedException(e: AuthorizationDeniedException): ResponseEntity { + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(ErrorMessage(e.message!!)) + } + @ExceptionHandler(RuntimeException::class) fun handleRuntimeException(e: RuntimeException): ResponseEntity { log.error("unexpected exception occurred", e) diff --git a/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt deleted file mode 100644 index 91fd6bc..0000000 --- a/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcer.kt +++ /dev/null @@ -1,65 +0,0 @@ -package de.twomartens.support.security - -import mu.KotlinLogging -import org.keycloak.AuthorizationContext -import org.keycloak.adapters.authorization.PolicyEnforcer -import org.keycloak.adapters.authorization.spi.HttpRequest -import org.keycloak.adapters.authorization.spi.HttpResponse -import org.keycloak.authorization.client.ClientAuthorizationContext -import org.keycloak.representations.adapters.config.PolicyEnforcerConfig -import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode -import org.keycloak.representations.idm.authorization.Permission - -class SpringPolicyEnforcer(private val policyEnforcer: PolicyEnforcer, - private val policyEnforcerConfig: PolicyEnforcerConfig) { - - fun enforce(request: HttpRequest, response: HttpResponse): AuthorizationContext { - if (log.isDebugEnabled) { - log.debug("Policy enforcement is enabled. Enforcing policy decisions for path [{}].", request.uri) - } - val context = authorize(request, response) - if (log.isDebugEnabled) { - log.debug("Policy enforcement result for path [{}] is : {}", request.uri, if (context.isGranted) "GRANTED" else "DENIED") - log.debug("Returning authorization context with permissions:") - for (permission in context.permissions) { - log.debug(permission.toString()) - } - } - return context - } - - private fun authorize(request: HttpRequest, response: HttpResponse): AuthorizationContext { - val enforcementMode = policyEnforcerConfig.enforcementMode - return if (EnforcementMode.DISABLED == enforcementMode) { - createAuthorizedContext() - } else policyEnforcer.enforce(request, response) - } - - private fun createAuthorizedContext(): AuthorizationContext { - return object : ClientAuthorizationContext(policyEnforcer.authzClient) { - override fun hasPermission(resourceName: String, scopeName: String): Boolean { - return true - } - - override fun hasResourcePermission(resourceName: String): Boolean { - return true - } - - override fun hasScopePermission(scopeName: String): Boolean { - return true - } - - override fun getPermissions(): List { - return emptyList() - } - - override fun isGranted(): Boolean { - return true - } - } - } - - companion object { - private val log = KotlinLogging.logger {} - } -} \ No newline at end of file diff --git a/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt b/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt deleted file mode 100644 index bd62bdf..0000000 --- a/module-support/src/main/kotlin/de/twomartens/support/security/SpringPolicyEnforcerFilter.kt +++ /dev/null @@ -1,71 +0,0 @@ -package de.twomartens.support.security - -import jakarta.servlet.* -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import mu.KotlinLogging -import org.keycloak.AuthorizationContext -import org.keycloak.adapters.authorization.PolicyEnforcer -import org.keycloak.adapters.authorization.integration.elytron.ServletHttpRequest -import org.keycloak.adapters.authorization.integration.elytron.ServletHttpResponse -import org.keycloak.adapters.authorization.spi.ConfigurationResolver -import org.keycloak.adapters.authorization.spi.HttpRequest -import org.keycloak.representations.adapters.config.PolicyEnforcerConfig -import java.io.IOException - - -class SpringPolicyEnforcerFilter(private val configResolver: ConfigurationResolver) : Filter { - @Throws(IOException::class, ServletException::class) - override fun doFilter(servletRequest: ServletRequest, servletResponse: ServletResponse?, filterChain: FilterChain) { - val request = servletRequest as HttpServletRequest - val response = servletResponse as HttpServletResponse? - val httpRequest = ServletHttpRequest(request) { extractBearerToken(request) } - val policyEnforcer = createPolicyEnforcer(httpRequest) - val authzContext = policyEnforcer.enforce(httpRequest, ServletHttpResponse(response)) - request.setAttribute(AuthorizationContext::class.java.name, authzContext) - if (authzContext.isGranted) { - log.debug("Request authorized, continuing the filter chain") - filterChain.doFilter(servletRequest, servletResponse) - } else { - log.debug("Unauthorized request to path [{}], aborting the filter chain", request.requestURI) - } - } - - private fun extractBearerToken(request: HttpServletRequest): String? { - val authorizationHeaderValues = request.getHeaders("Authorization") - while (authorizationHeaderValues.hasMoreElements()) { - val value = authorizationHeaderValues.nextElement() - val parts = value.trim() - .split("\\s+".toRegex()) - .dropLastWhile { it.isEmpty() } - .toTypedArray() - if (parts.size != 2) { - continue - } - val bearer = parts[0] - if (bearer.equals("Bearer", ignoreCase = true)) { - return parts[1] - } - } - return null - } - - private fun createPolicyEnforcer(request: HttpRequest): SpringPolicyEnforcer { - return createPolicyEnforcer(configResolver.resolve(request)) - } - - private fun createPolicyEnforcer(enforcerConfig: PolicyEnforcerConfig): SpringPolicyEnforcer { - val authServerUrl = enforcerConfig.authServerUrl - return SpringPolicyEnforcer(PolicyEnforcer.builder() - .authServerUrl(authServerUrl) - .realm(enforcerConfig.realm) - .clientId(enforcerConfig.resource) - .credentials(enforcerConfig.credentials) - .bearerOnly(false) - .enforcerConfig(enforcerConfig).build(), enforcerConfig) - } - - companion object { - private val log = KotlinLogging.logger {} - } -} \ No newline at end of file