From 17073cb7d64f8e97aca3d77d7ff081b7f6196d2e Mon Sep 17 00:00:00 2001 From: Rishabh-egov Date: Mon, 30 Sep 2024 18:32:48 +0530 Subject: [PATCH 1/2] Refactored the codebase Handling form-data content type separately fixed issues with mixed mode endpoint --- .../gateway/constants/GatewayConstants.java | 1 + .../gateway/filters/pre/AuthFilter.java | 7 +- .../filters/pre/AuthPreCheckFilter.java | 147 +++++++++++------- .../filters/pre/CorrelationIdFilter.java | 43 ++--- .../filters/pre/RbacPreCheckFilter.java | 7 +- .../pre/helpers/AuthCheckFilterHelper.java | 26 +++- .../pre/helpers/AuthPreCheckFilterHelper.java | 78 ++-------- .../helpers/CorrelationIdFilterHelper.java | 115 ++------------ .../pre/helpers/RbacPreCheckFilterHelper.java | 9 +- .../example/gateway/utils/CommonUtils.java | 107 ++++++++++++- .../com/example/gateway/utils/UserUtils.java | 27 ++-- 11 files changed, 284 insertions(+), 283 deletions(-) diff --git a/core-services/gateway/src/main/java/com/example/gateway/constants/GatewayConstants.java b/core-services/gateway/src/main/java/com/example/gateway/constants/GatewayConstants.java index ba6bc9f6329..4a29d508e2e 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/constants/GatewayConstants.java +++ b/core-services/gateway/src/main/java/com/example/gateway/constants/GatewayConstants.java @@ -3,6 +3,7 @@ public class GatewayConstants { public static final String EMPTY_STRING = ""; + public static final String AUTH_TOKEN = "auth-token"; public static final String JSON_TYPE = "json"; public static final String X_WWW_FORM_URLENCODED_TYPE = "application/x-www-form-urlencoded"; public static final String FORM_DATA = "multipart/form-data"; diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java index 551c4cb43d9..570f85d7631 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java @@ -30,15 +30,14 @@ public AuthFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - Boolean doAuth = exchange.getAttribute(AUTH_BOOLEAN_FLAG_NAME); - if(doAuth) { + // TODO: handle auth for form-data content type (filestore) + if (Boolean.TRUE.equals(doAuth)) { return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() .setRewriteFunction(Map.class, Map.class, authCheckFilterHelper)) .filter(exchange, chain); - } - else { + } else { return chain.filter(exchange); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java index 3c06c4fa08a..623a1bd7627 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java @@ -2,6 +2,7 @@ import com.example.gateway.config.ApplicationProperties; import com.example.gateway.filters.pre.helpers.AuthPreCheckFilterHelper; +import com.example.gateway.utils.CommonUtils; import com.example.gateway.utils.UserUtils; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @@ -16,17 +17,24 @@ import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.core.Ordered; +import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; import java.util.List; import java.util.Map; import static com.example.gateway.constants.GatewayConstants.*; import static com.example.gateway.constants.GatewayConstants.OPEN_ENDPOINT_MESSAGE; -import static com.example.gateway.filters.pre.helpers.AuthPreCheckFilterHelper.ROUTING_TO_ANONYMOUS_ENDPOINT_MESSAGE; +import static com.example.gateway.filters.pre.helpers.AuthPreCheckFilterHelper.*; @Slf4j @Component @@ -39,96 +47,115 @@ public class AuthPreCheckFilter implements GlobalFilter, Ordered { private ApplicationProperties applicationProperties; private UserUtils userUtils; - + private CommonUtils commonUtils; private MultiStateInstanceUtil centralInstanceUtil; - public AuthPreCheckFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, AuthPreCheckFilterHelper authPreCheckFilterHelper, ApplicationProperties applicationProperties, UserUtils userUtils, MultiStateInstanceUtil centralInstanceUtil) { + public AuthPreCheckFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, AuthPreCheckFilterHelper authPreCheckFilterHelper, ApplicationProperties applicationProperties, UserUtils userUtils, CommonUtils commonUtils, MultiStateInstanceUtil centralInstanceUtil) { this.modifyRequestBodyFilter = modifyRequestBodyFilter; this.authPreCheckFilterHelper = authPreCheckFilterHelper; this.applicationProperties = applicationProperties; this.userUtils = userUtils; + this.commonUtils = commonUtils; this.centralInstanceUtil = centralInstanceUtil; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - String authToken; + String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); String endPointPath = exchange.getRequest().getPath().value(); if (applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath)) { exchange.getAttributes().put(AUTH_BOOLEAN_FLAG_NAME, Boolean.FALSE); log.info(OPEN_ENDPOINT_MESSAGE, endPointPath); return chain.filter(exchange); + + } else if (commonUtils.isFormContentType(contentType)) { + return handleAuthPreCheck(exchange, chain); + } else { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, authPreCheckFilterHelper)).filter(exchange, chain); + return modifyRequestBodyFilter + .apply(new ModifyRequestBodyGatewayFilterFactory.Config() + .setRewriteFunction(Map.class, Map.class, authPreCheckFilterHelper)) + .filter(exchange, chain); } } - private void setAnonymousUser(ServerWebExchange exchange) { - ServerHttpRequest request = exchange.getRequest(); - String CorrelationId = exchange.getAttributes().get(CORRELATION_ID_KEY).toString(); - String tenantId = getStateLevelTenantForHost(request); - User systemUser = userUtils.fetchSystemUser(tenantId, CorrelationId); - modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, new RewriteFunction() { - @Override - public Publisher apply(ServerWebExchange serverWebExchange, Map body) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); - requestInfo.setUserInfo(systemUser); - body.put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, requestInfo); - return Mono.just(body); - } catch (Exception ex) { - log.error("An error occured while transforming the request body to set Anonymous User {}", ex); - - // Throw a custom exception - throw new CustomException("AUTHENTICATION_ERROR", ex.getMessage()); - } - } - })); + // will handle for selected content types + public Mono handleAuthPreCheck(ServerWebExchange exchange, GatewayFilterChain chain) { - } + String authToken = null; + String endPointPath = exchange.getRequest().getPath().value(); - /** - * method to fetch state level tenant-id based on whether the server is a - * multi-state instance or single-state instance - * - * @param request - * @return - */ - private String getStateLevelTenantForHost(ServerHttpRequest request) { - String tenantId = ""; - if (centralInstanceUtil.getIsEnvironmentCentralInstance()) { - String requestURL = getRequestURL(request); - String host = requestURL.replace(request.getURI().getPath(), "").replace("https://", "").replace("http://", ""); - tenantId = userUtils.getStateLevelTenantMap().get(host); - } else { - tenantId = userUtils.getStateLevelTenant(); + if (!ObjectUtils.isEmpty(exchange.getRequest().getHeaders().get(AUTH_TOKEN))) { + authToken = exchange.getRequest().getHeaders().get(AUTH_TOKEN).get(0); } - return tenantId; - } - public String getRequestURL(ServerHttpRequest request) { + if (ObjectUtils.isEmpty(authToken)) { + if (applicationProperties.getMixedModeEndpointsWhitelist().contains(endPointPath)) { + log.info(ROUTING_TO_ANONYMOUS_ENDPOINT_MESSAGE, endPointPath); + exchange.getAttributes().put(AUTH_BOOLEAN_FLAG_NAME, Boolean.FALSE); + setAnonymousUser(exchange); + } else { + + // TODO:Handle in RBAC + log.info(ROUTING_TO_PROTECTED_ENDPOINT_RESTRICTED_MESSAGE, endPointPath); + CustomException customException = new CustomException(UNAUTHORIZED_USER_MESSAGE, UNAUTHORIZED_USER_MESSAGE); + customException.setCode(HttpStatus.UNAUTHORIZED.toString()); + throw customException; + } + } else { + log.info(PROCEED_ROUTING_MESSAGE, endPointPath); + exchange.getAttributes().put(AUTH_BOOLEAN_FLAG_NAME, Boolean.TRUE); - // Manually construct the full request URL - String scheme = request.getURI().getScheme(); // e.g., "http" or "https" - String host = request.getURI().getHost(); // e.g., "example.com" - int port = request.getURI().getPort(); // e.g., 80 or 443 (can be -1 if default port is used) - String path = request.getURI().getPath(); // e.g., "/api/users" + } - // Construct the full URL - StringBuilder requestURL = new StringBuilder(scheme).append("://").append(host); + return chain.filter(exchange); + } - // Add the port if it's not the default (80 for HTTP, 443 for HTTPS) - if (port != -1) { - requestURL.append(":").append(port); - } + public void setAnonymousUser(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + String CorrelationId = exchange.getAttributes().get(CORRELATION_ID_KEY).toString(); + String tenantId = commonUtils.getStateLevelTenantForHost(request); + User systemUser = userUtils.fetchSystemUser(tenantId, CorrelationId); + try { + ObjectMapper objectMapper = new ObjectMapper(); - // Append the request path - requestURL.append(path); + // Check for PascalCase first + String requestInfoBase64 = null; + if (exchange.getRequest().getQueryParams().containsKey(REQUEST_INFO_FIELD_NAME_PASCAL_CASE)) { + requestInfoBase64 = exchange.getRequest().getQueryParams().getFirst(REQUEST_INFO_FIELD_NAME_PASCAL_CASE); + } + // Check for camelCase if PascalCase is not found + else if (exchange.getRequest().getQueryParams().containsKey(REQUEST_INFO_FIELD_NAME_CAMEL_CASE)) { + requestInfoBase64 = exchange.getRequest().getQueryParams().getFirst(REQUEST_INFO_FIELD_NAME_CAMEL_CASE); + } - return requestURL.toString(); + // TODO: Handle requestInfo not send + // TODO: Modify the request to send the updated + if (requestInfoBase64 != null) { + byte[] decodedBytes = Base64.getDecoder().decode(requestInfoBase64); + String requestInfoJson = new String(decodedBytes, StandardCharsets.UTF_8); + RequestInfo requestInfo = objectMapper.readValue(requestInfoJson, RequestInfo.class); + requestInfo.setUserInfo(systemUser); + String updatedRequestInfoJson = objectMapper.writeValueAsString(requestInfo); + String updatedRequestInfoBase64 = Base64.getEncoder().encodeToString(updatedRequestInfoJson.getBytes(StandardCharsets.UTF_8)); +/* + MultiValueMap originalQueryParams = exchange.getRequest().getQueryParams(); + MultiValueMap modifiableQueryParams = new LinkedMultiValueMap<>(originalQueryParams); + + if (exchange.getRequest().getPath().toString().contains("filestore")) { + exchange.getRequest().getQueryParams().put(REQUEST_INFO_FIELD_NAME_CAMEL_CASE, Collections.singletonList(updatedRequestInfoBase64)); + } else { + exchange.getRequest().getQueryParams().put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, Collections.singletonList(updatedRequestInfoBase64)); + } +*/ + } + } catch (Exception ex) { + log.error("Failed transforming the request body to set Anonymous User in mixed mode endpoints {}", ex); + // Throw a custom exception + throw new CustomException("AUTHENTICATION_ERROR", ex.getMessage()); + } } @Override diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java index daf7dfc0023..59feeb7ced1 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java @@ -3,18 +3,24 @@ import com.example.gateway.config.ApplicationProperties; import com.example.gateway.filters.pre.helpers.CorrIdFormDataFilterHelper; import com.example.gateway.filters.pre.helpers.CorrelationIdFilterHelper; +import com.example.gateway.utils.CommonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpHeaders; +import org.egov.common.utils.MultiStateInstanceUtil; +import org.egov.tracer.model.CustomException; +import org.slf4j.MDC; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; -import java.util.Map; +import java.util.*; import static com.example.gateway.constants.GatewayConstants.*; @@ -23,41 +29,42 @@ public class CorrelationIdFilter implements GlobalFilter, Ordered { private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; - private CorrelationIdFilterHelper correlationIdFilterHelper; - - private CorrIdFormDataFilterHelper correlationIdFormDataFilterHelper; - private ApplicationProperties applicationProperties; + private final CommonUtils commonUtils; + public CorrelationIdFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilter, CorrelationIdFilterHelper correlationIdFilterHelper, - CorrIdFormDataFilterHelper correlationIdFormDataFilterHelper, ApplicationProperties applicationProperties) { + ApplicationProperties applicationProperties, CommonUtils commonUtils) { this.modifyRequestBodyFilter = modifyRequestBodyGatewayFilter; this.correlationIdFilterHelper = correlationIdFilterHelper; - this.correlationIdFormDataFilterHelper = correlationIdFormDataFilterHelper; - this.applicationProperties = applicationProperties; + this.commonUtils = commonUtils; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); - String endPointPath = exchange.getRequest().getPath().value(); + String requestURI = exchange.getRequest().getPath().value(); + String correlationId = UUID.randomUUID().toString(); + + boolean isOpenRequest = applicationProperties.getOpenEndpointsWhitelist().contains(requestURI); + boolean isMixModeRequest = applicationProperties.getMixedModeEndpointsWhitelist().contains(requestURI); + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); - if(endPointPath.contains("/filestore")){ + if (isGetRequest || commonUtils.isFormContentType(contentType)) { + commonUtils.handleCentralInstanceLogic(exchange, requestURI, isOpenRequest, isMixModeRequest, null); + MDC.put(CORRELATION_ID_KEY, correlationId); + exchange.getAttributes().put(CORRELATION_ID_KEY, correlationId); + log.debug(RECEIVED_REQUEST_MESSAGE, requestURI); return chain.filter(exchange); - } - else if (contentType != null && (contentType.contains("multipart/form-data") || contentType.contains("application/x-www-form-urlencoded"))) { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(MultiValueMap.class, MultiValueMap.class, correlationIdFormDataFilterHelper)) - .filter(exchange, chain); - } - else { + + } else { return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() .setRewriteFunction(Map.class, Map.class, correlationIdFilterHelper)) - .filter(exchange, chain); + .filter(exchange, chain); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java index ed33de79182..fe975a9bb38 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java @@ -40,14 +40,13 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String endPointPath = exchange.getRequest().getPath().value(); - if(applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath) || applicationProperties.getMixedModeEndpointsWhitelist().contains(endPointPath)){ + if (applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath) || applicationProperties.getMixedModeEndpointsWhitelist().contains(endPointPath)) { exchange.getAttributes().put(RBAC_BOOLEAN_FLAG_NAME, false); log.info(SKIP_RBAC, endPointPath); - } - else { + } else { exchange.getAttributes().put(RBAC_BOOLEAN_FLAG_NAME, true); } - return chain.filter(exchange); + return chain.filter(exchange); } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthCheckFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthCheckFilterHelper.java index 317378187b9..c534005f727 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthCheckFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthCheckFilterHelper.java @@ -9,9 +9,14 @@ import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; + +import java.util.HashMap; import java.util.Map; + +import static com.example.gateway.constants.GatewayConstants.AUTH_TOKEN; import static com.example.gateway.constants.GatewayConstants.REQUEST_INFO_FIELD_NAME_PASCAL_CASE; @Slf4j @@ -28,14 +33,23 @@ public AuthCheckFilterHelper(ObjectMapper objectMapper, UserUtils userUtils) { } @Override - public Publisher apply(ServerWebExchange serverWebExchange, Map body) { + public Publisher apply(ServerWebExchange exchange, Map body) { + + // TODO: Handle cases when body is null but you want to check for authorization try { - RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); - requestInfo.setUserInfo(userUtils.getUser(requestInfo.getAuthToken())); - body.put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, requestInfo); - return Mono.just(body); + if (ObjectUtils.isEmpty(body)) { + String authToken = exchange.getRequest().getHeaders().get(AUTH_TOKEN).get(0); + userUtils.getUser(authToken, exchange); + return Mono.just(new HashMap<>()); + } else { + RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); + requestInfo.setUserInfo(userUtils.getUser(requestInfo.getAuthToken(), exchange)); + body.put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, requestInfo); + return Mono.just(body); + } + } catch (Exception ex) { - log.error("An error occured while transforming the request body in class RequestBodyRewrite. {}", ex); + log.error("An error occurred in Auth check filter", ex); // Throw a custom exception throw new CustomException("AUTHENTICATION_ERROR", ex.getMessage()); diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthPreCheckFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthPreCheckFilterHelper.java index c7a25468ca0..af711845ff3 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthPreCheckFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/AuthPreCheckFilterHelper.java @@ -1,6 +1,7 @@ package com.example.gateway.filters.pre.helpers; import com.example.gateway.config.ApplicationProperties; +import com.example.gateway.utils.CommonUtils; import com.example.gateway.utils.UserUtils; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @@ -19,6 +20,7 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,27 +32,30 @@ public class AuthPreCheckFilterHelper implements RewriteFunction { public static final String AUTH_TOKEN_RETRIEVE_FAILURE_MESSAGE = "Retrieving of auth token failed"; public static final String ROUTING_TO_ANONYMOUS_ENDPOINT_MESSAGE = "Routing to anonymous endpoint: {}"; - public static final String ROUTING_TO_PROTECTED_ENDPOINT_RESTRICTED_MESSAGE = - "Routing to protected endpoint {} restricted - No auth token"; + public static final String ROUTING_TO_PROTECTED_ENDPOINT_RESTRICTED_MESSAGE = "Routing to protected endpoint {} restricted - No auth token"; public static final String UNAUTHORIZED_USER_MESSAGE = "You are not authorized to access this resource"; public static final String PROCEED_ROUTING_MESSAGE = "Routing to an endpoint: {} - auth provided"; private ObjectMapper objectMapper; - private MultiStateInstanceUtil centralInstanceUtil; + private MultiStateInstanceUtil centralInstanceUtil; private ApplicationProperties applicationProperties; private UserUtils userUtils; + private CommonUtils commonUtils; - public AuthPreCheckFilterHelper(ObjectMapper objectMapper, MultiStateInstanceUtil centralInstanceUtil, UserUtils userUtils, ApplicationProperties applicationProperties) { + public AuthPreCheckFilterHelper(ObjectMapper objectMapper, MultiStateInstanceUtil centralInstanceUtil, UserUtils userUtils, ApplicationProperties applicationProperties, CommonUtils commonUtils) { this.centralInstanceUtil = centralInstanceUtil; - this.userUtils=userUtils; + this.userUtils = userUtils; this.objectMapper = objectMapper; this.applicationProperties = applicationProperties; + this.commonUtils = commonUtils; } @Override public Publisher apply(ServerWebExchange exchange, Map body) { - String authToken; + if (ObjectUtils.isEmpty(body)) body = new HashMap<>(); + + String authToken = null; String endPointPath = exchange.getRequest().getPath().value(); if (applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath)) { @@ -60,8 +65,10 @@ public Publisher apply(ServerWebExchange exchange, Map body) { } try { + body.putIfAbsent(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, new RequestInfo()); RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); authToken = requestInfo.getAuthToken(); + } catch (Exception e) { log.error(AUTH_TOKEN_RETRIEVE_FAILURE_MESSAGE, e); throw new CustomException(AUTH_TOKEN_RETRIEVE_FAILURE_MESSAGE, e.getMessage()); @@ -71,7 +78,7 @@ public Publisher apply(ServerWebExchange exchange, Map body) { if (applicationProperties.getMixedModeEndpointsWhitelist().contains(endPointPath)) { log.info(ROUTING_TO_ANONYMOUS_ENDPOINT_MESSAGE, endPointPath); exchange.getAttributes().put(AUTH_BOOLEAN_FLAG_NAME, Boolean.FALSE); - setAnonymousUser(exchange, body); + commonUtils.setAnonymousUser(exchange, body); } else { log.info(ROUTING_TO_PROTECTED_ENDPOINT_RESTRICTED_MESSAGE, endPointPath); CustomException customException = new CustomException(UNAUTHORIZED_USER_MESSAGE, UNAUTHORIZED_USER_MESSAGE); @@ -87,62 +94,5 @@ public Publisher apply(ServerWebExchange exchange, Map body) { return Mono.just(body); } - private void setAnonymousUser(ServerWebExchange exchange, Map body) { - ServerHttpRequest request = exchange.getRequest(); - String CorrelationId = exchange.getAttributes().get(CORRELATION_ID_KEY).toString(); - String tenantId = getStateLevelTenantForHost(request); - User systemUser = userUtils.fetchSystemUser(tenantId, CorrelationId); - try { - ObjectMapper objectMapper = new ObjectMapper(); - RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); - requestInfo.setUserInfo(systemUser); - body.put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, requestInfo); - } catch (Exception ex) { - log.error("An error occured while transforming the request body to set Anonymous User {}", ex); - - // Throw a custom exception - throw new CustomException("AUTHENTICATION_ERROR", ex.getMessage()); - } - } - - /** - * method to fetch state level tenant-id based on whether the server is a - * multi-state instance or single-state instance - * - * @param ctx - * @return - */ - private String getStateLevelTenantForHost(ServerHttpRequest request) { - String tenantId = ""; - if (centralInstanceUtil.getIsEnvironmentCentralInstance()) { - String requestURL = getRequestURL(request); - String host = requestURL.replace(request.getURI().getPath(), "").replace("https://", "").replace("http://", ""); - tenantId = userUtils.getStateLevelTenantMap().get(host); - } else { - tenantId = userUtils.getStateLevelTenant(); - } - return tenantId; - } - - public String getRequestURL(ServerHttpRequest request) { - - // Manually construct the full request URL - String scheme = request.getURI().getScheme(); // e.g., "http" or "https" - String host = request.getURI().getHost(); // e.g., "example.com" - int port = request.getURI().getPort(); // e.g., 80 or 443 (can be -1 if default port is used) - String path = request.getURI().getPath(); // e.g., "/api/users" - - // Construct the full URL - StringBuilder requestURL = new StringBuilder(scheme).append("://").append(host); - // Add the port if it's not the default (80 for HTTP, 443 for HTTPS) - if (port != -1) { - requestURL.append(":").append(port); - } - - // Append the request path - requestURL.append(path); - - return requestURL.toString(); - } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/CorrelationIdFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/CorrelationIdFilterHelper.java index 947915114c8..63bd6978fcb 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/CorrelationIdFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/CorrelationIdFilterHelper.java @@ -2,24 +2,15 @@ import com.example.gateway.config.ApplicationProperties; import com.example.gateway.utils.CommonUtils; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.JsonNodeType; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import org.egov.common.utils.MultiStateInstanceUtil; -import org.egov.tracer.model.CustomException; import org.reactivestreams.Publisher; import org.slf4j.MDC; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; + import java.util.*; import static com.example.gateway.constants.GatewayConstants.*; @@ -29,119 +20,31 @@ public class CorrelationIdFilterHelper implements RewriteFunction { private ApplicationProperties applicationProperties; - - private MultiStateInstanceUtil centralInstanceUtil; - - private ObjectMapper objectMapper; - private CommonUtils commonUtils; - public CorrelationIdFilterHelper(ApplicationProperties applicationProperties, MultiStateInstanceUtil centralInstanceUtil, - ObjectMapper objectMapper, CommonUtils commonUtils) { + public CorrelationIdFilterHelper(ApplicationProperties applicationProperties, CommonUtils commonUtils) { this.applicationProperties = applicationProperties; - this.centralInstanceUtil = centralInstanceUtil; - this.objectMapper = objectMapper; this.commonUtils = commonUtils; } @Override public Publisher apply(ServerWebExchange exchange, Map body) { + if (ObjectUtils.isEmpty(body)) body = new HashMap<>(); + String requestURI = exchange.getRequest().getPath().value(); String requestPath = exchange.getRequest().getPath().toString(); - Boolean isOpenRequest = applicationProperties.getOpenEndpointsWhitelist().contains(requestPath); - Boolean isMixModeRequest = applicationProperties.getMixedModeEndpointsWhitelist().contains(requestPath); - - if (centralInstanceUtil.getIsEnvironmentCentralInstance() && (isOpenRequest || isMixModeRequest) - && !requestURI.equalsIgnoreCase("/user/oauth/token")) { - /* - * Adding tenantid to header for open urls, authorized urls will get ovverrided - * in RBAC filter - */ - Set tenantIds = getTenantIdsFromRequest(exchange.getRequest(), body); - - if (CollectionUtils.isEmpty(tenantIds) && isOpenRequest) { - throw new CustomException("INVALID_TENANT_ID", "No tenantId in the request"); - } - - String tenantId = commonUtils.getLowLevelTenantIdFromSet(tenantIds); - MDC.put(TENANTID_MDC, tenantId); - exchange.getAttributes().put(TENANTID_MDC, tenantId); - - } + boolean isOpenRequest = applicationProperties.getOpenEndpointsWhitelist().contains(requestPath); + boolean isMixModeRequest = applicationProperties.getMixedModeEndpointsWhitelist().contains(requestPath); + // enriches tenantIds in MDC + commonUtils.handleCentralInstanceLogic(exchange, requestURI, isOpenRequest, isMixModeRequest, body); String correlationId = UUID.randomUUID().toString(); MDC.put(CORRELATION_ID_KEY, correlationId); exchange.getAttributes().put(CORRELATION_ID_KEY, correlationId); + log.debug(RECEIVED_REQUEST_MESSAGE, requestURI); - if(ObjectUtils.isEmpty(body)){ - body = new HashMap<>(); - } return Mono.just(body); } - - private Set getTenantIdsFromRequest(ServerHttpRequest request, Map body) throws CustomException { - - Set tenantIds = new HashSet<>(); - - if (CommonUtils.isRequestBodyCompatible(request)) { - - try { - ObjectNode requestBody = objectMapper.convertValue(body, ObjectNode.class); - - if (requestBody.has(REQUEST_INFO_FIELD_NAME_PASCAL_CASE)) - requestBody.remove(REQUEST_INFO_FIELD_NAME_PASCAL_CASE); - - else if (requestBody.has(REQUEST_INFO_FIELD_NAME_CAMEL_CASE)) - requestBody.remove(REQUEST_INFO_FIELD_NAME_CAMEL_CASE); - - List tenants = new LinkedList<>(); - - for (JsonNode node : requestBody.findValues(REQUEST_TENANT_ID_KEY)) { - if (node.getNodeType() == JsonNodeType.ARRAY) { - node.elements().forEachRemaining(n -> tenants.add(n.asText())); - } else if (node.getNodeType() == JsonNodeType.STRING) { - tenants.add(node.asText()); - } - } - - if (!tenants.isEmpty()) { - tenants.forEach(tenant -> { - if (tenant != null && !tenant.equalsIgnoreCase("null")) - tenantIds.add(tenant); - }); - } else { - setTenantIdsFromQueryParams(request.getQueryParams(), tenantIds); - } - - } catch (Exception e) { - CustomException customException = new CustomException("REQUEST_PARSE_FAILED", "Failed to parse request at API gateway"); - customException.setCode(HttpStatus.UNAUTHORIZED.toString()); - throw customException; - } - } - else { - setTenantIdsFromQueryParams(request.getQueryParams(), tenantIds); - } - - return tenantIds; - } - - private void setTenantIdsFromQueryParams(MultiValueMap queryParams, Set tenantIds) throws CustomException { - - if (!CollectionUtils.isEmpty(queryParams) && queryParams.containsKey(REQUEST_TENANT_ID_KEY) - && queryParams.get(REQUEST_TENANT_ID_KEY).size() > 0) { - String tenantId = queryParams.get(REQUEST_TENANT_ID_KEY).get(0); - if (tenantId.contains(",")) { - tenantIds.addAll(Arrays.asList(tenantId.split(","))); - } else { - tenantIds.add(tenantId); - } - } else { - throw new CustomException("TENANT_ID_MANDATORY", "TenantId is mandatory in URL for non json requests"); - } - - } - } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacPreCheckFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacPreCheckFilterHelper.java index f1ea5c72a55..4b5d11896f0 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacPreCheckFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacPreCheckFilterHelper.java @@ -16,14 +16,14 @@ @Slf4j @Component -public class RbacPreCheckFilterHelper implements RewriteFunction { +public class RbacPreCheckFilterHelper implements RewriteFunction { private List anonymousEndpointsWhitelist; private ApplicationProperties applicationProperties; - public RbacPreCheckFilterHelper(List anonymousEndpointsWhitelist , ApplicationProperties applicationProperties) { + public RbacPreCheckFilterHelper(List anonymousEndpointsWhitelist, ApplicationProperties applicationProperties) { this.anonymousEndpointsWhitelist = anonymousEndpointsWhitelist; this.applicationProperties = applicationProperties; } @@ -32,11 +32,10 @@ public RbacPreCheckFilterHelper(List anonymousEndpointsWhitelist , Appli public Publisher apply(ServerWebExchange serverWebExchange, Map map) { String endPointPath = serverWebExchange.getRequest().getPath().value(); - if(applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath) || anonymousEndpointsWhitelist.contains(endPointPath)){ + if (applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath) || anonymousEndpointsWhitelist.contains(endPointPath)) { serverWebExchange.getAttributes().put(RBAC_BOOLEAN_FLAG_NAME, false); log.info(SKIP_RBAC, endPointPath); - } - else { + } else { serverWebExchange.getAttributes().put(RBAC_BOOLEAN_FLAG_NAME, true); } return Mono.just(map); diff --git a/core-services/gateway/src/main/java/com/example/gateway/utils/CommonUtils.java b/core-services/gateway/src/main/java/com/example/gateway/utils/CommonUtils.java index a9c103a652f..0651f40810b 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/utils/CommonUtils.java +++ b/core-services/gateway/src/main/java/com/example/gateway/utils/CommonUtils.java @@ -4,8 +4,14 @@ import java.util.*; import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.request.User; +import org.egov.common.utils.MultiStateInstanceUtil; import org.egov.tracer.model.CustomException; +import org.slf4j.MDC; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -15,17 +21,26 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.web.server.ServerWebExchange; import static com.example.gateway.constants.GatewayConstants.*; @Component +@Slf4j public class CommonUtils { private ObjectMapper objectMapper; - public CommonUtils(ObjectMapper objectMapper) { + private MultiStateInstanceUtil centralInstanceUtil; + + private UserUtils userUtils; + + + public CommonUtils(ObjectMapper objectMapper, MultiStateInstanceUtil centralInstanceUtil, UserUtils userUtils) { this.objectMapper = objectMapper; + this.centralInstanceUtil = centralInstanceUtil; + this.userUtils = userUtils; } public static boolean isRequestBodyCompatible(ServerHttpRequest serverHttpRequest) { @@ -39,6 +54,32 @@ public static boolean isRequestBodyCompatible(ServerHttpRequest serverHttpReques || contentType.contains(X_WWW_FORM_URLENCODED_TYPE)); } + public boolean isFormContentType(String contentType) { + return contentType == null || contentType.contains(FORM_DATA) || contentType.contains(X_WWW_FORM_URLENCODED_TYPE); + } + + public void handleCentralInstanceLogic(ServerWebExchange exchange, String requestURI, boolean isOpenRequest, boolean isMixedModeRequest, Map body) { + if (centralInstanceUtil.getIsEnvironmentCentralInstance() && (isOpenRequest || isMixedModeRequest) + && !requestURI.equalsIgnoreCase("/user/oauth/token")) { + + Set tenantIds = new HashSet<>(); + if (HttpMethod.GET.equals(exchange.getRequest().getMethod()) || ObjectUtils.isEmpty(body)) { + setTenantIdsFromQueryParams(exchange.getRequest().getQueryParams(), tenantIds); + } else { + tenantIds = getTenantIdsFromRequest(exchange.getRequest(), body); + + } + + if (CollectionUtils.isEmpty(tenantIds) && isOpenRequest) { + throw new CustomException("INVALID_TENANT_ID", "No tenantId in the request"); + } + + String tenantId = getLowLevelTenantIdFromSet(tenantIds); + MDC.put(TENANTID_MDC, tenantId); + exchange.getAttributes().put(TENANTID_MDC, tenantId); + } + } + private static String getRequestMethod(ServerHttpRequest serverHttpRequest) { return serverHttpRequest.getMethod().toString(); } @@ -79,7 +120,7 @@ public String getLowLevelTenantIdFromSet(Set tenants) { } - public Set validateRequestAndSetRequestTenantId(ServerWebExchange exchange , Map body) { + public Set validateRequestAndSetRequestTenantId(ServerWebExchange exchange, Map body) { return getTenantIdsFromRequest(exchange.getRequest(), body); } @@ -123,13 +164,13 @@ else if (requestBody.has(REQUEST_INFO_FIELD_NAME_CAMEL_CASE)) customException.setCode(HttpStatus.UNAUTHORIZED.toString()); throw customException; } - } - else { + } else { setTenantIdsFromQueryParams(request.getQueryParams(), tenantIds); } return tenantIds; } + public void setTenantIdsFromQueryParams(MultiValueMap queryParams, Set tenantIds) throws CustomException { if (!CollectionUtils.isEmpty(queryParams) && queryParams.containsKey(REQUEST_TENANT_ID_KEY) @@ -146,4 +187,62 @@ public void setTenantIdsFromQueryParams(MultiValueMap queryParam } + public String getRequestURL(ServerHttpRequest request) { + + // Manually construct the full request URL + String scheme = request.getURI().getScheme(); // e.g., "http" or "https" + String host = request.getURI().getHost(); // e.g., "example.com" + int port = request.getURI().getPort(); // e.g., 80 or 443 (can be -1 if default port is used) + String path = request.getURI().getPath(); // e.g., "/api/users" + + // Construct the full URL + StringBuilder requestURL = new StringBuilder(scheme).append("://").append(host); + + // Add the port if it's not the default (80 for HTTP, 443 for HTTPS) + if (port != -1) { + requestURL.append(":").append(port); + } + + // Append the request path + requestURL.append(path); + + return requestURL.toString(); + } + + /** + * method to fetch state level tenant-id based on whether the server is a + * multi-state instance or single-state instance + * + * @return + */ + public String getStateLevelTenantForHost(ServerHttpRequest request) { + String tenantId = ""; + if (centralInstanceUtil.getIsEnvironmentCentralInstance()) { + String requestURL = getRequestURL(request); + String host = requestURL.replace(request.getURI().getPath(), "").replace("https://", "").replace("http://", ""); + tenantId = userUtils.getStateLevelTenantMap().get(host); + } else { + tenantId = userUtils.getStateLevelTenant(); + } + return tenantId; + } + + public void setAnonymousUser(ServerWebExchange exchange, Map body) { + ServerHttpRequest request = exchange.getRequest(); + String CorrelationId = exchange.getAttributes().get(CORRELATION_ID_KEY).toString(); + String tenantId = getStateLevelTenantForHost(request); + User systemUser = userUtils.fetchSystemUser(tenantId, CorrelationId); + try { + ObjectMapper objectMapper = new ObjectMapper(); + RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); + requestInfo.setUserInfo(systemUser); + body.put(REQUEST_INFO_FIELD_NAME_PASCAL_CASE, requestInfo); + } catch (Exception ex) { + log.error("An error occured while transforming the request body to set Anonymous User {}", ex); + + // Throw a custom exception + throw new CustomException("AUTHENTICATION_ERROR", ex.getMessage()); + } + } + } \ No newline at end of file diff --git a/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java b/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java index 9bff45593b5..b02f7794a5d 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java +++ b/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java @@ -15,12 +15,12 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; +import org.springframework.web.server.ServerWebExchange; import java.util.Collections; import java.util.Map; -import static com.example.gateway.constants.GatewayConstants.CORRELATION_ID_HEADER_NAME; -import static com.example.gateway.constants.GatewayConstants.REQUEST_TENANT_ID_KEY; +import static com.example.gateway.constants.GatewayConstants.*; @Slf4j @Component @@ -41,31 +41,34 @@ public class UserUtils { private MultiStateInstanceUtil multiStateInstanceUtil; - public UserUtils (RestTemplate restTemplate, ApplicationProperties applicationProperties, MultiStateInstanceUtil multiStateInstanceUtil) { + public UserUtils(RestTemplate restTemplate, ApplicationProperties applicationProperties, MultiStateInstanceUtil multiStateInstanceUtil) { this.restTemplate = restTemplate; this.applicationProperties = applicationProperties; this.multiStateInstanceUtil = multiStateInstanceUtil; } - public User getUser(String authToken) { - String authURL = String.format("%s%s%s", applicationProperties.getAuthServiceHost(), applicationProperties.getAuthUri(), authToken); - + public User getUser(String authToken, ServerWebExchange exchange) { User user; + String authURL = String.format("%s%s%s", applicationProperties.getAuthServiceHost(), applicationProperties.getAuthUri(), authToken); + final HttpHeaders headers = new HttpHeaders(); + headers.add(CORRELATION_ID_HEADER_NAME, (String) exchange.getAttributes().get(CORRELATION_ID_KEY)); + if (multiStateInstanceUtil.getIsEnvironmentCentralInstance()) + headers.add(REQUEST_TENANT_ID_KEY, (String) exchange.getAttributes().get(TENANTID_MDC)); + final HttpEntity httpEntity = new HttpEntity<>(null, headers); try { - user = restTemplate.postForObject(authURL, null, User.class); + user = restTemplate.postForObject(authURL, httpEntity, User.class); } catch (Exception e) { throw new CustomException("Exception occurred while fetching user: ", e.getMessage()); -// throw new CustomException("Exception occurred while fetching user: ", "Error while authenticating the auth token"); } return user; } - @Cacheable(value = "systemUser" , sync = true) + @Cacheable(value = "systemUser", sync = true) public User fetchSystemUser(String tenantId, String correlationId) { - UserSearchRequest userSearchRequest =new UserSearchRequest(); + UserSearchRequest userSearchRequest = new UserSearchRequest(); userSearchRequest.setRoleCodes(Collections.singletonList("ANONYMOUS")); userSearchRequest.setUserType("SYSTEM"); userSearchRequest.setPageSize(1); @@ -83,8 +86,8 @@ public User fetchSystemUser(String tenantId, String correlationId) { UserDetailResponse response = restTemplate.postForObject(uri.toString(), httpEntity, UserDetailResponse.class); if (!CollectionUtils.isEmpty(response.getUser())) user = response.getUser().get(0); - } catch(Exception e) { - log.error("Exception while fetching system user: ",e); + } catch (Exception e) { + log.error("Exception while fetching system user: ", e); } /*if(user == null) From a916a090e6afe63946fdc699e4107ea5273381c9 Mon Sep 17 00:00:00 2001 From: Rishabh-egov Date: Tue, 1 Oct 2024 12:00:04 +0530 Subject: [PATCH 2/2] - Handled get req and form content type separately in all necessary filters --- .../gateway/filters/pre/AuthFilter.java | 40 ++++-- .../filters/pre/AuthPreCheckFilter.java | 9 +- .../filters/pre/CorrelationIdFilter.java | 7 +- .../gateway/filters/pre/PreHookFilter.java | 14 ++- .../gateway/filters/pre/RbacFilter.java | 119 ++++++++++++++++-- .../filters/pre/RbacPreCheckFilter.java | 5 - .../filters/pre/RequestEnrichmentFilter.java | 18 +-- .../filters/pre/RequestStartTimeFilter.java | 4 +- .../filters/pre/helpers/RbacFilterHelper.java | 19 ++- .../RequestEnrichmentFilterHelper.java | 47 ++++--- .../com/example/gateway/utils/UserUtils.java | 1 + 11 files changed, 203 insertions(+), 80 deletions(-) diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java index 570f85d7631..e68ba78693a 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthFilter.java @@ -1,45 +1,69 @@ package com.example.gateway.filters.pre; import com.example.gateway.filters.pre.helpers.AuthCheckFilterHelper; +import com.example.gateway.utils.CommonUtils; +import com.example.gateway.utils.UserUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpHeaders; +import org.egov.common.contract.request.User; +import org.slf4j.MDC; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Map; -import static com.example.gateway.constants.GatewayConstants.AUTH_BOOLEAN_FLAG_NAME; +import static com.example.gateway.constants.GatewayConstants.*; @Slf4j @Component public class AuthFilter implements GlobalFilter, Ordered { private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; - private AuthCheckFilterHelper authCheckFilterHelper; + private CommonUtils commonUtils; + private UserUtils userUtils; + private ObjectMapper objectMapper; - public AuthFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, AuthCheckFilterHelper authCheckFilterHelper) { + public AuthFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, AuthCheckFilterHelper authCheckFilterHelper, CommonUtils commonUtils, UserUtils userUtils, ObjectMapper objectMapper) { this.modifyRequestBodyFilter = modifyRequestBodyFilter; this.authCheckFilterHelper = authCheckFilterHelper; + this.commonUtils = commonUtils; + this.userUtils = userUtils; + this.objectMapper = objectMapper; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { Boolean doAuth = exchange.getAttribute(AUTH_BOOLEAN_FLAG_NAME); + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); + String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); // TODO: handle auth for form-data content type (filestore) if (Boolean.TRUE.equals(doAuth)) { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, authCheckFilterHelper)) - .filter(exchange, chain); - } else { - return chain.filter(exchange); + + if (isGetRequest || commonUtils.isFormContentType(contentType)) { + String authToken = exchange.getRequest().getHeaders().get(AUTH_TOKEN).get(0); + User user = userUtils.getUser(authToken, exchange); + try { + MDC.put(USER_INFO_KEY, objectMapper.writeValueAsString(user)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } else { + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, authCheckFilterHelper)).filter(exchange, chain); + } + } + return chain.filter(exchange); + } @Override diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java index 623a1bd7627..9ae33a98047 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/AuthPreCheckFilter.java @@ -17,6 +17,7 @@ import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; @@ -64,20 +65,18 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); String endPointPath = exchange.getRequest().getPath().value(); + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); if (applicationProperties.getOpenEndpointsWhitelist().contains(endPointPath)) { exchange.getAttributes().put(AUTH_BOOLEAN_FLAG_NAME, Boolean.FALSE); log.info(OPEN_ENDPOINT_MESSAGE, endPointPath); return chain.filter(exchange); - } else if (commonUtils.isFormContentType(contentType)) { + } else if (isGetRequest || commonUtils.isFormContentType(contentType)) { return handleAuthPreCheck(exchange, chain); } else { - return modifyRequestBodyFilter - .apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, authPreCheckFilterHelper)) - .filter(exchange, chain); + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, authPreCheckFilterHelper)).filter(exchange, chain); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java index 59feeb7ced1..f94695a92bd 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/CorrelationIdFilter.java @@ -34,8 +34,7 @@ public class CorrelationIdFilter implements GlobalFilter, Ordered { private final CommonUtils commonUtils; - public CorrelationIdFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilter, CorrelationIdFilterHelper correlationIdFilterHelper, - ApplicationProperties applicationProperties, CommonUtils commonUtils) { + public CorrelationIdFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilter, CorrelationIdFilterHelper correlationIdFilterHelper, ApplicationProperties applicationProperties, CommonUtils commonUtils) { this.modifyRequestBodyFilter = modifyRequestBodyGatewayFilter; this.correlationIdFilterHelper = correlationIdFilterHelper; @@ -62,9 +61,7 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange); } else { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, correlationIdFilterHelper)) - .filter(exchange, chain); + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, correlationIdFilterHelper)).filter(exchange, chain); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/PreHookFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/PreHookFilter.java index 3b31fe5d133..37df53c1abb 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/PreHookFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/PreHookFilter.java @@ -1,11 +1,13 @@ package com.example.gateway.filters.pre; import com.example.gateway.filters.pre.helpers.PreHookFilterHelper; +import com.example.gateway.utils.CommonUtils; import org.apache.http.HttpHeaders; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @@ -17,22 +19,24 @@ public class PreHookFilter implements GlobalFilter, Ordered { private PreHookFilterHelper preHookFilterHelper; - public PreHookFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, PreHookFilterHelper preHookFilterHelper) { + private CommonUtils commonUtils; + + public PreHookFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, PreHookFilterHelper preHookFilterHelper, CommonUtils commonUtils) { this.modifyRequestBodyFilter = modifyRequestBodyFilter; this.preHookFilterHelper = preHookFilterHelper; + this.commonUtils = commonUtils; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); - if (contentType != null && (contentType.contains("multipart/form-data") || contentType.contains("application/x-www-form-urlencoded"))) { + if (isGetRequest || commonUtils.isFormContentType(contentType)) { return chain.filter(exchange); } else { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, preHookFilterHelper)) - .filter(exchange, chain); + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, preHookFilterHelper)).filter(exchange, chain); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacFilter.java index eef4a37a443..97b441c30ce 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacFilter.java @@ -1,47 +1,152 @@ package com.example.gateway.filters.pre; +import com.example.gateway.config.ApplicationProperties; import com.example.gateway.filters.pre.helpers.RbacFilterHelper; +import com.example.gateway.model.AuthorizationRequest; +import com.example.gateway.model.AuthorizationRequestWrapper; +import com.example.gateway.utils.CommonUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.apache.http.HttpHeaders; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.request.User; +import org.egov.common.utils.MultiStateInstanceUtil; +import org.egov.tracer.model.CustomException; +import org.slf4j.MDC; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.util.HashSet; import java.util.Map; +import java.util.Set; -import static com.example.gateway.constants.GatewayConstants.RBAC_BOOLEAN_FLAG_NAME; +import static com.example.gateway.constants.GatewayConstants.*; @Slf4j @Component -public class RbacFilter implements GlobalFilter , Ordered { +public class RbacFilter implements GlobalFilter, Ordered { private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; private RbacFilterHelper rbacFilterHelper; + private CommonUtils commonUtils; + private ObjectMapper objectMapper; + private MultiStateInstanceUtil centralInstanceUtil; + private RestTemplate restTemplate; + private ApplicationProperties applicationProperties; - public RbacFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, RbacFilterHelper rbacFilterHelper) { + public RbacFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, RbacFilterHelper rbacFilterHelper, CommonUtils commonUtils, ObjectMapper objectMapper, MultiStateInstanceUtil centralInstanceUtil, RestTemplate restTemplate, ApplicationProperties applicationProperties) { this.modifyRequestBodyFilter = modifyRequestBodyFilter; this.rbacFilterHelper = rbacFilterHelper; + this.commonUtils = commonUtils; + this.objectMapper = objectMapper; + this.centralInstanceUtil = centralInstanceUtil; + this.restTemplate = restTemplate; + this.applicationProperties = applicationProperties; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); + String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); Boolean rbacFlag = exchange.getAttribute(RBAC_BOOLEAN_FLAG_NAME); - if(rbacFlag) { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, rbacFilterHelper)) - .filter(exchange, chain); + if (Boolean.TRUE.equals(rbacFlag)) { + if (isGetRequest || commonUtils.isFormContentType(contentType)) { + isIncomingURIInAuthorizedActionList(exchange); + } else { + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, rbacFilterHelper)).filter(exchange, chain); + } } return chain.filter(exchange); } + private void isIncomingURIInAuthorizedActionList(ServerWebExchange exchange) { + + String requestUri = exchange.getRequest().getURI().getPath(); + String userInfo = MDC.get(USER_INFO_KEY); + User user = null; + if (!ObjectUtils.isEmpty(userInfo)) { + try { + user = objectMapper.readValue(userInfo, User.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + } + if (user == null) { + throw new RuntimeException("User information not found. Can't execute RBAC filter"); + } + + Set tenantIds = new HashSet<>(); + commonUtils.setTenantIdsFromQueryParams(exchange.getRequest().getQueryParams(), tenantIds); + + /* + * Adding tenantId to header for tracer logging with correlation-id + */ + if (centralInstanceUtil.getIsEnvironmentCentralInstance() && StringUtils.isEmpty(exchange.getAttributes().get(TENANTID_MDC))) { + String singleTenantId = commonUtils.getLowLevelTenantIdFromSet(tenantIds); + MDC.put(TENANTID_MDC, singleTenantId); + exchange.getAttributes().put(TENANTID_MDC, singleTenantId); + } + + exchange.getAttributes().put(CURRENT_REQUEST_TENANTID, String.join(",", tenantIds)); + + AuthorizationRequest request = AuthorizationRequest.builder().roles(new HashSet<>(user.getRoles())).uri(requestUri).tenantIds(tenantIds).build(); + + boolean isUriAuthorised = isUriAuthorized(request, exchange); + + if (!isUriAuthorised) { + throw new CustomException(HttpStatus.UNAUTHORIZED.toString(), "You are not authorized to access this resource"); + } + + } + + private boolean isUriAuthorized(AuthorizationRequest authorizationRequest, ServerWebExchange exchange) { + + AuthorizationRequestWrapper authorizationRequestWrapper = new AuthorizationRequestWrapper(new RequestInfo(), authorizationRequest); + + final org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders(); + + headers.add(CORRELATION_ID_HEADER_NAME, (String) exchange.getAttributes().get(CORRELATION_ID_KEY)); + + if (centralInstanceUtil.getIsEnvironmentCentralInstance()) + headers.add(REQUEST_TENANT_ID_KEY, (String) exchange.getAttributes().get(TENANTID_MDC)); + + final HttpEntity httpEntity = new HttpEntity<>(authorizationRequestWrapper, headers); + + try { + + ResponseEntity responseEntity = restTemplate.postForEntity(applicationProperties.getAuthorizationUrl(), httpEntity, Void.class); + + return responseEntity.getStatusCode().equals(HttpStatus.OK); + } catch (HttpClientErrorException e) { + log.warn("Exception while attempting to authorize via access control", e); + return false; + } catch (Exception e) { + log.error("Unknown exception occurred while attempting to authorize via access control", e); + return false; + } + + } + + @Override public int getOrder() { return 5; diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java index fe975a9bb38..f5faeddf91a 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RbacPreCheckFilter.java @@ -1,21 +1,16 @@ package com.example.gateway.filters.pre; import com.example.gateway.config.ApplicationProperties; -import com.example.gateway.filters.pre.helpers.RbacPreCheckFilterHelper; -import com.example.gateway.filters.pre.helpers.RbacPreCheckFormDataFilterHelper; import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpHeaders; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; -import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.List; -import java.util.Map; import static com.example.gateway.constants.GatewayConstants.*; diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestEnrichmentFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestEnrichmentFilter.java index 5fe3c5f0e28..eb2ced2b680 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestEnrichmentFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestEnrichmentFilter.java @@ -1,12 +1,14 @@ package com.example.gateway.filters.pre; import com.example.gateway.filters.pre.helpers.RequestEnrichmentFilterHelper; +import com.example.gateway.utils.CommonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpHeaders; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @@ -15,27 +17,29 @@ @Slf4j @Component -public class RequestEnrichmentFilter implements GlobalFilter , Ordered { +public class RequestEnrichmentFilter implements GlobalFilter, Ordered { private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; private RequestEnrichmentFilterHelper requestEnrichmentFilterHelper; - public RequestEnrichmentFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, RequestEnrichmentFilterHelper requestEnrichmentFilterHelper) { + private CommonUtils commonUtils; + + public RequestEnrichmentFilter(ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter, RequestEnrichmentFilterHelper requestEnrichmentFilterHelper, CommonUtils commonUtils) { this.modifyRequestBodyFilter = modifyRequestBodyFilter; this.requestEnrichmentFilterHelper = requestEnrichmentFilterHelper; + this.commonUtils = commonUtils; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); - - if (contentType != null && (contentType.contains("multipart/form-data") || contentType.contains("application/x-www-form-urlencoded"))) { + boolean isGetRequest = HttpMethod.GET.equals(exchange.getRequest().getMethod()); + if (isGetRequest || commonUtils.isFormContentType(contentType)) { + // TODO: Refactor the helper class to be re-usable for this case as well. return chain.filter(exchange); } else { - return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config() - .setRewriteFunction(Map.class, Map.class, requestEnrichmentFilterHelper)) - .filter(exchange, chain); + return modifyRequestBodyFilter.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(Map.class, Map.class, requestEnrichmentFilterHelper)).filter(exchange, chain); } } diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestStartTimeFilter.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestStartTimeFilter.java index b7060ad527c..ff5aa151bee 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestStartTimeFilter.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/RequestStartTimeFilter.java @@ -8,13 +8,15 @@ import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; + import static com.example.gateway.constants.GatewayConstants.CURRENT_REQUEST_START_TIME; @Slf4j @Component public class RequestStartTimeFilter implements GlobalFilter, Ordered { - public RequestStartTimeFilter() {} + public RequestStartTimeFilter() { + } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacFilterHelper.java index f0ae92bcd6f..4707e512b4b 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RbacFilterHelper.java @@ -55,7 +55,7 @@ public RbacFilterHelper(ObjectMapper objectMapper, MultiStateInstanceUtil centra @Override public Publisher apply(ServerWebExchange serverWebExchange, Map map) { - isIncomingURIInAuthorizedActionList(serverWebExchange,map); + isIncomingURIInAuthorizedActionList(serverWebExchange, map); return Mono.just(map); } @@ -69,7 +69,7 @@ private void isIncomingURIInAuthorizedActionList(ServerWebExchange exchange, Map throw new RuntimeException("User information not found. Can't execute RBAC filter"); } - Set tenantIds = commonUtils.validateRequestAndSetRequestTenantId(exchange,map); + Set tenantIds = commonUtils.validateRequestAndSetRequestTenantId(exchange, map); /* * Adding tenantId to header for tracer logging with correlation-id @@ -82,21 +82,17 @@ private void isIncomingURIInAuthorizedActionList(ServerWebExchange exchange, Map exchange.getAttributes().put(CURRENT_REQUEST_TENANTID, String.join(",", tenantIds)); - AuthorizationRequest request = AuthorizationRequest.builder() - .roles(new HashSet<>(user.getRoles())) - .uri(requestUri) - .tenantIds(tenantIds) - .build(); + AuthorizationRequest request = AuthorizationRequest.builder().roles(new HashSet<>(user.getRoles())).uri(requestUri).tenantIds(tenantIds).build(); - boolean isUriAuthorised = isUriAuthorized(request , exchange); + boolean isUriAuthorised = isUriAuthorized(request, exchange); - if(!isUriAuthorised) { + if (!isUriAuthorised) { throw new CustomException(HttpStatus.UNAUTHORIZED.toString(), "You are not authorized to access this resource"); } } - private boolean isUriAuthorized(AuthorizationRequest authorizationRequest , ServerWebExchange exchange) { + private boolean isUriAuthorized(AuthorizationRequest authorizationRequest, ServerWebExchange exchange) { AuthorizationRequestWrapper authorizationRequestWrapper = new AuthorizationRequestWrapper(new RequestInfo(), authorizationRequest); @@ -111,8 +107,7 @@ private boolean isUriAuthorized(AuthorizationRequest authorizationRequest , Serv try { - ResponseEntity responseEntity = restTemplate.postForEntity(applicationProperties.getAuthorizationUrl(), httpEntity, Void - .class); + ResponseEntity responseEntity = restTemplate.postForEntity(applicationProperties.getAuthorizationUrl(), httpEntity, Void.class); return responseEntity.getStatusCode().equals(HttpStatus.OK); } catch (HttpClientErrorException e) { diff --git a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RequestEnrichmentFilterHelper.java b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RequestEnrichmentFilterHelper.java index e072b8f6fb7..1123c2feeb4 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RequestEnrichmentFilterHelper.java +++ b/core-services/gateway/src/main/java/com/example/gateway/filters/pre/helpers/RequestEnrichmentFilterHelper.java @@ -29,15 +29,14 @@ @Slf4j @Component -public class RequestEnrichmentFilterHelper implements RewriteFunction { +public class RequestEnrichmentFilterHelper implements RewriteFunction { private MultiStateInstanceUtil centralInstanceUtil; private CommonUtils commonUtils; private static final String FAILED_TO_ENRICH_REQUEST_BODY_MESSAGE = "Failed to enrich request body"; private static final String USER_SERIALIZATION_MESSAGE = "Failed to serialize user"; - private static final String SKIPPED_BODY_ENRICHMENT_DUE_TO_NO_KNOWN_FIELD_MESSAGE = - "Skipped enriching request body since request info field is not present."; + private static final String SKIPPED_BODY_ENRICHMENT_DUE_TO_NO_KNOWN_FIELD_MESSAGE = "Skipped enriching request body since request info field is not present."; private static final String BODY_ENRICHED_MESSAGE = "Enriched request payload."; private static final String ADDED_USER_INFO_TO_HEADER_MESSAGE = "Adding user info to header."; private static final String EMPTY_STRING = ""; @@ -58,20 +57,18 @@ public RequestEnrichmentFilterHelper(MultiStateInstanceUtil centralInstanceUtil, public Publisher apply(ServerWebExchange exchange, Map body) { // Enrich User Info and Correlation Id in the request - modifyRequestBody(exchange , body); + modifyRequestBody(exchange, body); // Add User_Info and Correlation Id in the header - addRequestHeaders(exchange , body); - if(Objects.isNull(body)){ + addRequestHeaders(exchange, body); + if (Objects.isNull(body)) { return Mono.empty(); - } - else - return Mono.just(body); + } else return Mono.just(body); } - private void addRequestHeaders(ServerWebExchange exchange , Map body) { + private void addRequestHeaders(ServerWebExchange exchange, Map body) { addCorrelationIdHeader(exchange); - addUserInfoHeader(exchange,body); + addUserInfoHeader(exchange, body); addPassThroughGatewayHeader(exchange); } @@ -80,16 +77,15 @@ private void addCorrelationIdHeader(ServerWebExchange exchange) { String correlationId = (String) exchange.getAttributes().get(CORRELATION_ID_KEY); String TenantId = (String) exchange.getAttributes().get(TENANTID_MDC); - exchange.getRequest().mutate() - .headers(httpHeaders -> { - httpHeaders.add(CORRELATION_ID_HEADER_NAME, correlationId); - if (centralInstanceUtil.getIsEnvironmentCentralInstance()) { - httpHeaders.add(REQUEST_TENANT_ID_KEY, TenantId); - } - }); + exchange.getRequest().mutate().headers(httpHeaders -> { + httpHeaders.add(CORRELATION_ID_HEADER_NAME, correlationId); + if (centralInstanceUtil.getIsEnvironmentCentralInstance()) { + httpHeaders.add(REQUEST_TENANT_ID_KEY, TenantId); + } + }); } - private void addUserInfoHeader(ServerWebExchange exchange , Map body) { + private void addUserInfoHeader(ServerWebExchange exchange, Map body) { if (isUserInfoPresent(body) && !isRequestBodyCompatible(exchange.getRequest())) { User user = getUser(body); exchange.getRequest().mutate().headers(httpHeaders -> { @@ -107,6 +103,7 @@ private User getUser(Map body) { RequestInfo requestInfo = objectMapper.convertValue(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE), RequestInfo.class); return requestInfo.getUserInfo(); } + private void addPassThroughGatewayHeader(ServerWebExchange exchange) { exchange.getRequest().mutate().headers(httpHeaders -> { httpHeaders.add(PASS_THROUGH_GATEWAY_HEADER_NAME, PASS_THROUGH_GATEWAY_HEADER_VALUE); @@ -115,7 +112,7 @@ private void addPassThroughGatewayHeader(ServerWebExchange exchange) { private boolean isUserInfoPresent(Map body) { - if(Objects.isNull(body) || Objects.isNull(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE))){ + if (Objects.isNull(body) || Objects.isNull(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE))) { return Boolean.FALSE; } @@ -123,23 +120,23 @@ private boolean isUserInfoPresent(Map body) { return requestInfo.getUserInfo() != null; } - private void modifyRequestBody(ServerWebExchange exchange , Map body) { + private void modifyRequestBody(ServerWebExchange exchange, Map body) { - if(!isRequestBodyCompatible(exchange.getRequest())) { + if (!isRequestBodyCompatible(exchange.getRequest())) { return; } try { - enrichRequestBody(exchange , body); + enrichRequestBody(exchange, body); } catch (IOException e) { logger.error(FAILED_TO_ENRICH_REQUEST_BODY_MESSAGE, e); throw new CustomException("FAILED_TO_ENRICH_REQUEST_BODY", e.getMessage()); } } - private void enrichRequestBody(ServerWebExchange exchange , Map body) throws IOException { + private void enrichRequestBody(ServerWebExchange exchange, Map body) throws IOException { // TODO: Check for Camel case of requestInfo as well - if(Objects.isNull(body) || Objects.isNull(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE))){ + if (Objects.isNull(body) || Objects.isNull(body.get(REQUEST_INFO_FIELD_NAME_PASCAL_CASE))) { logger.info(SKIPPED_BODY_ENRICHMENT_DUE_TO_NO_KNOWN_FIELD_MESSAGE); return; } diff --git a/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java b/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java index b02f7794a5d..8581822b78f 100644 --- a/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java +++ b/core-services/gateway/src/main/java/com/example/gateway/utils/UserUtils.java @@ -65,6 +65,7 @@ public User getUser(String authToken, ServerWebExchange exchange) { return user; } + // TODO: test this once for actual data @Cacheable(value = "systemUser", sync = true) public User fetchSystemUser(String tenantId, String correlationId) {