aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorFiete Ostkamp <fiete.ostkamp@telekom.de>2024-12-17 09:09:23 +0000
committerGerrit Code Review <gerrit@onap.org>2024-12-17 09:09:23 +0000
commit11fa7721b17cd453583a3c0f91107794e837f7d6 (patch)
tree424559f732cb34dcb06981c55be8ffaac6c0dda1 /lib
parentd4f1e5129ff72bc0af6f29e9b9c3eb0c362f4d6e (diff)
parentc5fab8a832454dd8c641dd2991cb91e3c8f688ca (diff)
Merge "add KeycloakPermissionFilter"HEADmaster
Diffstat (limited to 'lib')
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java1
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/config/KeycloakPermissionFilter.java122
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java6
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java13
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java14
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java13
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java6
-rw-r--r--lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java44
8 files changed, 151 insertions, 68 deletions
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 3fada84..786f4bc 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
@@ -45,6 +45,7 @@ public class BffConfig {
@NotBlank private final String preferencesUrl;
@NotBlank private final String historyUrl;
@NotBlank private final String keycloakUrl;
+ @NotBlank private final String keycloakClientId;
@NotNull private final Map<String, Set<String>> accessControl;
diff --git a/lib/src/main/java/org/onap/portalng/bff/config/KeycloakPermissionFilter.java b/lib/src/main/java/org/onap/portalng/bff/config/KeycloakPermissionFilter.java
new file mode 100644
index 0000000..f6b7d21
--- /dev/null
+++ b/lib/src/main/java/org/onap/portalng/bff/config/KeycloakPermissionFilter.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright (c) 2024. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ *
+ */
+
+package org.onap.portalng.bff.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+@Component
+@Slf4j
+public class KeycloakPermissionFilter implements WebFilter {
+
+ private final WebClient webClient;
+
+ private final ObjectMapper objectMapper;
+ private final BffConfig bffConfig;
+
+ @Value("${bff.rbac.endpoints-excluded}")
+ private String[] EXCLUDED_PATHS;
+
+ public KeycloakPermissionFilter(
+ WebClient.Builder webClientBuilder, ObjectMapper objectMapper, BffConfig bffConfig) {
+ this.webClient = webClientBuilder.build();
+ this.objectMapper = objectMapper;
+ this.bffConfig = bffConfig;
+ }
+
+ @Override
+ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+ String accessToken = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+ String uri = exchange.getRequest().getURI().getPath();
+ String method = exchange.getRequest().getMethod().toString();
+
+ for (String excludedPath : EXCLUDED_PATHS) {
+ if (uri.matches(excludedPath.replace("**", ".*"))) {
+ return chain.filter(exchange);
+ }
+ }
+
+ String body =
+ new StringBuilder()
+ .append("grant_type=urn:ietf:params:oauth:grant-type:uma-ticket")
+ .append("&audience=")
+ .append(bffConfig.getKeycloakClientId())
+ .append("&permission_resource_format=uri")
+ .append("&permission_resource_matching_uri=true")
+ .append("&permission=")
+ .append(uri)
+ .append("#")
+ .append(method)
+ .append("&response_mode=decision")
+ .toString();
+
+ return webClient
+ .post()
+ .uri(
+ bffConfig.getKeycloakUrl()
+ + "/realms/"
+ + bffConfig.getRealm()
+ + "/protocol/openid-connect/token")
+ .header(HttpHeaders.AUTHORIZATION, accessToken)
+ .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .bodyValue(body)
+ .retrieve()
+ .bodyToMono(String.class)
+ .flatMap(
+ response -> {
+ if (isPermissionGranted(response)) {
+ return chain.filter(exchange);
+ } else {
+ exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
+ return exchange.getResponse().setComplete();
+ }
+ })
+ .onErrorResume(
+ ex -> {
+ exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
+ return exchange.getResponse().setComplete();
+ });
+ }
+
+ private boolean isPermissionGranted(String response) {
+ try {
+ JsonNode jsonNode = objectMapper.readTree(response);
+ if (jsonNode.has("result") && jsonNode.get("result").asBoolean()) {
+ return true;
+ }
+ } catch (Exception e) {
+ log.error("Error parsing JSON response", e);
+ }
+ return false;
+ }
+}
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 4ae842a..6c5e8ea 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
@@ -23,10 +23,12 @@ package org.onap.portalng.bff.config;
import static org.springframework.security.config.Customizer.withDefaults;
+import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
@@ -38,8 +40,11 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
+@RequiredArgsConstructor
public class SecurityConfig {
+ private final KeycloakPermissionFilter keycloakPermissionFilter;
+
@Value("${bff.endpoints.unauthenticated}")
private String[] unauthenticatedEndpoints;
@@ -59,6 +64,7 @@ public class SecurityConfig {
.authenticated())
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt)
.oauth2Client(withDefaults())
+ .addFilterAfter(keycloakPermissionFilter, SecurityWebFiltersOrder.AUTHORIZATION)
.build();
}
diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java b/lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java
index afcb448..c311cb9 100644
--- a/lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java
+++ b/lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java
@@ -22,9 +22,6 @@
package org.onap.portalng.bff.controller;
import org.onap.portalng.bff.config.BffConfig;
-import org.onap.portalng.bff.config.IdTokenExchangeFilterFunction;
-import org.springframework.web.server.ServerWebExchange;
-import reactor.core.publisher.Mono;
public abstract class AbstractBffController {
@@ -33,14 +30,4 @@ public abstract class AbstractBffController {
protected AbstractBffController(BffConfig bffConfig) {
this.bffConfig = bffConfig;
}
-
- public Mono<Void> checkRoleAccess(String method, ServerWebExchange exchange) {
- return bffConfig
- .getRoles(method)
- .flatMap(
- roles ->
- roles.contains("*")
- ? Mono.empty()
- : IdTokenExchangeFilterFunction.validateAccess(exchange, roles));
- }
}
diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java b/lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java
index 8b8f615..9d7706c 100644
--- a/lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java
+++ b/lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java
@@ -34,9 +34,6 @@ import reactor.core.publisher.Mono;
@RestController
public class ActionsController extends AbstractBffController implements ActionsApi {
- public static final String CREATE = "ACTIONS_CREATE";
- public static final String GET = "ACTIONS_GET";
- public static final String LIST = "ACTIONS_LIST";
private final ActionService actionService;
@@ -51,8 +48,7 @@ public class ActionsController extends AbstractBffController implements ActionsA
String xRequestId,
Mono<CreateActionRequestApiDto> createActionRequestApiDto,
ServerWebExchange exchange) {
- return checkRoleAccess(CREATE, exchange)
- .then(createActionRequestApiDto)
+ return createActionRequestApiDto
.flatMap(action -> actionService.createAction(userId, xRequestId, action))
.map(ResponseEntity::ok);
}
@@ -65,8 +61,8 @@ public class ActionsController extends AbstractBffController implements ActionsA
Integer showLastHours,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(GET, exchange)
- .then(actionService.getActions(userId, xRequestId, page, pageSize, showLastHours))
+ return actionService
+ .getActions(userId, xRequestId, page, pageSize, showLastHours)
.map(ResponseEntity::ok);
}
@@ -77,8 +73,8 @@ public class ActionsController extends AbstractBffController implements ActionsA
Integer showLastHours,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(LIST, exchange)
- .then(actionService.listActions(xRequestId, page, pageSize, showLastHours))
+ return actionService
+ .listActions(xRequestId, page, pageSize, showLastHours)
.map(ResponseEntity::ok);
}
}
diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java b/lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java
index af665db..b8b428f 100644
--- a/lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java
+++ b/lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java
@@ -34,9 +34,6 @@ import reactor.core.publisher.Mono;
@RestController
public class PreferencesController extends AbstractBffController implements PreferencesApi {
- public static final String CREATE = "PREFERENCES_CREATE";
- public static final String GET = "PREFERENCES_GET";
- public static final String UPDATE = "PREFERENCES_UPDATE";
private final PreferencesService preferencesService;
@@ -48,9 +45,7 @@ public class PreferencesController extends AbstractBffController implements Pref
@Override
public Mono<ResponseEntity<PreferencesResponseApiDto>> getPreferences(
String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(GET, exchange)
- .then(preferencesService.getPreferences(xRequestId))
- .map(ResponseEntity::ok);
+ return preferencesService.getPreferences(xRequestId).map(ResponseEntity::ok);
}
@Override
@@ -58,8 +53,7 @@ public class PreferencesController extends AbstractBffController implements Pref
@Valid Mono<CreatePreferencesRequestApiDto> preferencesApiDto,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(CREATE, exchange)
- .then(preferencesApiDto)
+ return preferencesApiDto
.flatMap(request -> preferencesService.createPreferences(xRequestId, request))
.map(ResponseEntity::ok);
}
@@ -69,8 +63,7 @@ public class PreferencesController extends AbstractBffController implements Pref
@Valid Mono<CreatePreferencesRequestApiDto> preferencesApiDto,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(UPDATE, exchange)
- .then(preferencesApiDto)
+ return preferencesApiDto
.flatMap(request -> preferencesService.updatePreferences(xRequestId, request))
.map(ResponseEntity::ok);
}
diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java b/lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java
index 69b4093..1d948a2 100644
--- a/lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java
+++ b/lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java
@@ -33,8 +33,6 @@ import reactor.core.publisher.Mono;
@RestController
public class RolesController extends AbstractBffController implements RolesApi {
- public static final String LIST = "ROLE_LIST";
-
private final KeycloakService keycloakService;
public RolesController(BffConfig bffConfig, KeycloakService keycloakService) {
@@ -45,8 +43,8 @@ public class RolesController extends AbstractBffController implements RolesApi {
@Override
public Mono<ResponseEntity<RoleListResponseApiDto>> listRoles(
String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(LIST, exchange)
- .thenMany(keycloakService.listRoles(xRequestId))
+ return keycloakService
+ .listRoles(xRequestId)
.collectList()
.map(roles -> new RoleListResponseApiDto().items(roles).totalCount(roles.size()))
.map(ResponseEntity::ok);
diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java b/lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java
index a8561af..3efb28f 100644
--- a/lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java
+++ b/lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java
@@ -40,16 +40,6 @@ import reactor.core.publisher.Mono;
@RestController
public class UsersController extends AbstractBffController implements UsersApi {
- public static final String CREATE = "USER_CREATE";
- public static final String GET = "USER_GET";
- public static final String UPDATE = "USER_UPDATE";
- public static final String DELETE = "USER_DELETE";
- public static final String LIST = "USER_LIST";
- public static final String UPDATE_PASSWORD = "USER_UPDATE_PASSWORD";
- public static final String UPDATE_ROLES = "USER_UPDATE_ROLES";
- public static final String LIST_ROLES = "USER_LIST_ROLES";
- public static final String LIST_AVAILABLE_ROLES = "USER_LIST_AVAILABLE_ROLES";
-
private final KeycloakService keycloakService;
public UsersController(BffConfig bffConfig, KeycloakService keycloakService) {
@@ -60,17 +50,15 @@ public class UsersController extends AbstractBffController implements UsersApi {
@Override
public Mono<ResponseEntity<UserResponseApiDto>> createUser(
Mono<CreateUserRequestApiDto> requestMono, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(CREATE, exchange)
- .then(requestMono.flatMap(request -> keycloakService.createUser(request, xRequestId)))
+ return requestMono
+ .flatMap(request -> keycloakService.createUser(request, xRequestId))
.map(ResponseEntity::ok);
}
@Override
public Mono<ResponseEntity<UserResponseApiDto>> getUser(
String userId, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(GET, exchange)
- .then(keycloakService.getUser(userId, xRequestId))
- .map(ResponseEntity::ok);
+ return keycloakService.getUser(userId, xRequestId).map(ResponseEntity::ok);
}
@Override
@@ -79,8 +67,7 @@ public class UsersController extends AbstractBffController implements UsersApi {
Mono<UpdateUserRequestApiDto> requestMono,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(UPDATE, exchange)
- .then(requestMono)
+ return requestMono
.flatMap(request -> keycloakService.updateUser(userId, request, xRequestId))
.map(ResponseEntity::ok);
}
@@ -88,8 +75,8 @@ public class UsersController extends AbstractBffController implements UsersApi {
@Override
public Mono<ResponseEntity<Void>> deleteUser(
String userId, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(DELETE, exchange)
- .then(keycloakService.deleteUser(userId, xRequestId))
+ return keycloakService
+ .deleteUser(userId, xRequestId)
.thenReturn(ResponseEntity.noContent().build());
}
@@ -97,9 +84,7 @@ public class UsersController extends AbstractBffController implements UsersApi {
public Mono<ResponseEntity<UserListResponseApiDto>> listUsers(
Integer page, Integer pageSize, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(LIST, exchange)
- .then(keycloakService.listUsers(page, pageSize, xRequestId))
- .map(ResponseEntity::ok);
+ return keycloakService.listUsers(page, pageSize, xRequestId).map(ResponseEntity::ok);
}
@Override
@@ -108,8 +93,7 @@ public class UsersController extends AbstractBffController implements UsersApi {
Mono<UpdateUserPasswordRequestApiDto> requestMono,
String xRequestId,
ServerWebExchange exchange) {
- return checkRoleAccess(UPDATE_PASSWORD, exchange)
- .then(requestMono)
+ return requestMono
.flatMap(request -> keycloakService.updateUserPassword(userId, request))
.thenReturn(ResponseEntity.noContent().build());
}
@@ -117,24 +101,20 @@ public class UsersController extends AbstractBffController implements UsersApi {
@Override
public Mono<ResponseEntity<RoleListResponseApiDto>> listAvailableRoles(
String userId, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(LIST_AVAILABLE_ROLES, exchange)
- .then(keycloakService.getAvailableRoles(userId, xRequestId))
- .map(ResponseEntity::ok);
+ return keycloakService.getAvailableRoles(userId, xRequestId).map(ResponseEntity::ok);
}
@Override
public Mono<ResponseEntity<RoleListResponseApiDto>> listAssignedRoles(
String userId, String xRequestId, ServerWebExchange exchange) {
- return checkRoleAccess(LIST_ROLES, exchange)
- .then(keycloakService.getAssignedRoles(userId, xRequestId))
- .map(ResponseEntity::ok);
+ return keycloakService.getAssignedRoles(userId, xRequestId).map(ResponseEntity::ok);
}
@Override
public Mono<ResponseEntity<RoleListResponseApiDto>> updateAssignedRoles(
String userId, String xRequestId, Flux<RoleApiDto> rolesFlux, ServerWebExchange exchange) {
- return checkRoleAccess(UPDATE_ROLES, exchange)
- .then(rolesFlux.collectList())
+ return rolesFlux
+ .collectList()
.flatMap(roles -> keycloakService.updateAssignedRoles(userId, roles, xRequestId))
.map(ResponseEntity::ok);
}