From 9ad020e36d7dba6e9e2fdd2e5b5276e728de4bd3 Mon Sep 17 00:00:00 2001 From: Fiete Ostkamp Date: Tue, 14 May 2024 13:38:17 +0200 Subject: Make rbac excluded endpoints configurable - introduce bff.rbac.endpoints-excluded config - add some performance improvements for role checking - resolve compilation warning related to missing swagger dependency Issue-ID: PORTALNG-100 Change-Id: I38ac942f0731a3297a797a09402f20aa6efc3b58 Signed-off-by: Fiete Ostkamp --- .../org/onap/portalng/bff/config/BeansConfig.java | 5 ---- .../org/onap/portalng/bff/config/BffConfig.java | 8 ++--- .../bff/config/IdTokenExchangeFilterFunction.java | 35 ++++++++++++---------- .../onap/portalng/bff/config/SecurityConfig.java | 10 +++++-- 4 files changed, 30 insertions(+), 28 deletions(-) (limited to 'lib/src') diff --git a/lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java index a23ac0c..f64da12 100644 --- a/lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java +++ b/lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java @@ -74,11 +74,6 @@ public class BeansConfig { return oauth2Filter; } - @Bean(name = ID_TOKEN_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction idTokenExchangeFilterFunction() { - return new IdTokenExchangeFilterFunction(); - } - @Bean(name = ERROR_HANDLING_EXCHANGE_FILTER_FUNCTION) ExchangeFilterFunction errorHandlingExchangeFilterFunction() { return ExchangeFilterFunction.ofResponseProcessor( diff --git a/lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java index 5bc618c..3fada84 100644 --- a/lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java +++ b/lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java @@ -24,8 +24,8 @@ package org.onap.portalng.bff.config; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import java.util.List; import java.util.Map; +import java.util.Set; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.zalando.problem.Problem; @@ -37,8 +37,8 @@ import reactor.core.publisher.Mono; * urls. */ @Valid -@ConfigurationProperties("bff") @Data +@ConfigurationProperties("bff") public class BffConfig { @NotBlank private final String realm; @@ -46,9 +46,9 @@ public class BffConfig { @NotBlank private final String historyUrl; @NotBlank private final String keycloakUrl; - @NotNull private final Map> accessControl; + @NotNull private final Map> accessControl; - public Mono> getRoles(String method) { + public Mono> getRoles(String method) { return Mono.just(accessControl) .map(control -> control.get(method)) .onErrorResume( diff --git a/lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java b/lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java index d747f3a..26db78d 100644 --- a/lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java +++ b/lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java @@ -23,9 +23,10 @@ package org.onap.portalng.bff.config; import com.nimbusds.jwt.JWTParser; import java.text.ParseException; -import java.util.Collections; import java.util.List; -import java.util.Optional; +import java.util.Set; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; @@ -37,27 +38,32 @@ import org.zalando.problem.Status; import reactor.core.Exceptions; import reactor.core.publisher.Mono; +@Component public class IdTokenExchangeFilterFunction implements ExchangeFilterFunction { public static final String X_AUTH_IDENTITY_HEADER = "X-Auth-Identity"; public static final String CLAIM_NAME_ROLES = "roles"; - private static final List EXCLUDED_PATHS_PATTERNS = - List.of( - "/actuator/**", "**/actuator/**", "*/actuator/**", "/**/actuator/**", "/*/actuator/**"); + private final List rbacExcludedPatterns; private static final Mono serverWebExchangeFromContext = Mono.deferContextual(Mono::just) .filter(context -> context.hasKey(ServerWebExchange.class)) .map(context -> context.get(ServerWebExchange.class)); + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + public IdTokenExchangeFilterFunction( + @Value("${bff.rbac.endpoints-excluded}") List rbacExcludedPatterns) { + this.rbacExcludedPatterns = rbacExcludedPatterns; + } + @Override public Mono filter(ClientRequest request, ExchangeFunction next) { boolean shouldNotFilter = - EXCLUDED_PATHS_PATTERNS.stream() + rbacExcludedPatterns.stream() .anyMatch( - excludedPath -> - new AntPathMatcher().match(excludedPath, request.url().getRawPath())); + excludedPath -> antPathMatcher.match(excludedPath, request.url().getRawPath())); if (shouldNotFilter) { return next.exchange(request).switchIfEmpty(Mono.defer(() -> next.exchange(request))); } @@ -86,7 +92,7 @@ public class IdTokenExchangeFilterFunction implements ExchangeFilterFunction { } public static Mono validateAccess( - ServerWebExchange exchange, List rolesListForMethod) { + ServerWebExchange exchange, Set rolesListForMethod) { return extractRoles(exchange) .map(roles -> roles.stream().anyMatch(rolesListForMethod::contains)) @@ -110,16 +116,13 @@ public class IdTokenExchangeFilterFunction implements ExchangeFilterFunction { .map( jwt -> { try { - return Optional.of(jwt.getJWTClaimsSet()); + return jwt.getJWTClaimsSet().getClaim(CLAIM_NAME_ROLES); } catch (ParseException e) { throw Exceptions.propagate(e); } }) - .map( - optionalClaimsSet -> - optionalClaimsSet - .map(claimsSet -> claimsSet.getClaim(CLAIM_NAME_ROLES)) - .map(obj -> (List) obj)) - .map(roles -> roles.orElse(Collections.emptyList())); + .filter(List.class::isInstance) + .map(roles -> (List) roles) + .switchIfEmpty(Mono.just(List.of())); } } diff --git a/lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java index 2a0f701..94c87bd 100644 --- a/lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java +++ b/lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java @@ -21,9 +21,9 @@ package org.onap.portalng.bff.config; +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.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager; @@ -34,9 +34,13 @@ import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2Autho import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.web.server.SecurityWebFilterChain; -@EnableWebFluxSecurity @Configuration +@EnableWebFluxSecurity public class SecurityConfig { + + @Value("${bff.endpoints.unauthenticated}") + private String[] unauthenticatedEndpoints; + @Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { return http.httpBasic() @@ -48,7 +52,7 @@ public class SecurityConfig { .cors() .and() .authorizeExchange() - .pathMatchers(HttpMethod.GET, "/api-docs.html", "/api.yaml", "/webjars/**", "/actuator/**") + .pathMatchers(unauthenticatedEndpoints) .permitAll() .anyExchange() .authenticated() -- cgit 1.2.3-korg