From 473792061d40a0027b57d65a97f3da06e4b8c7bd Mon Sep 17 00:00:00 2001 From: Fiete Ostkamp Date: Wed, 16 Aug 2023 08:01:39 +0000 Subject: Consistently use the bff name in the bff code base - rename package name from org.onap.portal.bff to org.onap.portalng.bff - replace all other occurences of portal-bff with bff Other: - remove Sonarqube plugin Issue-ID: PORTALNG-40 Signed-off-by: Fiete Ostkamp Change-Id: I50548630212e46a0ab84c88ff5fef743dafe9d38 --- .../org/onap/portal/bff/config/BeansConfig.java | 190 ---------- .../portal/bff/config/ConversionServiceConfig.java | 59 ---- .../bff/config/IdTokenExchangeFilterFunction.java | 125 ------- .../onap/portal/bff/config/LoggerInterceptor.java | 52 --- .../onap/portal/bff/config/MapperSpringConfig.java | 28 -- .../onap/portal/bff/config/PortalBffConfig.java | 63 ---- .../org/onap/portal/bff/config/SecurityConfig.java | 77 ----- .../bff/config/clients/AbstractClientConfig.java | 87 ----- .../portal/bff/config/clients/KeycloakConfig.java | 100 ------ .../bff/config/clients/PortalHistoryConfig.java | 97 ------ .../bff/config/clients/PortalPrefsConfig.java | 96 ----- .../bff/controller/AbstractBffController.java | 46 --- .../portal/bff/controller/ActionsController.java | 84 ----- .../portal/bff/controller/BffControllerAdvice.java | 28 -- .../bff/controller/PreferencesController.java | 77 ----- .../portal/bff/controller/RolesController.java | 56 --- .../portal/bff/controller/UsersController.java | 143 -------- .../exceptions/DownstreamApiProblemException.java | 65 ---- .../org/onap/portal/bff/mappers/ActionsMapper.java | 37 -- .../onap/portal/bff/mappers/CredentialMapper.java | 33 -- .../onap/portal/bff/mappers/PreferencesMapper.java | 37 -- .../org/onap/portal/bff/mappers/RolesMapper.java | 36 -- .../org/onap/portal/bff/mappers/UsersMapper.java | 48 --- .../onap/portal/bff/services/ActionService.java | 125 ------- .../onap/portal/bff/services/KeycloakService.java | 385 --------------------- .../portal/bff/services/PreferencesService.java | 91 ----- .../org/onap/portal/bff/utils/ErrorHandler.java | 65 ---- .../java/org/onap/portal/bff/utils/Logger.java | 61 ---- .../org/onap/portalng/bff/config/BeansConfig.java | 190 ++++++++++ .../org/onap/portalng/bff/config/BffConfig.java | 63 ++++ .../bff/config/ConversionServiceConfig.java | 59 ++++ .../bff/config/IdTokenExchangeFilterFunction.java | 125 +++++++ .../portalng/bff/config/LoggerInterceptor.java | 52 +++ .../portalng/bff/config/MapperSpringConfig.java | 28 ++ .../onap/portalng/bff/config/SecurityConfig.java | 77 +++++ .../bff/config/clients/AbstractClientConfig.java | 87 +++++ .../bff/config/clients/KeycloakConfig.java | 100 ++++++ .../bff/config/clients/PortalHistoryConfig.java | 97 ++++++ .../bff/config/clients/PortalPrefsConfig.java | 96 +++++ .../bff/controller/AbstractBffController.java | 46 +++ .../portalng/bff/controller/ActionsController.java | 84 +++++ .../bff/controller/BffControllerAdvice.java | 28 ++ .../bff/controller/PreferencesController.java | 77 +++++ .../portalng/bff/controller/RolesController.java | 56 +++ .../portalng/bff/controller/UsersController.java | 143 ++++++++ .../exceptions/DownstreamApiProblemException.java | 65 ++++ .../onap/portalng/bff/mappers/ActionsMapper.java | 37 ++ .../portalng/bff/mappers/CredentialMapper.java | 33 ++ .../portalng/bff/mappers/PreferencesMapper.java | 37 ++ .../org/onap/portalng/bff/mappers/RolesMapper.java | 36 ++ .../org/onap/portalng/bff/mappers/UsersMapper.java | 48 +++ .../onap/portalng/bff/services/ActionService.java | 125 +++++++ .../portalng/bff/services/KeycloakService.java | 385 +++++++++++++++++++++ .../portalng/bff/services/PreferencesService.java | 91 +++++ .../org/onap/portalng/bff/utils/ErrorHandler.java | 65 ++++ .../java/org/onap/portalng/bff/utils/Logger.java | 60 ++++ 56 files changed, 2390 insertions(+), 2391 deletions(-) delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/BeansConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/ConversionServiceConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/IdTokenExchangeFilterFunction.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/LoggerInterceptor.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/MapperSpringConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/PortalBffConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/SecurityConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/clients/AbstractClientConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/clients/KeycloakConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/clients/PortalHistoryConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/config/clients/PortalPrefsConfig.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/AbstractBffController.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/ActionsController.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/BffControllerAdvice.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/PreferencesController.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/RolesController.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/controller/UsersController.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/exceptions/DownstreamApiProblemException.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/mappers/ActionsMapper.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/mappers/CredentialMapper.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/mappers/PreferencesMapper.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/mappers/RolesMapper.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/mappers/UsersMapper.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/services/ActionService.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/services/KeycloakService.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/services/PreferencesService.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/utils/ErrorHandler.java delete mode 100644 lib/src/main/java/org/onap/portal/bff/utils/Logger.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/ConversionServiceConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/LoggerInterceptor.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/MapperSpringConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/clients/AbstractClientConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/clients/KeycloakConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/clients/PortalHistoryConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/config/clients/PortalPrefsConfig.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/BffControllerAdvice.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/exceptions/DownstreamApiProblemException.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/mappers/ActionsMapper.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/mappers/CredentialMapper.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/mappers/PreferencesMapper.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/mappers/RolesMapper.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/mappers/UsersMapper.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/services/ActionService.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/services/KeycloakService.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/services/PreferencesService.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/utils/ErrorHandler.java create mode 100644 lib/src/main/java/org/onap/portalng/bff/utils/Logger.java (limited to 'lib/src') diff --git a/lib/src/main/java/org/onap/portal/bff/config/BeansConfig.java b/lib/src/main/java/org/onap/portal/bff/config/BeansConfig.java deleted file mode 100644 index ca4333b..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/BeansConfig.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.time.Clock; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.utils.Logger; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Scope; -import org.springframework.http.codec.ClientCodecConfigurer; -import org.springframework.http.codec.json.Jackson2JsonDecoder; -import org.springframework.http.codec.json.Jackson2JsonEncoder; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; -import org.zalando.problem.jackson.ProblemModule; -import reactor.core.publisher.Mono; - -@Slf4j -@Configuration -public class BeansConfig { - - public static final String OAUTH2_EXCHANGE_FILTER_FUNCTION = "oauth2ExchangeFilterFunction"; - private static final String ID_TOKEN_EXCHANGE_FILTER_FUNCTION = "idTokenExchangeFilterFunction"; - private static final String ERROR_HANDLING_EXCHANGE_FILTER_FUNCTION = - "errorHandlingExchangeFilterFunction"; - private static final String LOG_REQUEST_EXCHANGE_FILTER_FUNCTION = - "logRequestExchangeFilterFunction"; - private static final String LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION = - "logResponseExchangeFilterFunction"; - private static final String CLIENT_REGISTRATION_ID = "keycloak"; - public static final String X_REQUEST_ID = "X-Request-Id"; - - @Bean(name = OAUTH2_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction oauth2ExchangeFilterFunction( - ReactiveOAuth2AuthorizedClientManager authorizedClientManager) { - final ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter = - new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); - oauth2Filter.setDefaultClientRegistrationId(CLIENT_REGISTRATION_ID); - - 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( - clientResponse -> { - if (clientResponse.statusCode().isError()) { - return clientResponse - .bodyToMono(String.class) - .doOnNext(s -> log.error("Received error response from downstream: {}", s)) - .flatMap( - downstreamExceptionBody -> { - try { - return Mono.error( - new ObjectMapper() - .readValue( - downstreamExceptionBody, DownstreamApiProblemException.class)); - } catch (JsonProcessingException e) { - return Mono.error(DownstreamApiProblemException.builder().build()); - } - }); - } - return Mono.just(clientResponse); - }); - } - - // - // Don't use this. Log will is written in the LoggerInterceptor - // - @Bean(name = LOG_REQUEST_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction logRequestExchangeFilterFunction() { - return ExchangeFilterFunction.ofRequestProcessor( - clientRequest -> { - List xRequestIdList = clientRequest.headers().get(X_REQUEST_ID); - if (xRequestIdList != null && !xRequestIdList.isEmpty()) { - String xRequestId = xRequestIdList.get(0); - Logger.requestLog(xRequestId, clientRequest.method(), clientRequest.url()); - } - return Mono.just(clientRequest); - }); - } - - @Bean(name = LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction logResponseExchangeFilterFunction() { - return ExchangeFilterFunction.ofResponseProcessor( - clientResponse -> { - String xRequestId = "not set"; - List xRequestIdList = clientResponse.headers().header(X_REQUEST_ID); - if (xRequestIdList != null && !xRequestIdList.isEmpty()) - xRequestId = xRequestIdList.get(0); - Logger.responseLog(xRequestId, clientResponse.statusCode()); - return Mono.just(clientResponse); - }); - } - - @Bean - ExchangeStrategies exchangeStrategies(ObjectMapper objectMapper) { - return ExchangeStrategies.builder() - .codecs( - configurer -> { - final ClientCodecConfigurer.ClientDefaultCodecs defaultCodecs = - configurer.defaultCodecs(); - - defaultCodecs.maxInMemorySize(16 * 1024 * 1024); // 16MB - defaultCodecs.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper)); - defaultCodecs.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper)); - }) - .build(); - } - - // we need to use prototype scope to always create new instance of the bean - // because internally WebClient.Builder is mutable - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - WebClient.Builder webClientBuilder( - ExchangeStrategies exchangeStrategies, - @Qualifier(ID_TOKEN_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction idTokenExchangeFilterFunction, - @Qualifier(ERROR_HANDLING_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction errorHandlingExchangeFilterFunction, - @Qualifier(LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction logResponseExchangeFilterFunction) { - return WebClient.builder() - .exchangeStrategies(exchangeStrategies) - .filter(idTokenExchangeFilterFunction) - .filter(errorHandlingExchangeFilterFunction) - .filter(logResponseExchangeFilterFunction); - } - - @Bean - Clock clock() { - return Clock.systemUTC(); - } - - @Bean - public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { - return builder - .modules(new ProblemModule(), new JavaTimeModule()) - .build() - .setSerializationInclusion(JsonInclude.Include.NON_NULL); - } - - @Bean - public XmlMapper xmlMapper() { - return XmlMapper.builder() - .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) - .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) - .build(); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/ConversionServiceConfig.java b/lib/src/main/java/org/onap/portal/bff/config/ConversionServiceConfig.java deleted file mode 100644 index 18830ef..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/ConversionServiceConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import java.util.List; -import org.onap.portal.bff.mappers.ActionsMapper; -import org.onap.portal.bff.mappers.PreferencesMapper; -import org.onap.portal.bff.mappers.RolesMapper; -import org.onap.portal.bff.mappers.UsersMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.core.convert.support.DefaultConversionService; - -@SuppressWarnings("rawtypes") -@Configuration -public class ConversionServiceConfig { - - @Bean - public ConfigurableConversionService conversionService( - ActionsMapper actionsMapper, - PreferencesMapper preferencesMapper, - RolesMapper rolesMapper, - UsersMapper usersMapper) { - final List converters = - List.of( - actionsMapper, - preferencesMapper, - preferencesMapper, - actionsMapper, - rolesMapper, - usersMapper); - - final ConfigurableConversionService conversionService = new DefaultConversionService(); - converters.forEach(conversionService::addConverter); - - return conversionService; - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/IdTokenExchangeFilterFunction.java b/lib/src/main/java/org/onap/portal/bff/config/IdTokenExchangeFilterFunction.java deleted file mode 100644 index 9cd8ac0..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/IdTokenExchangeFilterFunction.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import com.nimbusds.jwt.JWTParser; -import java.text.ParseException; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import org.springframework.util.AntPathMatcher; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFunction; -import org.springframework.web.server.ServerWebExchange; -import org.zalando.problem.Problem; -import org.zalando.problem.Status; -import reactor.core.Exceptions; -import reactor.core.publisher.Mono; - -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 static final Mono serverWebExchangeFromContext = - Mono.deferContextual(Mono::just) - .filter(context -> context.hasKey(ServerWebExchange.class)) - .map(context -> context.get(ServerWebExchange.class)); - - @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { - boolean shouldNotFilter = - EXCLUDED_PATHS_PATTERNS.stream() - .anyMatch( - excludedPath -> - new AntPathMatcher().match(excludedPath, request.url().getRawPath())); - if (shouldNotFilter) { - return next.exchange(request).switchIfEmpty(Mono.defer(() -> next.exchange(request))); - } - return extractServerWebExchange(request) - .flatMap(IdTokenExchangeFilterFunction::extractIdentityHeader) - .map(idToken -> ClientRequest.from(request).header(X_AUTH_IDENTITY_HEADER, idToken).build()) - .flatMap(requestWithIdToken -> next.exchange(requestWithIdToken)) - .switchIfEmpty(Mono.defer(() -> next.exchange(request))); - } - - private Mono extractServerWebExchange(ClientRequest request) { - return Mono.justOrEmpty(request.attribute(ServerWebExchange.class.getName())) - .cast(ServerWebExchange.class) - .switchIfEmpty(serverWebExchangeFromContext); - } - - private static Mono extractIdentityHeader(ServerWebExchange exchange) { - return Mono.just(exchange) - .map(exch -> exch.getRequest().getHeaders().getOrEmpty(X_AUTH_IDENTITY_HEADER).get(0)) - .onErrorResume(ex -> Mono.error(Problem.valueOf(Status.FORBIDDEN, "ID token is missing"))); - } - - private static Mono extractIdToken(ServerWebExchange exchange) { - return extractIdentityHeader(exchange) - .map(identityHeader -> identityHeader.replace("Bearer ", "")); - } - - public static Mono validateAccess( - ServerWebExchange exchange, List rolesListForMethod) { - - return extractRoles(exchange) - .map(roles -> roles.stream().anyMatch(rolesListForMethod::contains)) - .flatMap( - match -> - Boolean.TRUE.equals(match) - ? Mono.empty() - : Mono.error(Problem.valueOf(Status.FORBIDDEN))); - } - - private static Mono> extractRoles(ServerWebExchange exchange) { - return extractIdToken(exchange) - .map( - token -> { - try { - return JWTParser.parse(token); - } catch (ParseException e) { - throw Exceptions.propagate(e); - } - }) - .map( - jwt -> { - try { - return Optional.of(jwt.getJWTClaimsSet()); - } 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())); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/LoggerInterceptor.java b/lib/src/main/java/org/onap/portal/bff/config/LoggerInterceptor.java deleted file mode 100644 index 4fa2d82..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/LoggerInterceptor.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import java.util.List; -import org.onap.portal.bff.utils.Logger; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.reactive.ServerWebExchangeContextFilter; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilterChain; -import reactor.core.publisher.Mono; - -@Component -public class LoggerInterceptor extends ServerWebExchangeContextFilter { - public static final String EXCHANGE_CONTEXT_ATTRIBUTE = - ServerWebExchangeContextFilter.class.getName() + ".EXCHANGE_CONTEXT"; - - public static final String X_REQUEST_ID = "X-Request-Id"; - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - List xRequestIdList = exchange.getRequest().getHeaders().get(X_REQUEST_ID); - if (xRequestIdList != null && !xRequestIdList.isEmpty()) { - String xRequestId = xRequestIdList.get(0); - Logger.requestLog( - xRequestId, exchange.getRequest().getMethod(), exchange.getRequest().getURI()); - exchange.getResponse().getHeaders().add(X_REQUEST_ID, xRequestId); - } - return chain - .filter(exchange) - .contextWrite(cxt -> cxt.put(EXCHANGE_CONTEXT_ATTRIBUTE, exchange)); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/MapperSpringConfig.java b/lib/src/main/java/org/onap/portal/bff/config/MapperSpringConfig.java deleted file mode 100644 index c7e3711..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/MapperSpringConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import org.mapstruct.MapperConfig; -import org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator; - -@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapterGenerator.class) -public interface MapperSpringConfig {} diff --git a/lib/src/main/java/org/onap/portal/bff/config/PortalBffConfig.java b/lib/src/main/java/org/onap/portal/bff/config/PortalBffConfig.java deleted file mode 100644 index b80fe20..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/PortalBffConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -import java.util.List; -import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; -import org.zalando.problem.Problem; -import org.zalando.problem.Status; -import reactor.core.publisher.Mono; - -/** - * Class that contains configuration of the downstream apis. This could be username and password or - * urls. - */ -@Valid -@ConstructorBinding -@ConfigurationProperties("portal-bff") -@Data -public class PortalBffConfig { - - @NotBlank private final String realm; - @NotBlank private final String portalServiceUrl; - @NotBlank private final String portalPrefsUrl; - @NotBlank private final String portalHistoryUrl; - @NotBlank private final String keycloakUrl; - - @NotNull private final Map> accessControl; - - public Mono> getRoles(String method) { - return Mono.just(accessControl) - .map(control -> control.get(method)) - .onErrorResume( - e -> - Mono.error( - Problem.valueOf( - Status.FORBIDDEN, "The user does not have the necessary access rights"))); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/SecurityConfig.java b/lib/src/main/java/org/onap/portal/bff/config/SecurityConfig.java deleted file mode 100644 index 0d33980..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/SecurityConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config; - -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; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; -import org.springframework.security.web.server.SecurityWebFilterChain; - -@EnableWebFluxSecurity -@Configuration -public class SecurityConfig { - @Bean - public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { - return http.httpBasic() - .disable() - .formLogin() - .disable() - .csrf() - .disable() - .cors() - .and() - .authorizeExchange() - .pathMatchers(HttpMethod.GET, "/api-docs.html", "/api.yaml", "/webjars/**", "/actuator/**") - .permitAll() - .anyExchange() - .authenticated() - .and() - .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt) - .oauth2Client() - .and() - .build(); - } - - @Bean - ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager( - ReactiveClientRegistrationRepository clientRegistrationRepository, - ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { - - final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = - ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build(); - - final DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = - new DefaultReactiveOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - return authorizedClientManager; - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/clients/AbstractClientConfig.java b/lib/src/main/java/org/onap/portal/bff/config/clients/AbstractClientConfig.java deleted file mode 100644 index 85ee8ba..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/clients/AbstractClientConfig.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config.clients; - -import java.time.Duration; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; -import reactor.netty.http.client.HttpClient; -import reactor.netty.resources.ConnectionProvider; - -@Slf4j -@RequiredArgsConstructor -public abstract class AbstractClientConfig { - private final Class errorResponseTypeClass; - - protected ExchangeFilterFunction errorHandlingExchangeFilterFunction() { - return ExchangeFilterFunction.ofResponseProcessor( - clientResponse -> { - if (clientResponse.statusCode().isError()) { - return clientResponse - .bodyToMono(errorResponseTypeClass) - .doOnNext(s -> log.error("Received error response from downstream: {}", s)) - .flatMap( - problemResponse -> - Mono.error(mapException(problemResponse, clientResponse.statusCode()))); - } - return Mono.just(clientResponse); - }); - } - - protected abstract DownstreamApiProblemException mapException( - E errorResponse, HttpStatus httpStatus); - - protected ClientHttpConnector getClientHttpConnector() { - // ConnectionTimeouts introduced due to - // io.netty.channel.unix.Errors$NativeIoException: readAddress(..) failed: Connection reset by - // peer issue - // https://github.com/reactor/reactor-netty/issues/1774#issuecomment-908066283 - ConnectionProvider connectionProvider = - ConnectionProvider.builder("fixed") - .maxConnections(500) - .maxIdleTime(Duration.ofSeconds(20)) - .maxLifeTime(Duration.ofSeconds(60)) - .pendingAcquireTimeout(Duration.ofSeconds(60)) - .evictInBackground(Duration.ofSeconds(120)) - .build(); - return new ReactorClientHttpConnector(HttpClient.create(connectionProvider)); - } - - protected WebClient getWebClient( - WebClient.Builder webClientBuilder, List filters) { - if (filters != null) { - filters.forEach(webClientBuilder::filter); - } - return webClientBuilder - .filter(errorHandlingExchangeFilterFunction()) - .clientConnector(getClientHttpConnector()) - .build(); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/clients/KeycloakConfig.java b/lib/src/main/java/org/onap/portal/bff/config/clients/KeycloakConfig.java deleted file mode 100644 index 168c350..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/clients/KeycloakConfig.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config.clients; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import java.util.function.Function; -import org.onap.portal.bff.config.BeansConfig; -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.client_portal_keycloak.ApiClient; -import org.onap.portal.bff.openapi.client_portal_keycloak.api.KeycloakApi; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.ErrorResponseKeycloakDto; -import org.onap.portal.bff.openapi.server.model.ProblemApiDto; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -public class KeycloakConfig extends AbstractClientConfig { - private final ObjectMapper objectMapper; - private final PortalBffConfig bffConfig; - private final ExchangeFilterFunction oauth2ExchangeFilterFunction; - - public KeycloakConfig( - @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction oauth2ExchangeFilterFunction, - ObjectMapper objectMapper, - PortalBffConfig bffConfig) { - super(ErrorResponseKeycloakDto.class); - this.objectMapper = objectMapper; - this.bffConfig = bffConfig; - this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; - } - - @Bean - public KeycloakApi keycloakApi(WebClient.Builder webClientBuilder) { - return constructApiClient(webClientBuilder, KeycloakApi::new); - } - - private T constructApiClient( - WebClient.Builder webClientBuilder, Function apiConstructor) { - final ApiClient apiClient = - new ApiClient( - getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), - objectMapper, - objectMapper.getDateFormat()); - - // Extract service name and version from BasePath - String urlBasePathPrefix = - String.format("%s/auth/admin/realms/%s", bffConfig.getKeycloakUrl(), bffConfig.getRealm()); - - return apiConstructor.apply(apiClient.setBasePath(urlBasePathPrefix)); - } - - @Override - protected DownstreamApiProblemException mapException( - ErrorResponseKeycloakDto errorResponse, HttpStatus httpStatus) { - String errorDetail = - errorResponse.getErrorMessage() != null - ? errorResponse.getErrorMessage() - : errorResponse.getError(); - - return DownstreamApiProblemException.builder() - .title(httpStatus.toString()) - .detail(errorDetail) - .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()) - .downstreamMessageId("not set by downstream system") - .downstreamStatus(httpStatus.value()) - .build(); - } - - @Override - protected ClientHttpConnector getClientHttpConnector() { - return null; - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/clients/PortalHistoryConfig.java b/lib/src/main/java/org/onap/portal/bff/config/clients/PortalHistoryConfig.java deleted file mode 100644 index b71608f..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/clients/PortalHistoryConfig.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config.clients; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; -import java.util.function.Function; -import lombok.extern.slf4j.Slf4j; -import org.onap.portal.bff.config.BeansConfig; -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.client_portal_history.ApiClient; -import org.onap.portal.bff.openapi.client_portal_history.api.ActionsApi; -import org.onap.portal.bff.openapi.client_portal_history.model.ProblemPortalHistoryDto; -import org.onap.portal.bff.openapi.server.model.ProblemApiDto; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.WebClient; - -@Slf4j -@Configuration -public class PortalHistoryConfig extends AbstractClientConfig { - private final ObjectMapper objectMapper; - private final PortalBffConfig bffConfig; - private final ExchangeFilterFunction oauth2ExchangeFilterFunction; - - @Autowired - public PortalHistoryConfig( - @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction oauth2ExchangeFilterFunction, - ObjectMapper objectMapper, - PortalBffConfig bffConfig) { - super(ProblemPortalHistoryDto.class); - this.objectMapper = objectMapper; - this.bffConfig = bffConfig; - this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; - } - - @Bean - public ActionsApi portalHistoryActionApi(WebClient.Builder webClientBuilder) { - return constructApiClient(webClientBuilder, ActionsApi::new); - } - - private T constructApiClient( - WebClient.Builder webClientBuilder, Function apiConstructor) { - final ApiClient apiClient = - new ApiClient( - getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), - objectMapper, - objectMapper.getDateFormat()); - final String generatedBasePath = apiClient.getBasePath(); - String basePath = ""; - try { - basePath = bffConfig.getPortalHistoryUrl() + new URL(generatedBasePath).getPath(); - } catch (MalformedURLException e) { - log.error(e.getLocalizedMessage()); - } - return apiConstructor.apply(apiClient.setBasePath(basePath)); - } - - @Override - protected DownstreamApiProblemException mapException( - ProblemPortalHistoryDto errorResponse, HttpStatus httpStatus) { - return DownstreamApiProblemException.builder() - .title(httpStatus.toString()) - .detail(errorResponse.getDetail()) - .downstreamMessageId(errorResponse.getType()) - .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()) - .downstreamStatus(httpStatus.value()) - .build(); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/config/clients/PortalPrefsConfig.java b/lib/src/main/java/org/onap/portal/bff/config/clients/PortalPrefsConfig.java deleted file mode 100644 index 5e23348..0000000 --- a/lib/src/main/java/org/onap/portal/bff/config/clients/PortalPrefsConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.config.clients; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; -import java.util.function.Function; -import lombok.extern.slf4j.Slf4j; -import org.onap.portal.bff.config.BeansConfig; -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.client_portal_prefs.ApiClient; -import org.onap.portal.bff.openapi.client_portal_prefs.api.PreferencesApi; -import org.onap.portal.bff.openapi.client_portal_prefs.model.ProblemPortalPrefsDto; -import org.onap.portal.bff.openapi.server.model.ProblemApiDto; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.WebClient; - -@Slf4j -@Configuration -public class PortalPrefsConfig extends AbstractClientConfig { - private final ObjectMapper objectMapper; - private final PortalBffConfig bffConfig; - private final ExchangeFilterFunction oauth2ExchangeFilterFunction; - - public PortalPrefsConfig( - @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) - ExchangeFilterFunction oauth2ExchangeFilterFunction, - ObjectMapper objectMapper, - PortalBffConfig bffConfig) { - super(ProblemPortalPrefsDto.class); - this.objectMapper = objectMapper; - this.bffConfig = bffConfig; - this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; - } - - @Bean - public PreferencesApi portalPrefsApi(WebClient.Builder webClientBuilder) { - return constructApiClient(webClientBuilder, PreferencesApi::new); - } - - private T constructApiClient( - WebClient.Builder webClientBuilder, Function apiConstructor) { - final ApiClient apiClient = - new ApiClient( - getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), - objectMapper, - objectMapper.getDateFormat()); - - final String generatedBasePath = apiClient.getBasePath(); - String basePath = ""; - try { - basePath = bffConfig.getPortalPrefsUrl() + new URL(generatedBasePath).getPath(); - } catch (MalformedURLException e) { - log.error(e.getLocalizedMessage()); - } - return apiConstructor.apply(apiClient.setBasePath(basePath)); - } - - @Override - protected DownstreamApiProblemException mapException( - ProblemPortalPrefsDto errorResponse, HttpStatus httpStatus) { - return DownstreamApiProblemException.builder() - .title(httpStatus.toString()) - .detail(errorResponse.getDetail()) - .downstreamMessageId(errorResponse.getType()) - .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.PORTAL_PREFS.toString()) - .downstreamStatus(httpStatus.value()) - .build(); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/controller/AbstractBffController.java b/lib/src/main/java/org/onap/portal/bff/controller/AbstractBffController.java deleted file mode 100644 index bc92b68..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/AbstractBffController.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import org.onap.portal.bff.config.IdTokenExchangeFilterFunction; -import org.onap.portal.bff.config.PortalBffConfig; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -public abstract class AbstractBffController { - - protected PortalBffConfig bffConfig; - - protected AbstractBffController(PortalBffConfig bffConfig) { - this.bffConfig = bffConfig; - } - - public Mono 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/portal/bff/controller/ActionsController.java b/lib/src/main/java/org/onap/portal/bff/controller/ActionsController.java deleted file mode 100644 index ece6683..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/ActionsController.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.openapi.server.api.ActionsApi; -import org.onap.portal.bff.openapi.server.model.ActionsListResponseApiDto; -import org.onap.portal.bff.openapi.server.model.ActionsResponseApiDto; -import org.onap.portal.bff.openapi.server.model.CreateActionRequestApiDto; -import org.onap.portal.bff.services.ActionService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ServerWebExchange; -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; - - public ActionsController(PortalBffConfig bffConfig, ActionService actionService) { - super(bffConfig); - this.actionService = actionService; - } - - @Override - public Mono> createAction( - String userId, - String xRequestId, - Mono createActionRequestApiDto, - ServerWebExchange exchange) { - return checkRoleAccess(CREATE, exchange) - .then(createActionRequestApiDto) - .flatMap(action -> actionService.createAction(userId, xRequestId, action)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> getActions( - String userId, - Integer page, - Integer pageSize, - Integer showLastHours, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(GET, exchange) - .then(actionService.getActions(userId, xRequestId, page, pageSize, showLastHours)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> listActions( - Integer page, - Integer pageSize, - Integer showLastHours, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(LIST, exchange) - .then(actionService.listActions(xRequestId, page, pageSize, showLastHours)) - .map(ResponseEntity::ok); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/controller/BffControllerAdvice.java b/lib/src/main/java/org/onap/portal/bff/controller/BffControllerAdvice.java deleted file mode 100644 index 3580495..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/BffControllerAdvice.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.zalando.problem.spring.webflux.advice.ProblemHandling; - -@RestControllerAdvice -public class BffControllerAdvice implements ProblemHandling {} diff --git a/lib/src/main/java/org/onap/portal/bff/controller/PreferencesController.java b/lib/src/main/java/org/onap/portal/bff/controller/PreferencesController.java deleted file mode 100644 index 625d034..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/PreferencesController.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import javax.validation.Valid; -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.openapi.server.api.PreferencesApi; -import org.onap.portal.bff.openapi.server.model.CreatePreferencesRequestApiDto; -import org.onap.portal.bff.openapi.server.model.PreferencesResponseApiDto; -import org.onap.portal.bff.services.PreferencesService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ServerWebExchange; -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; - - public PreferencesController(PortalBffConfig bffConfig, PreferencesService preferencesService) { - super(bffConfig); - this.preferencesService = preferencesService; - } - - @Override - public Mono> getPreferences( - String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(GET, exchange) - .then(preferencesService.getPreferences(xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> savePreferences( - @Valid Mono preferencesApiDto, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(CREATE, exchange) - .then(preferencesApiDto) - .flatMap(request -> preferencesService.createPreferences(xRequestId, request)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> updatePreferences( - @Valid Mono preferencesApiDto, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(UPDATE, exchange) - .then(preferencesApiDto) - .flatMap(request -> preferencesService.updatePreferences(xRequestId, request)) - .map(ResponseEntity::ok); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/controller/RolesController.java b/lib/src/main/java/org/onap/portal/bff/controller/RolesController.java deleted file mode 100644 index 34d495f..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/RolesController.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.openapi.server.api.RolesApi; -import org.onap.portal.bff.openapi.server.model.RoleListResponseApiDto; -import org.onap.portal.bff.services.KeycloakService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -@RestController -public class RolesController extends AbstractBffController implements RolesApi { - - public static final String LIST = "ROLE_LIST"; - - private final KeycloakService keycloakService; - - @Autowired - public RolesController(PortalBffConfig bffConfig, KeycloakService keycloakService) { - super(bffConfig); - this.keycloakService = keycloakService; - } - - @Override - public Mono> listRoles( - String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(LIST, exchange) - .thenMany(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/portal/bff/controller/UsersController.java b/lib/src/main/java/org/onap/portal/bff/controller/UsersController.java deleted file mode 100644 index 9214ebe..0000000 --- a/lib/src/main/java/org/onap/portal/bff/controller/UsersController.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.controller; - -import org.onap.portal.bff.config.PortalBffConfig; -import org.onap.portal.bff.openapi.server.api.UsersApi; -import org.onap.portal.bff.openapi.server.model.CreateUserRequestApiDto; -import org.onap.portal.bff.openapi.server.model.RoleApiDto; -import org.onap.portal.bff.openapi.server.model.RoleListResponseApiDto; -import org.onap.portal.bff.openapi.server.model.UpdateUserPasswordRequestApiDto; -import org.onap.portal.bff.openapi.server.model.UpdateUserRequestApiDto; -import org.onap.portal.bff.openapi.server.model.UserListResponseApiDto; -import org.onap.portal.bff.openapi.server.model.UserResponseApiDto; -import org.onap.portal.bff.services.KeycloakService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; -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; - - @Autowired - public UsersController(PortalBffConfig bffConfig, KeycloakService keycloakService) { - super(bffConfig); - this.keycloakService = keycloakService; - } - - @Override - public Mono> createUser( - Mono requestMono, String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(CREATE, exchange) - .then(requestMono.flatMap(request -> keycloakService.createUser(request, xRequestId))) - .map(ResponseEntity::ok); - } - - @Override - public Mono> getUser( - String userId, String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(GET, exchange) - .then(keycloakService.getUser(userId, xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> updateUser( - String userId, - Mono requestMono, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(UPDATE, exchange) - .then(requestMono) - .flatMap(request -> keycloakService.updateUser(userId, request, xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> deleteUser( - String userId, String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(DELETE, exchange) - .then(keycloakService.deleteUser(userId, xRequestId)) - .thenReturn(ResponseEntity.noContent().build()); - } - - @Override - public Mono> listUsers( - Integer page, Integer pageSize, String xRequestId, ServerWebExchange exchange) { - - return checkRoleAccess(LIST, exchange) - .then(keycloakService.listUsers(page, pageSize, xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> updatePassword( - String userId, - Mono requestMono, - String xRequestId, - ServerWebExchange exchange) { - return checkRoleAccess(UPDATE_PASSWORD, exchange) - .then(requestMono) - .flatMap(request -> keycloakService.updateUserPassword(userId, request)) - .thenReturn(ResponseEntity.noContent().build()); - } - - @Override - public Mono> listAvailableRoles( - String userId, String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(LIST_AVAILABLE_ROLES, exchange) - .then(keycloakService.getAvailableRoles(userId, xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> listAssignedRoles( - String userId, String xRequestId, ServerWebExchange exchange) { - return checkRoleAccess(LIST_ROLES, exchange) - .then(keycloakService.getAssignedRoles(userId, xRequestId)) - .map(ResponseEntity::ok); - } - - @Override - public Mono> updateAssignedRoles( - String userId, String xRequestId, Flux rolesFlux, ServerWebExchange exchange) { - return checkRoleAccess(UPDATE_ROLES, exchange) - .then(rolesFlux.collectList()) - .flatMap(roles -> keycloakService.updateAssignedRoles(userId, roles, xRequestId)) - .map(ResponseEntity::ok); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/exceptions/DownstreamApiProblemException.java b/lib/src/main/java/org/onap/portal/bff/exceptions/DownstreamApiProblemException.java deleted file mode 100644 index 35b895e..0000000 --- a/lib/src/main/java/org/onap/portal/bff/exceptions/DownstreamApiProblemException.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.exceptions; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.net.URI; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; -import org.onap.portal.bff.openapi.server.model.ConstraintViolationApiDto; -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Problem; -import org.zalando.problem.Status; -import org.zalando.problem.StatusType; - -/** The default portal-bff exception */ -@Getter -@Builder -@AllArgsConstructor -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -@ToString -@JsonIgnoreProperties -public class DownstreamApiProblemException extends AbstractThrowableProblem { - - @Builder.Default private final URI type = Problem.DEFAULT_TYPE; - @Builder.Default private final String title = "Bad gateway error"; - - @JsonIgnore @Builder.Default private final transient StatusType status = Status.BAD_GATEWAY; - - @Builder.Default - private final String detail = "Please find more detail under correlationId: 'TODO'"; - - @Builder.Default private final String downstreamSystem = null; - @Builder.Default private final URI instance = null; - @Builder.Default private final Integer downstreamStatus = null; - @Builder.Default private final String downstreamMessageId = null; - - @JsonIgnore @Builder.Default - private final transient List violations = null; -} diff --git a/lib/src/main/java/org/onap/portal/bff/mappers/ActionsMapper.java b/lib/src/main/java/org/onap/portal/bff/mappers/ActionsMapper.java deleted file mode 100644 index 588deba..0000000 --- a/lib/src/main/java/org/onap/portal/bff/mappers/ActionsMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.mappers; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.onap.portal.bff.config.MapperSpringConfig; -import org.onap.portal.bff.openapi.client_portal_history.model.ActionsListResponsePortalHistoryDto; -import org.onap.portal.bff.openapi.server.model.ActionsListResponseApiDto; -import org.springframework.core.convert.converter.Converter; - -@Mapper(config = MapperSpringConfig.class) -public interface ActionsMapper - extends Converter { - - @Mapping(source = "actionsList", target = "items") - ActionsListResponseApiDto convert(ActionsListResponsePortalHistoryDto source); -} diff --git a/lib/src/main/java/org/onap/portal/bff/mappers/CredentialMapper.java b/lib/src/main/java/org/onap/portal/bff/mappers/CredentialMapper.java deleted file mode 100644 index e1db8de..0000000 --- a/lib/src/main/java/org/onap/portal/bff/mappers/CredentialMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.mappers; - -import org.mapstruct.Mapper; -import org.mapstruct.ReportingPolicy; -import org.onap.portal.bff.config.MapperSpringConfig; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.CredentialKeycloakDto; -import org.onap.portal.bff.openapi.server.model.UpdateUserPasswordRequestApiDto; - -@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface CredentialMapper { - CredentialKeycloakDto convert(UpdateUserPasswordRequestApiDto source); -} diff --git a/lib/src/main/java/org/onap/portal/bff/mappers/PreferencesMapper.java b/lib/src/main/java/org/onap/portal/bff/mappers/PreferencesMapper.java deleted file mode 100644 index 8a554fe..0000000 --- a/lib/src/main/java/org/onap/portal/bff/mappers/PreferencesMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.mappers; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.onap.portal.bff.config.MapperSpringConfig; -import org.onap.portal.bff.openapi.client_portal_prefs.model.PreferencesPortalPrefsDto; -import org.onap.portal.bff.openapi.server.model.PreferencesResponseApiDto; -import org.springframework.core.convert.converter.Converter; - -@Mapper(config = MapperSpringConfig.class) -public interface PreferencesMapper - extends Converter { - - @Mapping(source = "properties", target = "properties") - PreferencesResponseApiDto convert(PreferencesPortalPrefsDto source); -} diff --git a/lib/src/main/java/org/onap/portal/bff/mappers/RolesMapper.java b/lib/src/main/java/org/onap/portal/bff/mappers/RolesMapper.java deleted file mode 100644 index 68d00d8..0000000 --- a/lib/src/main/java/org/onap/portal/bff/mappers/RolesMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.mappers; - -import org.mapstruct.Mapper; -import org.mapstruct.ReportingPolicy; -import org.onap.portal.bff.config.MapperSpringConfig; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.RoleKeycloakDto; -import org.onap.portal.bff.openapi.server.model.RoleApiDto; -import org.springframework.core.convert.converter.Converter; - -@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface RolesMapper extends Converter { - RoleApiDto convert(RoleKeycloakDto source); - - RoleKeycloakDto convert(RoleApiDto source); -} diff --git a/lib/src/main/java/org/onap/portal/bff/mappers/UsersMapper.java b/lib/src/main/java/org/onap/portal/bff/mappers/UsersMapper.java deleted file mode 100644 index 19cb4c7..0000000 --- a/lib/src/main/java/org/onap/portal/bff/mappers/UsersMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.mappers; - -import java.util.List; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.ReportingPolicy; -import org.onap.portal.bff.config.MapperSpringConfig; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.RequiredActionsKeycloakDto; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.UserKeycloakDto; -import org.onap.portal.bff.openapi.server.model.CreateUserRequestApiDto; -import org.onap.portal.bff.openapi.server.model.UpdateUserRequestApiDto; -import org.onap.portal.bff.openapi.server.model.UserResponseApiDto; -import org.springframework.core.convert.converter.Converter; - -@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface UsersMapper extends Converter { - - UserResponseApiDto convert(UserKeycloakDto source); - - @Mapping(source = "roles", target = "realmRoles") - UserResponseApiDto convert(UserKeycloakDto source, List roles); - - @Mapping(source = "actions", target = "requiredActions") - UserKeycloakDto convert(CreateUserRequestApiDto source, List actions); - - UserKeycloakDto convert(UpdateUserRequestApiDto source); -} diff --git a/lib/src/main/java/org/onap/portal/bff/services/ActionService.java b/lib/src/main/java/org/onap/portal/bff/services/ActionService.java deleted file mode 100644 index 0358d29..0000000 --- a/lib/src/main/java/org/onap/portal/bff/services/ActionService.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.services; - -import lombok.RequiredArgsConstructor; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.client_portal_history.api.ActionsApi; -import org.onap.portal.bff.openapi.client_portal_history.model.CreateActionRequestPortalHistoryDto; -import org.onap.portal.bff.openapi.server.model.ActionsListResponseApiDto; -import org.onap.portal.bff.openapi.server.model.ActionsResponseApiDto; -import org.onap.portal.bff.openapi.server.model.CreateActionRequestApiDto; -import org.onap.portal.bff.openapi.server.model.ProblemApiDto; -import org.onap.portal.bff.utils.Logger; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.stereotype.Service; -import reactor.core.publisher.Mono; - -@RequiredArgsConstructor -@Service -public class ActionService { - private final ActionsApi actionsApi; - private final ConfigurableConversionService conversionService; - - public Mono createAction( - String userId, String xRequestId, CreateActionRequestApiDto createActionRequestApiDto) { - // First map from server API model to client API model - CreateActionRequestPortalHistoryDto createActionRequestPortalHistoryDto = - new CreateActionRequestPortalHistoryDto(); - createActionRequestPortalHistoryDto.setUserId(createActionRequestApiDto.getUserId()); - createActionRequestPortalHistoryDto.setAction(createActionRequestApiDto.getAction()); - createActionRequestPortalHistoryDto.setActionCreatedAt( - createActionRequestApiDto.getActionCreatedAt()); - - return actionsApi - .createAction(userId, xRequestId, createActionRequestPortalHistoryDto) - .map( - action -> - new ActionsResponseApiDto() - .action(action.getAction()) - .actionCreatedAt(action.getActionCreatedAt())) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Create actions failed for userId", - userId, - ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); - return Mono.error(ex); - }); - } - - public Mono getActions( - String userId, String xRequestId, Integer page, Integer pageSize, Integer showLastHours) { - - return actionsApi - .getActions(userId, xRequestId, page, pageSize, showLastHours) - .map(actions -> conversionService.convert(actions, ActionsListResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Get actions failed for userId", - userId, - ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); - return Mono.error(ex); - }); - } - - public Mono listActions( - String xRequestId, Integer page, Integer pageSize, Integer showLast) { - return actionsApi - .listActions(xRequestId, page, pageSize, showLast) - .map( - responseEntity -> - conversionService.convert(responseEntity, ActionsListResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "List actions failed", - null, - ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); - return Mono.error(ex); - }); - } - - public Mono deleteActions(String userId, String xRequestId, Integer deleteAfterHours) { - return actionsApi - .deleteActions(userId, xRequestId, deleteAfterHours) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Get actions failed for userId because actions cannot be deleted after " - + deleteAfterHours - + " hours", - userId, - ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); - return Mono.error(ex); - }); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/services/KeycloakService.java b/lib/src/main/java/org/onap/portal/bff/services/KeycloakService.java deleted file mode 100644 index 765efa7..0000000 --- a/lib/src/main/java/org/onap/portal/bff/services/KeycloakService.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.services; - -import java.net.URI; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.mappers.CredentialMapper; -import org.onap.portal.bff.mappers.RolesMapper; -import org.onap.portal.bff.mappers.UsersMapper; -import org.onap.portal.bff.openapi.client_portal_keycloak.api.KeycloakApi; -import org.onap.portal.bff.openapi.client_portal_keycloak.model.RequiredActionsKeycloakDto; -import org.onap.portal.bff.openapi.server.model.*; -import org.onap.portal.bff.utils.Logger; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.zalando.problem.Status; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.function.Tuples; - -@Slf4j -@RequiredArgsConstructor -@Service -public class KeycloakService { - private final KeycloakApi keycloakApi; - private final ConfigurableConversionService conversionService; - private final RolesMapper rolesMapper; - private final UsersMapper usersMapper; - private final CredentialMapper credentialMapper; - - public Mono createUser(CreateUserRequestApiDto request, String xRequestId) { - log.debug("Create user in keycloak. request=`{}`", request); - - final List rolesToBeAssigned = - request.getRoles().isEmpty() ? Collections.emptyList() : request.getRoles(); - return listRoles(xRequestId) - .collectList() - .flatMap( - realmRoles -> { - final List absentRoles = - rolesToBeAssigned.stream().filter(role -> !realmRoles.contains(role)).toList(); - if (!absentRoles.isEmpty()) { - return Mono.error( - DownstreamApiProblemException.builder() - .status(Status.NOT_FOUND) - .detail( - String.format( - "Roles not found in the realm: %s", - absentRoles.stream().map(RoleApiDto::getName).toList())) - .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()) - .title(HttpStatus.NOT_FOUND.toString()) - .build()); - } - return Mono.just(rolesToBeAssigned); - }) - .flatMap(roles -> createUserWithRoles(request, xRequestId, roles)); - } - - private Mono createUserWithRoles( - CreateUserRequestApiDto request, String xRequestId, List roles) { - return keycloakApi - .createUserWithHttpInfo( - usersMapper.convert(request, List.of(RequiredActionsKeycloakDto.UPDATE_PASSWORD))) - .map(responseEntit -> responseEntit.getHeaders().getLocation()) - .map(URI::toString) - .map(location -> location.substring(location.lastIndexOf("/") + 1)) - .flatMap(userId -> !roles.isEmpty() ? assignRoles(userId, roles) : Mono.just(userId)) - .flatMap( - userId -> sendActionEmail(userId, List.of(RequiredActionsKeycloakDto.UPDATE_PASSWORD))) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Create user failed at sending update-password email for userName", - request.getUsername(), - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }) - .flatMap((String userId1) -> getUser(userId1, xRequestId)); - } - - public Mono getUser(String userId, String xRequestId) { - log.debug("Get user from keycloak. userId=`{}`", userId); - return Mono.zip( - keycloakApi - .getUser(userId) - .map(user -> conversionService.convert(user, UserResponseApiDto.class)), - getAssignedRoles(userId, xRequestId)) - .map( - tuple -> - new UserResponseApiDto() - .username(tuple.getT1().getUsername()) - .email(tuple.getT1().getEmail()) - .enabled(tuple.getT1().getEnabled()) - .id(tuple.getT1().getId()) - .firstName(tuple.getT1().getFirstName()) - .lastName(tuple.getT1().getLastName()) - .realmRoles( - tuple.getT2().getItems().stream().map(RoleApiDto::getName).toList())) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Failed to get user", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Mono listUsers(int page, int pageSize, String xRequestId) { - log.debug("Get users from keycloak. page=`{}`, pageSize=`{}`", page, pageSize); - final int first = (page - 1) * pageSize; - - var userIdRoleMap = - listRoles(xRequestId) - .flatMap( - role -> - listUsersByRole(role.getName(), xRequestId) - .map(userResponse -> Tuples.of(role.getName(), userResponse))) - .collectList() - .map( - tupleList -> - tupleList.stream() - .collect( - Collectors.groupingBy( - tuple -> tuple.getT2().getId(), - Collectors.mapping(tuple -> tuple.getT1(), Collectors.toList())))); - - return Mono.zip( - keycloakApi.getUsersCount(null, null, null, null, null, null, null), - keycloakApi - .getUsers( - null, null, null, null, null, null, null, null, first, pageSize, null, null, - null, null) - .collectList(), - userIdRoleMap) - .map( - tuple -> { - final UserListResponseApiDto result = new UserListResponseApiDto(); - Map> userRoleMap = tuple.getT3(); - result.setTotalCount(tuple.getT1()); - var roleList = - tuple.getT2().stream() - .map( - user -> - usersMapper.convert( - user, - Optional.ofNullable(userRoleMap.get(user.getId())) - .orElse(Collections.emptyList()))) - .toList(); - result.setItems(roleList); - return result; - }) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "List users failed", - null, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Mono updateUser(String userId, UpdateUserRequestApiDto request, String xRequestId) { - log.debug("Update user in keycloak. userId=`{}`, request=`{}`", userId, request); - return keycloakApi - .updateUser(userId, usersMapper.convert(request)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Failed to update user", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Mono updateUserPassword(String userId, UpdateUserPasswordRequestApiDto request) { - log.debug( - "Update password for user in keycloak. userId=`{}`, temporary=`{}`", - userId, - request.getTemporary()); - - return keycloakApi.resetUserPassword(userId, credentialMapper.convert(request)); - } - - public Mono deleteUser(String userId, String xRequestId) { - log.debug("Delete user from keycloak. userId=`{}`", userId); - - return keycloakApi - .deleteUser(userId) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Failed to delete user", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Mono assignRoles(String userId, List roles) { - log.debug( - "Assign roles to user in keycloak. userId=`{}`, roleIds=`{}`", - userId, - roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); - - return keycloakApi - .addRealmRoleMappingsToUser(userId, roles.stream().map(rolesMapper::convert).toList()) - .thenReturn(userId); - } - - public Mono updateAssignedRoles( - String userId, List roles, String xRequestId) { - log.debug( - "Update assigned roles for user in keycloak. userId=`{}`, roleIds=`{}`", - userId, - roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); - - return getAssignedRoles(userId, xRequestId) - .map(response -> response.getItems()) - .flatMap( - assignedRoles -> { - if (assignedRoles.isEmpty()) { - return Mono.empty(); - } - return unassignRoles(userId, assignedRoles); - }) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Update assigned roles failed for userId", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }) - .then( - Mono.defer( - () -> { - if (roles.isEmpty()) { - return Mono.empty(); - } - return assignRoles(userId, roles); - })) - .then(Mono.defer(() -> getAssignedRoles(userId, xRequestId))); - } - - public Mono unassignRoles(String userId, List roles) { - log.debug( - "Unassign roles from user in keycloak. userId=`{}`, roleIds=`{}`", - userId, - roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); - - return keycloakApi.deleteRealmRoleMappingsByUserId( - userId, roles.stream().map(rolesMapper::convert).toList()); - } - - public Mono sendActionEmail( - String userId, java.util.List requiredActions) { - log.debug( - "Sending update actions email to user in keycloak. userId=`{}`, actions=`{}`", - userId, - requiredActions); - return keycloakApi - .executeActionsEmail(userId, null, null, null, requiredActions) - .thenReturn(userId); - } - - public Flux listRoles(String xRequestId) { - return keycloakApi - .getRoles(null, null, null, null) - .map(role -> conversionService.convert(role, RoleApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog(xRequestId, "Get realm roles failed for ID", xRequestId, "KEYCLOAK"); - return Mono.error(ex); - }); - } - - public Mono getAssignedRoles(String userId, String xRequestId) { - log.debug("Get assigned roles from keycloak. userId=`{}`", userId); - - return keycloakApi - .getRealmRoleMappingsByUserId(userId) - .map(role -> conversionService.convert(role, RoleApiDto.class)) - .collectList() - .map( - items -> { - final RoleListResponseApiDto result = new RoleListResponseApiDto(); - result.setTotalCount(items.size()); // keycloak does not support pagination for roles - result.setItems(items); - return result; - }) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Get assigned roles failed for userId", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Mono getAvailableRoles(String userId, String xRequestId) { - log.debug("Get available roles from keycloak. userId=`{}`", userId); - - return keycloakApi - .getAvailableRealmRoleMappingsByUserId(userId) - .map(role -> conversionService.convert(role, RoleApiDto.class)) - .collectList() - .map( - items -> { - final RoleListResponseApiDto result = new RoleListResponseApiDto(); - result.setTotalCount(items.size()); // keycloak does not support pagination for roles - result.setItems(items); - - return result; - }) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Get available roles failed for userId", - userId, - ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); - return Mono.error(ex); - }); - } - - public Flux listUsersByRole(String roleName, String xRequestId) { - return keycloakApi - .getUsersByRole(roleName, null, null) - .log() - .map(user -> conversionService.convert(user, UserResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, "Get users by realm role failed for ID", xRequestId, "KEYCLOAK"); - return Mono.error(ex); - }); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/services/PreferencesService.java b/lib/src/main/java/org/onap/portal/bff/services/PreferencesService.java deleted file mode 100644 index ee0a5df..0000000 --- a/lib/src/main/java/org/onap/portal/bff/services/PreferencesService.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.services; - -import lombok.RequiredArgsConstructor; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.client_portal_prefs.api.PreferencesApi; -import org.onap.portal.bff.openapi.client_portal_prefs.model.PreferencesPortalPrefsDto; -import org.onap.portal.bff.openapi.server.model.CreatePreferencesRequestApiDto; -import org.onap.portal.bff.openapi.server.model.PreferencesResponseApiDto; -import org.onap.portal.bff.utils.Logger; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.stereotype.Service; -import reactor.core.publisher.Mono; - -@RequiredArgsConstructor -@Service -public class PreferencesService { - - private static final String PREFERENCES_APPLICATION_NAME = "PORTAL_PREFS"; - - private final PreferencesApi preferencesApi; - private final ConfigurableConversionService conversionService; - - public Mono createPreferences( - String xRequestId, CreatePreferencesRequestApiDto request) { - PreferencesPortalPrefsDto preferencesPortalPrefsDto = new PreferencesPortalPrefsDto(); - preferencesPortalPrefsDto.setProperties(request.getProperties()); - return preferencesApi - .savePreferences(xRequestId, preferencesPortalPrefsDto) - .map(resp -> conversionService.convert(resp, PreferencesResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, "Preference raise error", xRequestId, PREFERENCES_APPLICATION_NAME); - return Mono.error(ex); - }); - } - - public Mono updatePreferences( - String xRequestId, CreatePreferencesRequestApiDto request) { - PreferencesPortalPrefsDto preferencesPortalPrefsDto = new PreferencesPortalPrefsDto(); - preferencesPortalPrefsDto.setProperties(request.getProperties()); - return preferencesApi - .updatePreferences(xRequestId, preferencesPortalPrefsDto) - .map(resp -> conversionService.convert(resp, PreferencesResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, "Preference raise error", xRequestId, PREFERENCES_APPLICATION_NAME); - return Mono.error(ex); - }); - } - - public Mono getPreferences(String xRequestId) { - return preferencesApi - .getPreferences(xRequestId) - .map(preferences -> conversionService.convert(preferences, PreferencesResponseApiDto.class)) - .onErrorResume( - DownstreamApiProblemException.class, - ex -> { - Logger.errorLog( - xRequestId, - "Get preferences failed for ID", - xRequestId, - PREFERENCES_APPLICATION_NAME); - return Mono.error(ex); - }); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/utils/ErrorHandler.java b/lib/src/main/java/org/onap/portal/bff/utils/ErrorHandler.java deleted file mode 100644 index 8bec189..0000000 --- a/lib/src/main/java/org/onap/portal/bff/utils/ErrorHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.utils; - -import java.util.List; -import java.util.Objects; -import org.onap.portal.bff.exceptions.DownstreamApiProblemException; -import org.onap.portal.bff.openapi.server.model.ProblemApiDto; -import org.springframework.http.HttpStatus; - -public class ErrorHandler { - /** - * Not meant to be instantiated. To prevent Java from adding an implicit public constructor to - * every class which does not define at least one explicitly. - */ - private ErrorHandler() {} - - public static String mapVariablesToDetails(List variables, String details) { - int i = 0; - for (String variable : variables) { - i++; - details = details.replace("%" + i, variable); - } - return details; - } - - public static DownstreamApiProblemException getDownstreamApiProblemException( - HttpStatus httpStatus, - List variables, - String text, - String messageId, - ProblemApiDto.DownstreamSystemEnum downStreamSystem) { - String errorDetail = - variables != null && text != null - ? ErrorHandler.mapVariablesToDetails(variables, text) - : null; - - return DownstreamApiProblemException.builder() - .title(httpStatus.toString()) - .detail(errorDetail) - .downstreamMessageId(Objects.requireNonNullElse(messageId, "not set by downstream system")) - .downstreamSystem(downStreamSystem.toString()) - .downstreamStatus(httpStatus.value()) - .build(); - } -} diff --git a/lib/src/main/java/org/onap/portal/bff/utils/Logger.java b/lib/src/main/java/org/onap/portal/bff/utils/Logger.java deleted file mode 100644 index b985ad5..0000000 --- a/lib/src/main/java/org/onap/portal/bff/utils/Logger.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright (c) 2022. 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.portal.bff.utils; - -import java.net.URI; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; - -@Slf4j -public class Logger { - - /** - * Not meant to be instantiated. To prevent Java from adding an implicit public constructor to - * every class which does not define at least one explicitly. - */ - private Logger() {} - - public static void requestLog(String xRequestId, HttpMethod methode, URI path) { - log.info("Portal-bff - request - X-Request-Id {} {} {}", xRequestId, methode, path); - } - - public static void responseLog(String xRequestId, HttpStatus code) { - log.info("Portal-bff - response - X-Request-Id {} {}", xRequestId, code); - } - - public static void errorLog(String xRequestId, String msg, String id, String app) { - log.info( - "Portal-bff - error - X-Request-Id {} {} {} not found in {}", xRequestId, msg, id, app); - } - - public static void errorLog( - String xRequestId, String msg, String id, String app, String errorDetails) { - log.info( - "Portal-bff - error - X-Request-Id {} {} {} not found in {} error message: {}", - xRequestId, - msg, - id, - app, - errorDetails); - } -} 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 new file mode 100644 index 0000000..926108a --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/BeansConfig.java @@ -0,0 +1,190 @@ +/* + * + * Copyright (c) 2022. 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.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.time.Clock; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.utils.Logger; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.http.codec.ClientCodecConfigurer; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; +import org.zalando.problem.jackson.ProblemModule; +import reactor.core.publisher.Mono; + +@Slf4j +@Configuration +public class BeansConfig { + + public static final String OAUTH2_EXCHANGE_FILTER_FUNCTION = "oauth2ExchangeFilterFunction"; + private static final String ID_TOKEN_EXCHANGE_FILTER_FUNCTION = "idTokenExchangeFilterFunction"; + private static final String ERROR_HANDLING_EXCHANGE_FILTER_FUNCTION = + "errorHandlingExchangeFilterFunction"; + private static final String LOG_REQUEST_EXCHANGE_FILTER_FUNCTION = + "logRequestExchangeFilterFunction"; + private static final String LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION = + "logResponseExchangeFilterFunction"; + private static final String CLIENT_REGISTRATION_ID = "keycloak"; + public static final String X_REQUEST_ID = "X-Request-Id"; + + @Bean(name = OAUTH2_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction oauth2ExchangeFilterFunction( + ReactiveOAuth2AuthorizedClientManager authorizedClientManager) { + final ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter = + new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); + oauth2Filter.setDefaultClientRegistrationId(CLIENT_REGISTRATION_ID); + + 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( + clientResponse -> { + if (clientResponse.statusCode().isError()) { + return clientResponse + .bodyToMono(String.class) + .doOnNext(s -> log.error("Received error response from downstream: {}", s)) + .flatMap( + downstreamExceptionBody -> { + try { + return Mono.error( + new ObjectMapper() + .readValue( + downstreamExceptionBody, DownstreamApiProblemException.class)); + } catch (JsonProcessingException e) { + return Mono.error(DownstreamApiProblemException.builder().build()); + } + }); + } + return Mono.just(clientResponse); + }); + } + + // + // Don't use this. Log will is written in the LoggerInterceptor + // + @Bean(name = LOG_REQUEST_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction logRequestExchangeFilterFunction() { + return ExchangeFilterFunction.ofRequestProcessor( + clientRequest -> { + List xRequestIdList = clientRequest.headers().get(X_REQUEST_ID); + if (xRequestIdList != null && !xRequestIdList.isEmpty()) { + String xRequestId = xRequestIdList.get(0); + Logger.requestLog(xRequestId, clientRequest.method(), clientRequest.url()); + } + return Mono.just(clientRequest); + }); + } + + @Bean(name = LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction logResponseExchangeFilterFunction() { + return ExchangeFilterFunction.ofResponseProcessor( + clientResponse -> { + String xRequestId = "not set"; + List xRequestIdList = clientResponse.headers().header(X_REQUEST_ID); + if (xRequestIdList != null && !xRequestIdList.isEmpty()) + xRequestId = xRequestIdList.get(0); + Logger.responseLog(xRequestId, clientResponse.statusCode()); + return Mono.just(clientResponse); + }); + } + + @Bean + ExchangeStrategies exchangeStrategies(ObjectMapper objectMapper) { + return ExchangeStrategies.builder() + .codecs( + configurer -> { + final ClientCodecConfigurer.ClientDefaultCodecs defaultCodecs = + configurer.defaultCodecs(); + + defaultCodecs.maxInMemorySize(16 * 1024 * 1024); // 16MB + defaultCodecs.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper)); + defaultCodecs.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper)); + }) + .build(); + } + + // we need to use prototype scope to always create new instance of the bean + // because internally WebClient.Builder is mutable + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + WebClient.Builder webClientBuilder( + ExchangeStrategies exchangeStrategies, + @Qualifier(ID_TOKEN_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction idTokenExchangeFilterFunction, + @Qualifier(ERROR_HANDLING_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction errorHandlingExchangeFilterFunction, + @Qualifier(LOG_RESPONSE_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction logResponseExchangeFilterFunction) { + return WebClient.builder() + .exchangeStrategies(exchangeStrategies) + .filter(idTokenExchangeFilterFunction) + .filter(errorHandlingExchangeFilterFunction) + .filter(logResponseExchangeFilterFunction); + } + + @Bean + Clock clock() { + return Clock.systemUTC(); + } + + @Bean + public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { + return builder + .modules(new ProblemModule(), new JavaTimeModule()) + .build() + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + } + + @Bean + public XmlMapper xmlMapper() { + return XmlMapper.builder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) + .build(); + } +} 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 new file mode 100644 index 0000000..0a78da1 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/BffConfig.java @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2022. 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 java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.zalando.problem.Problem; +import org.zalando.problem.Status; +import reactor.core.publisher.Mono; + +/** + * Class that contains configuration of the downstream apis. This could be username and password or + * urls. + */ +@Valid +@ConstructorBinding +@ConfigurationProperties("bff") +@Data +public class BffConfig { + + @NotBlank private final String realm; + @NotBlank private final String portalServiceUrl; + @NotBlank private final String portalPrefsUrl; + @NotBlank private final String portalHistoryUrl; + @NotBlank private final String keycloakUrl; + + @NotNull private final Map> accessControl; + + public Mono> getRoles(String method) { + return Mono.just(accessControl) + .map(control -> control.get(method)) + .onErrorResume( + e -> + Mono.error( + Problem.valueOf( + Status.FORBIDDEN, "The user does not have the necessary access rights"))); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/ConversionServiceConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/ConversionServiceConfig.java new file mode 100644 index 0000000..517de13 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/ConversionServiceConfig.java @@ -0,0 +1,59 @@ +/* + * + * Copyright (c) 2022. 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 java.util.List; +import org.onap.portalng.bff.mappers.ActionsMapper; +import org.onap.portalng.bff.mappers.PreferencesMapper; +import org.onap.portalng.bff.mappers.RolesMapper; +import org.onap.portalng.bff.mappers.UsersMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.core.convert.support.DefaultConversionService; + +@SuppressWarnings("rawtypes") +@Configuration +public class ConversionServiceConfig { + + @Bean + public ConfigurableConversionService conversionService( + ActionsMapper actionsMapper, + PreferencesMapper preferencesMapper, + RolesMapper rolesMapper, + UsersMapper usersMapper) { + final List converters = + List.of( + actionsMapper, + preferencesMapper, + preferencesMapper, + actionsMapper, + rolesMapper, + usersMapper); + + final ConfigurableConversionService conversionService = new DefaultConversionService(); + converters.forEach(conversionService::addConverter); + + return conversionService; + } +} 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 new file mode 100644 index 0000000..d747f3a --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/IdTokenExchangeFilterFunction.java @@ -0,0 +1,125 @@ +/* + * + * Copyright (c) 2022. 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.nimbusds.jwt.JWTParser; +import java.text.ParseException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.server.ServerWebExchange; +import org.zalando.problem.Problem; +import org.zalando.problem.Status; +import reactor.core.Exceptions; +import reactor.core.publisher.Mono; + +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 static final Mono serverWebExchangeFromContext = + Mono.deferContextual(Mono::just) + .filter(context -> context.hasKey(ServerWebExchange.class)) + .map(context -> context.get(ServerWebExchange.class)); + + @Override + public Mono filter(ClientRequest request, ExchangeFunction next) { + boolean shouldNotFilter = + EXCLUDED_PATHS_PATTERNS.stream() + .anyMatch( + excludedPath -> + new AntPathMatcher().match(excludedPath, request.url().getRawPath())); + if (shouldNotFilter) { + return next.exchange(request).switchIfEmpty(Mono.defer(() -> next.exchange(request))); + } + return extractServerWebExchange(request) + .flatMap(IdTokenExchangeFilterFunction::extractIdentityHeader) + .map(idToken -> ClientRequest.from(request).header(X_AUTH_IDENTITY_HEADER, idToken).build()) + .flatMap(requestWithIdToken -> next.exchange(requestWithIdToken)) + .switchIfEmpty(Mono.defer(() -> next.exchange(request))); + } + + private Mono extractServerWebExchange(ClientRequest request) { + return Mono.justOrEmpty(request.attribute(ServerWebExchange.class.getName())) + .cast(ServerWebExchange.class) + .switchIfEmpty(serverWebExchangeFromContext); + } + + private static Mono extractIdentityHeader(ServerWebExchange exchange) { + return Mono.just(exchange) + .map(exch -> exch.getRequest().getHeaders().getOrEmpty(X_AUTH_IDENTITY_HEADER).get(0)) + .onErrorResume(ex -> Mono.error(Problem.valueOf(Status.FORBIDDEN, "ID token is missing"))); + } + + private static Mono extractIdToken(ServerWebExchange exchange) { + return extractIdentityHeader(exchange) + .map(identityHeader -> identityHeader.replace("Bearer ", "")); + } + + public static Mono validateAccess( + ServerWebExchange exchange, List rolesListForMethod) { + + return extractRoles(exchange) + .map(roles -> roles.stream().anyMatch(rolesListForMethod::contains)) + .flatMap( + match -> + Boolean.TRUE.equals(match) + ? Mono.empty() + : Mono.error(Problem.valueOf(Status.FORBIDDEN))); + } + + private static Mono> extractRoles(ServerWebExchange exchange) { + return extractIdToken(exchange) + .map( + token -> { + try { + return JWTParser.parse(token); + } catch (ParseException e) { + throw Exceptions.propagate(e); + } + }) + .map( + jwt -> { + try { + return Optional.of(jwt.getJWTClaimsSet()); + } 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())); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/LoggerInterceptor.java b/lib/src/main/java/org/onap/portalng/bff/config/LoggerInterceptor.java new file mode 100644 index 0000000..5b9b492 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/LoggerInterceptor.java @@ -0,0 +1,52 @@ +/* + * + * Copyright (c) 2022. 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 java.util.List; +import org.onap.portalng.bff.utils.Logger; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.reactive.ServerWebExchangeContextFilter; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +@Component +public class LoggerInterceptor extends ServerWebExchangeContextFilter { + public static final String EXCHANGE_CONTEXT_ATTRIBUTE = + ServerWebExchangeContextFilter.class.getName() + ".EXCHANGE_CONTEXT"; + + public static final String X_REQUEST_ID = "X-Request-Id"; + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + List xRequestIdList = exchange.getRequest().getHeaders().get(X_REQUEST_ID); + if (xRequestIdList != null && !xRequestIdList.isEmpty()) { + String xRequestId = xRequestIdList.get(0); + Logger.requestLog( + xRequestId, exchange.getRequest().getMethod(), exchange.getRequest().getURI()); + exchange.getResponse().getHeaders().add(X_REQUEST_ID, xRequestId); + } + return chain + .filter(exchange) + .contextWrite(cxt -> cxt.put(EXCHANGE_CONTEXT_ATTRIBUTE, exchange)); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/MapperSpringConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/MapperSpringConfig.java new file mode 100644 index 0000000..cc48080 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/MapperSpringConfig.java @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2022. 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 org.mapstruct.MapperConfig; +import org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator; + +@MapperConfig(componentModel = "spring", uses = ConversionServiceAdapterGenerator.class) +public interface MapperSpringConfig {} 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 new file mode 100644 index 0000000..2a0f701 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/SecurityConfig.java @@ -0,0 +1,77 @@ +/* + * + * Copyright (c) 2022. 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 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; +import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +@Configuration +public class SecurityConfig { + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + return http.httpBasic() + .disable() + .formLogin() + .disable() + .csrf() + .disable() + .cors() + .and() + .authorizeExchange() + .pathMatchers(HttpMethod.GET, "/api-docs.html", "/api.yaml", "/webjars/**", "/actuator/**") + .permitAll() + .anyExchange() + .authenticated() + .and() + .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt) + .oauth2Client() + .and() + .build(); + } + + @Bean + ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager( + ReactiveClientRegistrationRepository clientRegistrationRepository, + ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { + + final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = + ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build(); + + final DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = + new DefaultReactiveOAuth2AuthorizedClientManager( + clientRegistrationRepository, authorizedClientRepository); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + + return authorizedClientManager; + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/clients/AbstractClientConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/clients/AbstractClientConfig.java new file mode 100644 index 0000000..75dce3d --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/clients/AbstractClientConfig.java @@ -0,0 +1,87 @@ +/* + * + * Copyright (c) 2022. 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.clients; + +import java.time.Duration; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.netty.resources.ConnectionProvider; + +@Slf4j +@RequiredArgsConstructor +public abstract class AbstractClientConfig { + private final Class errorResponseTypeClass; + + protected ExchangeFilterFunction errorHandlingExchangeFilterFunction() { + return ExchangeFilterFunction.ofResponseProcessor( + clientResponse -> { + if (clientResponse.statusCode().isError()) { + return clientResponse + .bodyToMono(errorResponseTypeClass) + .doOnNext(s -> log.error("Received error response from downstream: {}", s)) + .flatMap( + problemResponse -> + Mono.error(mapException(problemResponse, clientResponse.statusCode()))); + } + return Mono.just(clientResponse); + }); + } + + protected abstract DownstreamApiProblemException mapException( + E errorResponse, HttpStatus httpStatus); + + protected ClientHttpConnector getClientHttpConnector() { + // ConnectionTimeouts introduced due to + // io.netty.channel.unix.Errors$NativeIoException: readAddress(..) failed: Connection reset by + // peer issue + // https://github.com/reactor/reactor-netty/issues/1774#issuecomment-908066283 + ConnectionProvider connectionProvider = + ConnectionProvider.builder("fixed") + .maxConnections(500) + .maxIdleTime(Duration.ofSeconds(20)) + .maxLifeTime(Duration.ofSeconds(60)) + .pendingAcquireTimeout(Duration.ofSeconds(60)) + .evictInBackground(Duration.ofSeconds(120)) + .build(); + return new ReactorClientHttpConnector(HttpClient.create(connectionProvider)); + } + + protected WebClient getWebClient( + WebClient.Builder webClientBuilder, List filters) { + if (filters != null) { + filters.forEach(webClientBuilder::filter); + } + return webClientBuilder + .filter(errorHandlingExchangeFilterFunction()) + .clientConnector(getClientHttpConnector()) + .build(); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/clients/KeycloakConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/clients/KeycloakConfig.java new file mode 100644 index 0000000..d96db71 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/clients/KeycloakConfig.java @@ -0,0 +1,100 @@ +/* + * + * Copyright (c) 2022. 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.clients; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import java.util.function.Function; +import org.onap.portalng.bff.config.BeansConfig; +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.client_portal_keycloak.ApiClient; +import org.onap.portalng.bff.openapi.client_portal_keycloak.api.KeycloakApi; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.ErrorResponseKeycloakDto; +import org.onap.portalng.bff.openapi.server.model.ProblemApiDto; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class KeycloakConfig extends AbstractClientConfig { + private final ObjectMapper objectMapper; + private final BffConfig bffConfig; + private final ExchangeFilterFunction oauth2ExchangeFilterFunction; + + public KeycloakConfig( + @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction oauth2ExchangeFilterFunction, + ObjectMapper objectMapper, + BffConfig bffConfig) { + super(ErrorResponseKeycloakDto.class); + this.objectMapper = objectMapper; + this.bffConfig = bffConfig; + this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; + } + + @Bean + public KeycloakApi keycloakApi(WebClient.Builder webClientBuilder) { + return constructApiClient(webClientBuilder, KeycloakApi::new); + } + + private T constructApiClient( + WebClient.Builder webClientBuilder, Function apiConstructor) { + final ApiClient apiClient = + new ApiClient( + getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), + objectMapper, + objectMapper.getDateFormat()); + + // Extract service name and version from BasePath + String urlBasePathPrefix = + String.format("%s/auth/admin/realms/%s", bffConfig.getKeycloakUrl(), bffConfig.getRealm()); + + return apiConstructor.apply(apiClient.setBasePath(urlBasePathPrefix)); + } + + @Override + protected DownstreamApiProblemException mapException( + ErrorResponseKeycloakDto errorResponse, HttpStatus httpStatus) { + String errorDetail = + errorResponse.getErrorMessage() != null + ? errorResponse.getErrorMessage() + : errorResponse.getError(); + + return DownstreamApiProblemException.builder() + .title(httpStatus.toString()) + .detail(errorDetail) + .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()) + .downstreamMessageId("not set by downstream system") + .downstreamStatus(httpStatus.value()) + .build(); + } + + @Override + protected ClientHttpConnector getClientHttpConnector() { + return null; + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalHistoryConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalHistoryConfig.java new file mode 100644 index 0000000..00cda73 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalHistoryConfig.java @@ -0,0 +1,97 @@ +/* + * + * Copyright (c) 2022. 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.clients; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.function.Function; +import lombok.extern.slf4j.Slf4j; +import org.onap.portalng.bff.config.BeansConfig; +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.client_portal_history.ApiClient; +import org.onap.portalng.bff.openapi.client_portal_history.api.ActionsApi; +import org.onap.portalng.bff.openapi.client_portal_history.model.ProblemPortalHistoryDto; +import org.onap.portalng.bff.openapi.server.model.ProblemApiDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Configuration +public class PortalHistoryConfig extends AbstractClientConfig { + private final ObjectMapper objectMapper; + private final BffConfig bffConfig; + private final ExchangeFilterFunction oauth2ExchangeFilterFunction; + + @Autowired + public PortalHistoryConfig( + @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction oauth2ExchangeFilterFunction, + ObjectMapper objectMapper, + BffConfig bffConfig) { + super(ProblemPortalHistoryDto.class); + this.objectMapper = objectMapper; + this.bffConfig = bffConfig; + this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; + } + + @Bean + public ActionsApi portalHistoryActionApi(WebClient.Builder webClientBuilder) { + return constructApiClient(webClientBuilder, ActionsApi::new); + } + + private T constructApiClient( + WebClient.Builder webClientBuilder, Function apiConstructor) { + final ApiClient apiClient = + new ApiClient( + getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), + objectMapper, + objectMapper.getDateFormat()); + final String generatedBasePath = apiClient.getBasePath(); + String basePath = ""; + try { + basePath = bffConfig.getPortalHistoryUrl() + new URL(generatedBasePath).getPath(); + } catch (MalformedURLException e) { + log.error(e.getLocalizedMessage()); + } + return apiConstructor.apply(apiClient.setBasePath(basePath)); + } + + @Override + protected DownstreamApiProblemException mapException( + ProblemPortalHistoryDto errorResponse, HttpStatus httpStatus) { + return DownstreamApiProblemException.builder() + .title(httpStatus.toString()) + .detail(errorResponse.getDetail()) + .downstreamMessageId(errorResponse.getType()) + .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()) + .downstreamStatus(httpStatus.value()) + .build(); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalPrefsConfig.java b/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalPrefsConfig.java new file mode 100644 index 0000000..c1aacbe --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/config/clients/PortalPrefsConfig.java @@ -0,0 +1,96 @@ +/* + * + * Copyright (c) 2022. 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.clients; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.function.Function; +import lombok.extern.slf4j.Slf4j; +import org.onap.portalng.bff.config.BeansConfig; +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.client_portal_prefs.ApiClient; +import org.onap.portalng.bff.openapi.client_portal_prefs.api.PreferencesApi; +import org.onap.portalng.bff.openapi.client_portal_prefs.model.ProblemPortalPrefsDto; +import org.onap.portalng.bff.openapi.server.model.ProblemApiDto; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Configuration +public class PortalPrefsConfig extends AbstractClientConfig { + private final ObjectMapper objectMapper; + private final BffConfig bffConfig; + private final ExchangeFilterFunction oauth2ExchangeFilterFunction; + + public PortalPrefsConfig( + @Qualifier(BeansConfig.OAUTH2_EXCHANGE_FILTER_FUNCTION) + ExchangeFilterFunction oauth2ExchangeFilterFunction, + ObjectMapper objectMapper, + BffConfig bffConfig) { + super(ProblemPortalPrefsDto.class); + this.objectMapper = objectMapper; + this.bffConfig = bffConfig; + this.oauth2ExchangeFilterFunction = oauth2ExchangeFilterFunction; + } + + @Bean + public PreferencesApi portalPrefsApi(WebClient.Builder webClientBuilder) { + return constructApiClient(webClientBuilder, PreferencesApi::new); + } + + private T constructApiClient( + WebClient.Builder webClientBuilder, Function apiConstructor) { + final ApiClient apiClient = + new ApiClient( + getWebClient(webClientBuilder, List.of(oauth2ExchangeFilterFunction)), + objectMapper, + objectMapper.getDateFormat()); + + final String generatedBasePath = apiClient.getBasePath(); + String basePath = ""; + try { + basePath = bffConfig.getPortalPrefsUrl() + new URL(generatedBasePath).getPath(); + } catch (MalformedURLException e) { + log.error(e.getLocalizedMessage()); + } + return apiConstructor.apply(apiClient.setBasePath(basePath)); + } + + @Override + protected DownstreamApiProblemException mapException( + ProblemPortalPrefsDto errorResponse, HttpStatus httpStatus) { + return DownstreamApiProblemException.builder() + .title(httpStatus.toString()) + .detail(errorResponse.getDetail()) + .downstreamMessageId(errorResponse.getType()) + .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.PORTAL_PREFS.toString()) + .downstreamStatus(httpStatus.value()) + .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 new file mode 100644 index 0000000..afcb448 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/AbstractBffController.java @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2022. 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.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 { + + protected BffConfig bffConfig; + + protected AbstractBffController(BffConfig bffConfig) { + this.bffConfig = bffConfig; + } + + public Mono 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 new file mode 100644 index 0000000..8b8f615 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/ActionsController.java @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2022. 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.controller; + +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.openapi.server.api.ActionsApi; +import org.onap.portalng.bff.openapi.server.model.ActionsListResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.ActionsResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.CreateActionRequestApiDto; +import org.onap.portalng.bff.services.ActionService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +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; + + public ActionsController(BffConfig bffConfig, ActionService actionService) { + super(bffConfig); + this.actionService = actionService; + } + + @Override + public Mono> createAction( + String userId, + String xRequestId, + Mono createActionRequestApiDto, + ServerWebExchange exchange) { + return checkRoleAccess(CREATE, exchange) + .then(createActionRequestApiDto) + .flatMap(action -> actionService.createAction(userId, xRequestId, action)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> getActions( + String userId, + Integer page, + Integer pageSize, + Integer showLastHours, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(GET, exchange) + .then(actionService.getActions(userId, xRequestId, page, pageSize, showLastHours)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> listActions( + Integer page, + Integer pageSize, + Integer showLastHours, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(LIST, exchange) + .then(actionService.listActions(xRequestId, page, pageSize, showLastHours)) + .map(ResponseEntity::ok); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/controller/BffControllerAdvice.java b/lib/src/main/java/org/onap/portalng/bff/controller/BffControllerAdvice.java new file mode 100644 index 0000000..7bd6efa --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/BffControllerAdvice.java @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2022. 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.controller; + +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.zalando.problem.spring.webflux.advice.ProblemHandling; + +@RestControllerAdvice +public class BffControllerAdvice implements ProblemHandling {} 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 new file mode 100644 index 0000000..c43b4c2 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/PreferencesController.java @@ -0,0 +1,77 @@ +/* + * + * Copyright (c) 2022. 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.controller; + +import javax.validation.Valid; +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.openapi.server.api.PreferencesApi; +import org.onap.portalng.bff.openapi.server.model.CreatePreferencesRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.PreferencesResponseApiDto; +import org.onap.portalng.bff.services.PreferencesService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +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; + + public PreferencesController(BffConfig bffConfig, PreferencesService preferencesService) { + super(bffConfig); + this.preferencesService = preferencesService; + } + + @Override + public Mono> getPreferences( + String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(GET, exchange) + .then(preferencesService.getPreferences(xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> savePreferences( + @Valid Mono preferencesApiDto, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(CREATE, exchange) + .then(preferencesApiDto) + .flatMap(request -> preferencesService.createPreferences(xRequestId, request)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> updatePreferences( + @Valid Mono preferencesApiDto, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(UPDATE, exchange) + .then(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 new file mode 100644 index 0000000..3958811 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/RolesController.java @@ -0,0 +1,56 @@ +/* + * + * Copyright (c) 2022. 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.controller; + +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.openapi.server.api.RolesApi; +import org.onap.portalng.bff.openapi.server.model.RoleListResponseApiDto; +import org.onap.portalng.bff.services.KeycloakService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@RestController +public class RolesController extends AbstractBffController implements RolesApi { + + public static final String LIST = "ROLE_LIST"; + + private final KeycloakService keycloakService; + + @Autowired + public RolesController(BffConfig bffConfig, KeycloakService keycloakService) { + super(bffConfig); + this.keycloakService = keycloakService; + } + + @Override + public Mono> listRoles( + String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(LIST, exchange) + .thenMany(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 new file mode 100644 index 0000000..f2c5653 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/controller/UsersController.java @@ -0,0 +1,143 @@ +/* + * + * Copyright (c) 2022. 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.controller; + +import org.onap.portalng.bff.config.BffConfig; +import org.onap.portalng.bff.openapi.server.api.UsersApi; +import org.onap.portalng.bff.openapi.server.model.CreateUserRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.RoleApiDto; +import org.onap.portalng.bff.openapi.server.model.RoleListResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.UpdateUserPasswordRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.UpdateUserRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.UserListResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.UserResponseApiDto; +import org.onap.portalng.bff.services.KeycloakService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +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; + + @Autowired + public UsersController(BffConfig bffConfig, KeycloakService keycloakService) { + super(bffConfig); + this.keycloakService = keycloakService; + } + + @Override + public Mono> createUser( + Mono requestMono, String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(CREATE, exchange) + .then(requestMono.flatMap(request -> keycloakService.createUser(request, xRequestId))) + .map(ResponseEntity::ok); + } + + @Override + public Mono> getUser( + String userId, String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(GET, exchange) + .then(keycloakService.getUser(userId, xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> updateUser( + String userId, + Mono requestMono, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(UPDATE, exchange) + .then(requestMono) + .flatMap(request -> keycloakService.updateUser(userId, request, xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> deleteUser( + String userId, String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(DELETE, exchange) + .then(keycloakService.deleteUser(userId, xRequestId)) + .thenReturn(ResponseEntity.noContent().build()); + } + + @Override + public Mono> listUsers( + Integer page, Integer pageSize, String xRequestId, ServerWebExchange exchange) { + + return checkRoleAccess(LIST, exchange) + .then(keycloakService.listUsers(page, pageSize, xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> updatePassword( + String userId, + Mono requestMono, + String xRequestId, + ServerWebExchange exchange) { + return checkRoleAccess(UPDATE_PASSWORD, exchange) + .then(requestMono) + .flatMap(request -> keycloakService.updateUserPassword(userId, request)) + .thenReturn(ResponseEntity.noContent().build()); + } + + @Override + public Mono> listAvailableRoles( + String userId, String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(LIST_AVAILABLE_ROLES, exchange) + .then(keycloakService.getAvailableRoles(userId, xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> listAssignedRoles( + String userId, String xRequestId, ServerWebExchange exchange) { + return checkRoleAccess(LIST_ROLES, exchange) + .then(keycloakService.getAssignedRoles(userId, xRequestId)) + .map(ResponseEntity::ok); + } + + @Override + public Mono> updateAssignedRoles( + String userId, String xRequestId, Flux rolesFlux, ServerWebExchange exchange) { + return checkRoleAccess(UPDATE_ROLES, exchange) + .then(rolesFlux.collectList()) + .flatMap(roles -> keycloakService.updateAssignedRoles(userId, roles, xRequestId)) + .map(ResponseEntity::ok); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/exceptions/DownstreamApiProblemException.java b/lib/src/main/java/org/onap/portalng/bff/exceptions/DownstreamApiProblemException.java new file mode 100644 index 0000000..0c90473 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/exceptions/DownstreamApiProblemException.java @@ -0,0 +1,65 @@ +/* + * + * Copyright (c) 2022. 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.exceptions; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.net.URI; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.onap.portalng.bff.openapi.server.model.ConstraintViolationApiDto; +import org.zalando.problem.AbstractThrowableProblem; +import org.zalando.problem.Problem; +import org.zalando.problem.Status; +import org.zalando.problem.StatusType; + +/** The default bff exception */ +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString +@JsonIgnoreProperties +public class DownstreamApiProblemException extends AbstractThrowableProblem { + + @Builder.Default private final URI type = Problem.DEFAULT_TYPE; + @Builder.Default private final String title = "Bad gateway error"; + + @JsonIgnore @Builder.Default private final transient StatusType status = Status.BAD_GATEWAY; + + @Builder.Default + private final String detail = "Please find more detail under correlationId: 'TODO'"; + + @Builder.Default private final String downstreamSystem = null; + @Builder.Default private final URI instance = null; + @Builder.Default private final Integer downstreamStatus = null; + @Builder.Default private final String downstreamMessageId = null; + + @JsonIgnore @Builder.Default + private final transient List violations = null; +} diff --git a/lib/src/main/java/org/onap/portalng/bff/mappers/ActionsMapper.java b/lib/src/main/java/org/onap/portalng/bff/mappers/ActionsMapper.java new file mode 100644 index 0000000..3f0ebdf --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/mappers/ActionsMapper.java @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2022. 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.mappers; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.onap.portalng.bff.config.MapperSpringConfig; +import org.onap.portalng.bff.openapi.client_portal_history.model.ActionsListResponsePortalHistoryDto; +import org.onap.portalng.bff.openapi.server.model.ActionsListResponseApiDto; +import org.springframework.core.convert.converter.Converter; + +@Mapper(config = MapperSpringConfig.class) +public interface ActionsMapper + extends Converter { + + @Mapping(source = "actionsList", target = "items") + ActionsListResponseApiDto convert(ActionsListResponsePortalHistoryDto source); +} diff --git a/lib/src/main/java/org/onap/portalng/bff/mappers/CredentialMapper.java b/lib/src/main/java/org/onap/portalng/bff/mappers/CredentialMapper.java new file mode 100644 index 0000000..b3c7f05 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/mappers/CredentialMapper.java @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2022. 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.mappers; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.onap.portalng.bff.config.MapperSpringConfig; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.CredentialKeycloakDto; +import org.onap.portalng.bff.openapi.server.model.UpdateUserPasswordRequestApiDto; + +@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface CredentialMapper { + CredentialKeycloakDto convert(UpdateUserPasswordRequestApiDto source); +} diff --git a/lib/src/main/java/org/onap/portalng/bff/mappers/PreferencesMapper.java b/lib/src/main/java/org/onap/portalng/bff/mappers/PreferencesMapper.java new file mode 100644 index 0000000..6e615c3 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/mappers/PreferencesMapper.java @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2022. 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.mappers; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.onap.portalng.bff.config.MapperSpringConfig; +import org.onap.portalng.bff.openapi.client_portal_prefs.model.PreferencesPortalPrefsDto; +import org.onap.portalng.bff.openapi.server.model.PreferencesResponseApiDto; +import org.springframework.core.convert.converter.Converter; + +@Mapper(config = MapperSpringConfig.class) +public interface PreferencesMapper + extends Converter { + + @Mapping(source = "properties", target = "properties") + PreferencesResponseApiDto convert(PreferencesPortalPrefsDto source); +} diff --git a/lib/src/main/java/org/onap/portalng/bff/mappers/RolesMapper.java b/lib/src/main/java/org/onap/portalng/bff/mappers/RolesMapper.java new file mode 100644 index 0000000..6e14d7e --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/mappers/RolesMapper.java @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2022. 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.mappers; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.onap.portalng.bff.config.MapperSpringConfig; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.RoleKeycloakDto; +import org.onap.portalng.bff.openapi.server.model.RoleApiDto; +import org.springframework.core.convert.converter.Converter; + +@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface RolesMapper extends Converter { + RoleApiDto convert(RoleKeycloakDto source); + + RoleKeycloakDto convert(RoleApiDto source); +} diff --git a/lib/src/main/java/org/onap/portalng/bff/mappers/UsersMapper.java b/lib/src/main/java/org/onap/portalng/bff/mappers/UsersMapper.java new file mode 100644 index 0000000..f494c5d --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/mappers/UsersMapper.java @@ -0,0 +1,48 @@ +/* + * + * Copyright (c) 2022. 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.mappers; + +import java.util.List; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.onap.portalng.bff.config.MapperSpringConfig; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.RequiredActionsKeycloakDto; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.UserKeycloakDto; +import org.onap.portalng.bff.openapi.server.model.CreateUserRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.UpdateUserRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.UserResponseApiDto; +import org.springframework.core.convert.converter.Converter; + +@Mapper(config = MapperSpringConfig.class, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UsersMapper extends Converter { + + UserResponseApiDto convert(UserKeycloakDto source); + + @Mapping(source = "roles", target = "realmRoles") + UserResponseApiDto convert(UserKeycloakDto source, List roles); + + @Mapping(source = "actions", target = "requiredActions") + UserKeycloakDto convert(CreateUserRequestApiDto source, List actions); + + UserKeycloakDto convert(UpdateUserRequestApiDto source); +} diff --git a/lib/src/main/java/org/onap/portalng/bff/services/ActionService.java b/lib/src/main/java/org/onap/portalng/bff/services/ActionService.java new file mode 100644 index 0000000..17afa39 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/services/ActionService.java @@ -0,0 +1,125 @@ +/* + * + * Copyright (c) 2022. 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.services; + +import lombok.RequiredArgsConstructor; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.client_portal_history.api.ActionsApi; +import org.onap.portalng.bff.openapi.client_portal_history.model.CreateActionRequestPortalHistoryDto; +import org.onap.portalng.bff.openapi.server.model.ActionsListResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.ActionsResponseApiDto; +import org.onap.portalng.bff.openapi.server.model.CreateActionRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.ProblemApiDto; +import org.onap.portalng.bff.utils.Logger; +import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@RequiredArgsConstructor +@Service +public class ActionService { + private final ActionsApi actionsApi; + private final ConfigurableConversionService conversionService; + + public Mono createAction( + String userId, String xRequestId, CreateActionRequestApiDto createActionRequestApiDto) { + // First map from server API model to client API model + CreateActionRequestPortalHistoryDto createActionRequestPortalHistoryDto = + new CreateActionRequestPortalHistoryDto(); + createActionRequestPortalHistoryDto.setUserId(createActionRequestApiDto.getUserId()); + createActionRequestPortalHistoryDto.setAction(createActionRequestApiDto.getAction()); + createActionRequestPortalHistoryDto.setActionCreatedAt( + createActionRequestApiDto.getActionCreatedAt()); + + return actionsApi + .createAction(userId, xRequestId, createActionRequestPortalHistoryDto) + .map( + action -> + new ActionsResponseApiDto() + .action(action.getAction()) + .actionCreatedAt(action.getActionCreatedAt())) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Create actions failed for userId", + userId, + ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); + return Mono.error(ex); + }); + } + + public Mono getActions( + String userId, String xRequestId, Integer page, Integer pageSize, Integer showLastHours) { + + return actionsApi + .getActions(userId, xRequestId, page, pageSize, showLastHours) + .map(actions -> conversionService.convert(actions, ActionsListResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Get actions failed for userId", + userId, + ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); + return Mono.error(ex); + }); + } + + public Mono listActions( + String xRequestId, Integer page, Integer pageSize, Integer showLast) { + return actionsApi + .listActions(xRequestId, page, pageSize, showLast) + .map( + responseEntity -> + conversionService.convert(responseEntity, ActionsListResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "List actions failed", + null, + ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); + return Mono.error(ex); + }); + } + + public Mono deleteActions(String userId, String xRequestId, Integer deleteAfterHours) { + return actionsApi + .deleteActions(userId, xRequestId, deleteAfterHours) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Get actions failed for userId because actions cannot be deleted after " + + deleteAfterHours + + " hours", + userId, + ProblemApiDto.DownstreamSystemEnum.PORTAL_HISTORY.toString()); + return Mono.error(ex); + }); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/services/KeycloakService.java b/lib/src/main/java/org/onap/portalng/bff/services/KeycloakService.java new file mode 100644 index 0000000..b902128 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/services/KeycloakService.java @@ -0,0 +1,385 @@ +/* + * + * Copyright (c) 2022. 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.services; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.mappers.CredentialMapper; +import org.onap.portalng.bff.mappers.RolesMapper; +import org.onap.portalng.bff.mappers.UsersMapper; +import org.onap.portalng.bff.openapi.client_portal_keycloak.api.KeycloakApi; +import org.onap.portalng.bff.openapi.client_portal_keycloak.model.RequiredActionsKeycloakDto; +import org.onap.portalng.bff.openapi.server.model.*; +import org.onap.portalng.bff.utils.Logger; +import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.zalando.problem.Status; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuples; + +@Slf4j +@RequiredArgsConstructor +@Service +public class KeycloakService { + private final KeycloakApi keycloakApi; + private final ConfigurableConversionService conversionService; + private final RolesMapper rolesMapper; + private final UsersMapper usersMapper; + private final CredentialMapper credentialMapper; + + public Mono createUser(CreateUserRequestApiDto request, String xRequestId) { + log.debug("Create user in keycloak. request=`{}`", request); + + final List rolesToBeAssigned = + request.getRoles().isEmpty() ? Collections.emptyList() : request.getRoles(); + return listRoles(xRequestId) + .collectList() + .flatMap( + realmRoles -> { + final List absentRoles = + rolesToBeAssigned.stream().filter(role -> !realmRoles.contains(role)).toList(); + if (!absentRoles.isEmpty()) { + return Mono.error( + DownstreamApiProblemException.builder() + .status(Status.NOT_FOUND) + .detail( + String.format( + "Roles not found in the realm: %s", + absentRoles.stream().map(RoleApiDto::getName).toList())) + .downstreamSystem(ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()) + .title(HttpStatus.NOT_FOUND.toString()) + .build()); + } + return Mono.just(rolesToBeAssigned); + }) + .flatMap(roles -> createUserWithRoles(request, xRequestId, roles)); + } + + private Mono createUserWithRoles( + CreateUserRequestApiDto request, String xRequestId, List roles) { + return keycloakApi + .createUserWithHttpInfo( + usersMapper.convert(request, List.of(RequiredActionsKeycloakDto.UPDATE_PASSWORD))) + .map(responseEntit -> responseEntit.getHeaders().getLocation()) + .map(URI::toString) + .map(location -> location.substring(location.lastIndexOf("/") + 1)) + .flatMap(userId -> !roles.isEmpty() ? assignRoles(userId, roles) : Mono.just(userId)) + .flatMap( + userId -> sendActionEmail(userId, List.of(RequiredActionsKeycloakDto.UPDATE_PASSWORD))) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Create user failed at sending update-password email for userName", + request.getUsername(), + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }) + .flatMap((String userId1) -> getUser(userId1, xRequestId)); + } + + public Mono getUser(String userId, String xRequestId) { + log.debug("Get user from keycloak. userId=`{}`", userId); + return Mono.zip( + keycloakApi + .getUser(userId) + .map(user -> conversionService.convert(user, UserResponseApiDto.class)), + getAssignedRoles(userId, xRequestId)) + .map( + tuple -> + new UserResponseApiDto() + .username(tuple.getT1().getUsername()) + .email(tuple.getT1().getEmail()) + .enabled(tuple.getT1().getEnabled()) + .id(tuple.getT1().getId()) + .firstName(tuple.getT1().getFirstName()) + .lastName(tuple.getT1().getLastName()) + .realmRoles( + tuple.getT2().getItems().stream().map(RoleApiDto::getName).toList())) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Failed to get user", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Mono listUsers(int page, int pageSize, String xRequestId) { + log.debug("Get users from keycloak. page=`{}`, pageSize=`{}`", page, pageSize); + final int first = (page - 1) * pageSize; + + var userIdRoleMap = + listRoles(xRequestId) + .flatMap( + role -> + listUsersByRole(role.getName(), xRequestId) + .map(userResponse -> Tuples.of(role.getName(), userResponse))) + .collectList() + .map( + tupleList -> + tupleList.stream() + .collect( + Collectors.groupingBy( + tuple -> tuple.getT2().getId(), + Collectors.mapping(tuple -> tuple.getT1(), Collectors.toList())))); + + return Mono.zip( + keycloakApi.getUsersCount(null, null, null, null, null, null, null), + keycloakApi + .getUsers( + null, null, null, null, null, null, null, null, first, pageSize, null, null, + null, null) + .collectList(), + userIdRoleMap) + .map( + tuple -> { + final UserListResponseApiDto result = new UserListResponseApiDto(); + Map> userRoleMap = tuple.getT3(); + result.setTotalCount(tuple.getT1()); + var roleList = + tuple.getT2().stream() + .map( + user -> + usersMapper.convert( + user, + Optional.ofNullable(userRoleMap.get(user.getId())) + .orElse(Collections.emptyList()))) + .toList(); + result.setItems(roleList); + return result; + }) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "List users failed", + null, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Mono updateUser(String userId, UpdateUserRequestApiDto request, String xRequestId) { + log.debug("Update user in keycloak. userId=`{}`, request=`{}`", userId, request); + return keycloakApi + .updateUser(userId, usersMapper.convert(request)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Failed to update user", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Mono updateUserPassword(String userId, UpdateUserPasswordRequestApiDto request) { + log.debug( + "Update password for user in keycloak. userId=`{}`, temporary=`{}`", + userId, + request.getTemporary()); + + return keycloakApi.resetUserPassword(userId, credentialMapper.convert(request)); + } + + public Mono deleteUser(String userId, String xRequestId) { + log.debug("Delete user from keycloak. userId=`{}`", userId); + + return keycloakApi + .deleteUser(userId) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Failed to delete user", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Mono assignRoles(String userId, List roles) { + log.debug( + "Assign roles to user in keycloak. userId=`{}`, roleIds=`{}`", + userId, + roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); + + return keycloakApi + .addRealmRoleMappingsToUser(userId, roles.stream().map(rolesMapper::convert).toList()) + .thenReturn(userId); + } + + public Mono updateAssignedRoles( + String userId, List roles, String xRequestId) { + log.debug( + "Update assigned roles for user in keycloak. userId=`{}`, roleIds=`{}`", + userId, + roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); + + return getAssignedRoles(userId, xRequestId) + .map(response -> response.getItems()) + .flatMap( + assignedRoles -> { + if (assignedRoles.isEmpty()) { + return Mono.empty(); + } + return unassignRoles(userId, assignedRoles); + }) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Update assigned roles failed for userId", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }) + .then( + Mono.defer( + () -> { + if (roles.isEmpty()) { + return Mono.empty(); + } + return assignRoles(userId, roles); + })) + .then(Mono.defer(() -> getAssignedRoles(userId, xRequestId))); + } + + public Mono unassignRoles(String userId, List roles) { + log.debug( + "Unassign roles from user in keycloak. userId=`{}`, roleIds=`{}`", + userId, + roles.stream().map(RoleApiDto::getId).collect(Collectors.joining(", "))); + + return keycloakApi.deleteRealmRoleMappingsByUserId( + userId, roles.stream().map(rolesMapper::convert).toList()); + } + + public Mono sendActionEmail( + String userId, java.util.List requiredActions) { + log.debug( + "Sending update actions email to user in keycloak. userId=`{}`, actions=`{}`", + userId, + requiredActions); + return keycloakApi + .executeActionsEmail(userId, null, null, null, requiredActions) + .thenReturn(userId); + } + + public Flux listRoles(String xRequestId) { + return keycloakApi + .getRoles(null, null, null, null) + .map(role -> conversionService.convert(role, RoleApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog(xRequestId, "Get realm roles failed for ID", xRequestId, "KEYCLOAK"); + return Mono.error(ex); + }); + } + + public Mono getAssignedRoles(String userId, String xRequestId) { + log.debug("Get assigned roles from keycloak. userId=`{}`", userId); + + return keycloakApi + .getRealmRoleMappingsByUserId(userId) + .map(role -> conversionService.convert(role, RoleApiDto.class)) + .collectList() + .map( + items -> { + final RoleListResponseApiDto result = new RoleListResponseApiDto(); + result.setTotalCount(items.size()); // keycloak does not support pagination for roles + result.setItems(items); + return result; + }) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Get assigned roles failed for userId", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Mono getAvailableRoles(String userId, String xRequestId) { + log.debug("Get available roles from keycloak. userId=`{}`", userId); + + return keycloakApi + .getAvailableRealmRoleMappingsByUserId(userId) + .map(role -> conversionService.convert(role, RoleApiDto.class)) + .collectList() + .map( + items -> { + final RoleListResponseApiDto result = new RoleListResponseApiDto(); + result.setTotalCount(items.size()); // keycloak does not support pagination for roles + result.setItems(items); + + return result; + }) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Get available roles failed for userId", + userId, + ProblemApiDto.DownstreamSystemEnum.KEYCLOAK.toString()); + return Mono.error(ex); + }); + } + + public Flux listUsersByRole(String roleName, String xRequestId) { + return keycloakApi + .getUsersByRole(roleName, null, null) + .log() + .map(user -> conversionService.convert(user, UserResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, "Get users by realm role failed for ID", xRequestId, "KEYCLOAK"); + return Mono.error(ex); + }); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/services/PreferencesService.java b/lib/src/main/java/org/onap/portalng/bff/services/PreferencesService.java new file mode 100644 index 0000000..af62d28 --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/services/PreferencesService.java @@ -0,0 +1,91 @@ +/* + * + * Copyright (c) 2022. 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.services; + +import lombok.RequiredArgsConstructor; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.client_portal_prefs.api.PreferencesApi; +import org.onap.portalng.bff.openapi.client_portal_prefs.model.PreferencesPortalPrefsDto; +import org.onap.portalng.bff.openapi.server.model.CreatePreferencesRequestApiDto; +import org.onap.portalng.bff.openapi.server.model.PreferencesResponseApiDto; +import org.onap.portalng.bff.utils.Logger; +import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@RequiredArgsConstructor +@Service +public class PreferencesService { + + private static final String PREFERENCES_APPLICATION_NAME = "PORTAL_PREFS"; + + private final PreferencesApi preferencesApi; + private final ConfigurableConversionService conversionService; + + public Mono createPreferences( + String xRequestId, CreatePreferencesRequestApiDto request) { + PreferencesPortalPrefsDto preferencesPortalPrefsDto = new PreferencesPortalPrefsDto(); + preferencesPortalPrefsDto.setProperties(request.getProperties()); + return preferencesApi + .savePreferences(xRequestId, preferencesPortalPrefsDto) + .map(resp -> conversionService.convert(resp, PreferencesResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, "Preference raise error", xRequestId, PREFERENCES_APPLICATION_NAME); + return Mono.error(ex); + }); + } + + public Mono updatePreferences( + String xRequestId, CreatePreferencesRequestApiDto request) { + PreferencesPortalPrefsDto preferencesPortalPrefsDto = new PreferencesPortalPrefsDto(); + preferencesPortalPrefsDto.setProperties(request.getProperties()); + return preferencesApi + .updatePreferences(xRequestId, preferencesPortalPrefsDto) + .map(resp -> conversionService.convert(resp, PreferencesResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, "Preference raise error", xRequestId, PREFERENCES_APPLICATION_NAME); + return Mono.error(ex); + }); + } + + public Mono getPreferences(String xRequestId) { + return preferencesApi + .getPreferences(xRequestId) + .map(preferences -> conversionService.convert(preferences, PreferencesResponseApiDto.class)) + .onErrorResume( + DownstreamApiProblemException.class, + ex -> { + Logger.errorLog( + xRequestId, + "Get preferences failed for ID", + xRequestId, + PREFERENCES_APPLICATION_NAME); + return Mono.error(ex); + }); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/utils/ErrorHandler.java b/lib/src/main/java/org/onap/portalng/bff/utils/ErrorHandler.java new file mode 100644 index 0000000..0b198ac --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/utils/ErrorHandler.java @@ -0,0 +1,65 @@ +/* + * + * Copyright (c) 2022. 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.utils; + +import java.util.List; +import java.util.Objects; +import org.onap.portalng.bff.exceptions.DownstreamApiProblemException; +import org.onap.portalng.bff.openapi.server.model.ProblemApiDto; +import org.springframework.http.HttpStatus; + +public class ErrorHandler { + /** + * Not meant to be instantiated. To prevent Java from adding an implicit public constructor to + * every class which does not define at least one explicitly. + */ + private ErrorHandler() {} + + public static String mapVariablesToDetails(List variables, String details) { + int i = 0; + for (String variable : variables) { + i++; + details = details.replace("%" + i, variable); + } + return details; + } + + public static DownstreamApiProblemException getDownstreamApiProblemException( + HttpStatus httpStatus, + List variables, + String text, + String messageId, + ProblemApiDto.DownstreamSystemEnum downStreamSystem) { + String errorDetail = + variables != null && text != null + ? ErrorHandler.mapVariablesToDetails(variables, text) + : null; + + return DownstreamApiProblemException.builder() + .title(httpStatus.toString()) + .detail(errorDetail) + .downstreamMessageId(Objects.requireNonNullElse(messageId, "not set by downstream system")) + .downstreamSystem(downStreamSystem.toString()) + .downstreamStatus(httpStatus.value()) + .build(); + } +} diff --git a/lib/src/main/java/org/onap/portalng/bff/utils/Logger.java b/lib/src/main/java/org/onap/portalng/bff/utils/Logger.java new file mode 100644 index 0000000..3c431fb --- /dev/null +++ b/lib/src/main/java/org/onap/portalng/bff/utils/Logger.java @@ -0,0 +1,60 @@ +/* + * + * Copyright (c) 2022. 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.utils; + +import java.net.URI; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; + +@Slf4j +public class Logger { + + /** + * Not meant to be instantiated. To prevent Java from adding an implicit public constructor to + * every class which does not define at least one explicitly. + */ + private Logger() {} + + public static void requestLog(String xRequestId, HttpMethod methode, URI path) { + log.info("bff - request - X-Request-Id {} {} {}", xRequestId, methode, path); + } + + public static void responseLog(String xRequestId, HttpStatus code) { + log.info("bff - response - X-Request-Id {} {}", xRequestId, code); + } + + public static void errorLog(String xRequestId, String msg, String id, String app) { + log.info("bff - error - X-Request-Id {} {} {} not found in {}", xRequestId, msg, id, app); + } + + public static void errorLog( + String xRequestId, String msg, String id, String app, String errorDetails) { + log.info( + "bff - error - X-Request-Id {} {} {} not found in {} error message: {}", + xRequestId, + msg, + id, + app, + errorDetails); + } +} -- cgit 1.2.3-korg