From e308fa5e1af9db25a8f026692cbcaab468f50ed2 Mon Sep 17 00:00:00 2001 From: Shan Chathusanda Jayathilaka Date: Sun, 1 Dec 2024 02:01:15 +0530 Subject: [PATCH] Add support to access organization resources in tenant perspective --- .../impl/OAuth2AccessTokenHandler.java | 27 +++++- .../authz/valve/AuthorizationValve.java | 6 +- .../valve/TenantContextRewriteValve.java | 84 ++++++++++++++++++- pom.xml | 4 +- 4 files changed, 113 insertions(+), 8 deletions(-) diff --git a/components/org.wso2.carbon.identity.auth.service/src/main/java/org/wso2/carbon/identity/auth/service/handler/impl/OAuth2AccessTokenHandler.java b/components/org.wso2.carbon.identity.auth.service/src/main/java/org/wso2/carbon/identity/auth/service/handler/impl/OAuth2AccessTokenHandler.java index d6a15e40..2557d41c 100644 --- a/components/org.wso2.carbon.identity.auth.service/src/main/java/org/wso2/carbon/identity/auth/service/handler/impl/OAuth2AccessTokenHandler.java +++ b/components/org.wso2.carbon.identity.auth.service/src/main/java/org/wso2/carbon/identity/auth/service/handler/impl/OAuth2AccessTokenHandler.java @@ -32,6 +32,7 @@ import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.common.model.ProvisioningServiceProviderType; import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty; import org.wso2.carbon.identity.application.common.model.ThreadLocalProvisioningServiceProvider; import org.wso2.carbon.identity.application.common.model.User; import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil; @@ -57,6 +58,7 @@ import org.wso2.carbon.identity.oauth2.validators.RefreshTokenValidator; import java.text.ParseException; +import java.util.Arrays; import java.util.Map; import java.util.Optional; @@ -173,8 +175,23 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) { ServiceProvider serviceProvider = null; String serviceProviderName = null; String serviceProviderUUID = null; + boolean isSubOrgApp = false; try { - serviceProvider = OAuth2Util.getServiceProvider(oAuth2IntrospectionResponseDTO.getClientId()); + if (authorizedUser != null) { + ServiceProviderProperty[] serviceProviderProperties = OAuth2Util.getServiceProvider( + oAuth2IntrospectionResponseDTO.getClientId(), authorizedUser.getTenantDomain()). + getSpProperties(); + if (serviceProviderProperties != null && Arrays.stream(serviceProviderProperties) + .anyMatch(property -> "isSubOrgApp".equals(property.getName()) + && Boolean.parseBoolean(property.getValue()))) { + isSubOrgApp = true; + authenticationContext.addParameter("isSubOrgApp", true); + serviceProvider = OAuth2Util.getServiceProvider(oAuth2IntrospectionResponseDTO.getClientId(), + authorizedUser.getTenantDomain()); + } + } else { + serviceProvider = OAuth2Util.getServiceProvider(oAuth2IntrospectionResponseDTO.getClientId()); + } if (serviceProvider != null) { serviceProviderName = serviceProvider.getApplicationName(); serviceProviderUUID = serviceProvider.getApplicationResourceId(); @@ -193,8 +210,12 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) { String serviceProviderTenantDomain = null; try { - serviceProviderTenantDomain = - OAuth2Util.getTenantDomainOfOauthApp(oAuth2IntrospectionResponseDTO.getClientId()); + if (serviceProvider != null && isSubOrgApp) { + serviceProviderTenantDomain = serviceProvider.getTenantDomain(); + } else { + serviceProviderTenantDomain = OAuth2Util.getTenantDomainOfOauthApp( + oAuth2IntrospectionResponseDTO.getClientId()); + } } catch (InvalidOAuthClientException | IdentityOAuth2Exception e) { if (log.isDebugEnabled()) { log.debug("Error occurred while getting the OAuth App tenantDomain by Consumer key: " diff --git a/components/org.wso2.carbon.identity.authz.valve/src/main/java/org/wso2/carbon/identity/authz/valve/AuthorizationValve.java b/components/org.wso2.carbon.identity.authz.valve/src/main/java/org/wso2/carbon/identity/authz/valve/AuthorizationValve.java index e30f3cfc..8854172c 100644 --- a/components/org.wso2.carbon.identity.authz.valve/src/main/java/org/wso2/carbon/identity/authz/valve/AuthorizationValve.java +++ b/components/org.wso2.carbon.identity.authz.valve/src/main/java/org/wso2/carbon/identity/authz/valve/AuthorizationValve.java @@ -235,7 +235,11 @@ private AuthorizationResult authorizeInOrganizationLevel(Request request, Respon private boolean isRequestValidForTenant(AuthenticationContext authenticationContext, AuthorizationContext authorizationContext, Request request) { - return (Utils.isUserBelongsToRequestedTenant(authenticationContext, request) || + boolean isSubOrgApp = false; + if (authenticationContext.getParameter("isSubOrgApp") != null) { + isSubOrgApp = Boolean.parseBoolean(authenticationContext.getParameter("isSubOrgApp").toString()); + } + return (Utils.isUserBelongsToRequestedTenant(authenticationContext, request) || isSubOrgApp || (authorizationContext.isCrossTenantAllowed()) && Utils.isTenantBelongsToAllowedCrossTenant(authenticationContext, authorizationContext)); } diff --git a/components/org.wso2.carbon.identity.context.rewrite.valve/src/main/java/org/wso2/carbon/identity/context/rewrite/valve/TenantContextRewriteValve.java b/components/org.wso2.carbon.identity.context.rewrite.valve/src/main/java/org/wso2/carbon/identity/context/rewrite/valve/TenantContextRewriteValve.java index e956cdc8..f21a86fc 100644 --- a/components/org.wso2.carbon.identity.context.rewrite.valve/src/main/java/org/wso2/carbon/identity/context/rewrite/valve/TenantContextRewriteValve.java +++ b/components/org.wso2.carbon.identity.context.rewrite.valve/src/main/java/org/wso2/carbon/identity/context/rewrite/valve/TenantContextRewriteValve.java @@ -23,6 +23,7 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -30,6 +31,7 @@ import org.wso2.carbon.base.ServerConfiguration; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.base.IdentityRuntimeException; +import org.wso2.carbon.identity.context.rewrite.bean.OrganizationRewriteContext; import org.wso2.carbon.identity.context.rewrite.bean.RewriteContext; import org.wso2.carbon.identity.context.rewrite.internal.ContextRewriteValveServiceComponentHolder; import org.wso2.carbon.identity.core.util.IdentityConfigParser; @@ -41,8 +43,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; import javax.servlet.ServletException; @@ -59,6 +63,7 @@ public class TenantContextRewriteValve extends ValveBase { private static List contextsToRewrite; + private static List contextsToRewriteInTenantPerspective; private static List contextListToOverwriteDispatch; private static List ignorePathListForOverwriteDispatch; private static List organizationRoutingOnlySupportedAPIPaths; @@ -73,6 +78,7 @@ protected synchronized void startInternal() throws LifecycleException { super.startInternal(); // Initialize the tenant context rewrite valve. contextsToRewrite = getContextsToRewrite(); + contextsToRewriteInTenantPerspective = getContextsToRewriteInTenantPerspective(); contextListToOverwriteDispatch = getContextListToOverwriteDispatchLocation(); ignorePathListForOverwriteDispatch = getIgnorePathListForOverwriteDispatch(); isTenantQualifiedUrlsEnabled = isTenantQualifiedUrlsEnabled(); @@ -110,6 +116,26 @@ public void invoke(Request request, Response response) throws IOException, Servl } } + outerLoop: + for (OrganizationRewriteContext context : contextsToRewriteInTenantPerspective) { + Pattern patternTenantPerspective = Pattern.compile("^/t/[^/]+/o/[a-f0-9\\-]+?" + context.getContext()); + if (patternTenantPerspective.matcher(requestURI).find() && CollectionUtils.isNotEmpty(context.getSubPaths())) { + for (Pattern subPath : context.getSubPaths()) { + if (subPath.matcher(requestURI).find()) { + isContextRewrite = true; + isWebApp = context.isWebApp(); + contextToForward = context.getContext(); + int startIndex = requestURI.indexOf("/o/") + 3; + int endIndex = requestURI.indexOf("/", startIndex); + String appOrgId = requestURI.substring(startIndex, endIndex); + PrivilegedCarbonContext.getThreadLocalCarbonContext(). + setApplicationResidentOrganizationId(appOrgId); + break outerLoop; + } + } + } + } + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); try { MDC.put(TENANT_DOMAIN, tenantDomain); @@ -135,7 +161,8 @@ public void invoke(Request request, Response response) throws IOException, Servl Ex-: Request: /t//o/api/server/v1/applications --> /o/server/v1/applications */ if (!requestURI.startsWith(ORGANIZATION_PATH_PARAM) && - requestURI.contains(ORGANIZATION_PATH_PARAM)) { + requestURI.contains(ORGANIZATION_PATH_PARAM) && + !isOrganizationIdAvailableInTenantPerspective(requestURI)) { dispatchLocation = "/o" + dispatchLocation; } if (contextListToOverwriteDispatch.contains(contextToForward) && !isIgnorePath(dispatchLocation)) { @@ -151,7 +178,10 @@ public void invoke(Request request, Response response) throws IOException, Servl requestURI = requestURI.replace(carbonWebContext + "/", ""); } //Servlet - requestURI = requestURI.replace("/t/" + tenantDomain, ""); + if (StringUtils.isEmpty(PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId())) { + requestURI = requestURI.replace("/t/" + tenantDomain, ""); + } request.getRequestDispatcher(requestURI).forward(request, response); } } @@ -176,6 +206,11 @@ public void invoke(Request request, Response response) throws IOException, Servl } } + private boolean isOrganizationIdAvailableInTenantPerspective(String requestURI) { + + return Pattern.compile("^/t/[^/]+/o/[a-f0-9\\-]+?").matcher(requestURI).find(); + } + private void unsetMDCThreadLocals() { MDC.remove(TENANT_DOMAIN); @@ -217,6 +252,51 @@ private List getContextsToRewrite() { return rewriteContexts; } + private List getContextsToRewriteInTenantPerspective() { + + List organizationRewriteContexts = new ArrayList<>(); + Map configuration = IdentityConfigParser.getInstance().getConfiguration(); + Object webAppBasePathContexts = configuration.get("OrgContextsToRewriteInTenantPerspective.WebApp.Context." + + "BasePath"); + setOrganizationRewriteContexts(organizationRewriteContexts, webAppBasePathContexts, true); + + Object webAppSubPathContexts = configuration.get("OrgContextsToRewriteInTenantPerspective.WebApp.Context." + + "SubPaths.Path"); + setSubPathContexts(organizationRewriteContexts, webAppSubPathContexts); + + return organizationRewriteContexts; + } + + private void setOrganizationRewriteContexts(List organizationRewriteContexts, + Object basePathContexts, boolean isWebApp) { + + if (basePathContexts != null) { + if (basePathContexts instanceof ArrayList) { + for (String context : (ArrayList) basePathContexts) { + organizationRewriteContexts.add(new OrganizationRewriteContext(isWebApp, context)); + } + } else { + organizationRewriteContexts.add(new OrganizationRewriteContext(isWebApp, + basePathContexts.toString())); + } + } + } + + private void setSubPathContexts(List organizationRewriteContexts, + Object subPathContexts) { + + if (subPathContexts instanceof ArrayList) { + for (String subPath : (ArrayList) subPathContexts) { + Optional maybeOrgRewriteContext = organizationRewriteContexts.stream() + .filter(rewriteContext -> subPath.startsWith(rewriteContext.getContext())) + .max(Comparator.comparingInt(rewriteContext -> rewriteContext.getContext().length())); + maybeOrgRewriteContext.ifPresent( + organizationRewriteContext -> organizationRewriteContext.addSubPath( + Pattern.compile("^/t/[^/]+/o/[a-f0-9\\-]+" + subPath))); + } + } + } + /** * Get context list to overwrite dispatch location. * diff --git a/pom.xml b/pom.xml index 18b280f8..35295b23 100644 --- a/pom.xml +++ b/pom.xml @@ -452,8 +452,8 @@ [1.5.1, 2.0.0) - 4.9.17 - 4.9.0 + 4.10.25 + 4.10.0 [4.5.0, 5.0.0)