Skip to content

Commit

Permalink
Authorization valve and tenant context valve improvements for support…
Browse files Browse the repository at this point in the history
… B2B usecases
  • Loading branch information
sadilchamishka committed Oct 17, 2023
1 parent 067e314 commit e4dc253
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* Copyright (c) 2016-2023, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 Inc. licenses this file to you under the Apache License,
* WSO2 LLC. licenses this file to you 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
Expand Down Expand Up @@ -31,14 +31,9 @@
import org.wso2.carbon.identity.auth.service.exception.AuthClientException;
import org.wso2.carbon.identity.auth.service.exception.AuthServerException;
import org.wso2.carbon.identity.auth.service.exception.AuthenticationFailException;
import org.wso2.carbon.identity.auth.service.internal.AuthenticationServiceHolder;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.handler.AbstractIdentityMessageHandler;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;

import java.util.Optional;

/**
* This is the abstract class for custom authentication handlers.
Expand Down Expand Up @@ -104,51 +99,35 @@ protected void postAuthenticate(MessageContext messageContext, AuthenticationRes

User user = authenticationContext.getUser();
if (user != null) {
// Set the user in to the Carbon context if the user belongs to same tenant. Skip this for cross tenant
// scenarios.
// Set the user in to the Carbon context if the user belongs to same tenant or else if the accessing
// organization is authorized to access. Skip this for cross tenant scenarios.

if (user.getTenantDomain() != null && user.getTenantDomain().equalsIgnoreCase(PrivilegedCarbonContext
.getThreadLocalCarbonContext().getTenantDomain())) {
String authorizedOrganization = null;
if (user instanceof AuthenticatedUser) {
authorizedOrganization = ((AuthenticatedUser) user).getAccessingOrganization();
}
if (user.getTenantDomain() != null && (user.getTenantDomain()
.equalsIgnoreCase(PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain()) ||
StringUtils.isNotEmpty(authorizedOrganization))) {
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(IdentityUtil.addDomainToName
(user.getUserName(), user.getUserStoreDomain()));
}
// Set the user id to the Carbon context if the user authentication is succeeded.
try {
AuthenticatedUser authenticatedUser;
if (user instanceof AuthenticatedUser) {
PrivilegedCarbonContext.getThreadLocalCarbonContext()
.setUserId(((AuthenticatedUser) user).getUserId());
authenticatedUser = (AuthenticatedUser) user;
// For B2B organization users, set the user ID which is set as username in user object.
if (authenticatedUser.isFederatedUser() && StringUtils.isNotEmpty(authorizedOrganization)) {
PrivilegedCarbonContext.getThreadLocalCarbonContext()
.setUserId(authenticatedUser.getUserName());
return;
}
} else {
AuthenticatedUser authenticatedUser = new AuthenticatedUser(user);
PrivilegedCarbonContext.getThreadLocalCarbonContext()
.setUserId(authenticatedUser.getUserId());
authenticatedUser = new AuthenticatedUser(user);
}
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUserId(authenticatedUser.getUserId());
} catch (UserIdNotFoundException e) {
// Resolve authenticated resident user for organization specific calls.
String organizationId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getOrganizationId();
if (StringUtils.isNotBlank(organizationId)) {
try {
OrganizationUserResidentResolverService orgUserResolverService = AuthenticationServiceHolder
.getInstance().getOrganizationUserResidentResolverService();
Optional<org.wso2.carbon.user.core.common.User> resolvedUser = orgUserResolverService
.resolveUserFromResidentOrganization(
user.getUserName(), null, organizationId);

// Handle scenarios where the user id is set as the username.
if (!resolvedUser.isPresent()) {
resolvedUser = orgUserResolverService.resolveUserFromResidentOrganization(
null, user.getUserName(), organizationId);
}

if (resolvedUser.isPresent()) {
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUserId(resolvedUser.get()
.getUserID());
return;
}
} catch (OrganizationManagementException ex) {
LOG.error("Server encountered an error while resolving authenticated resident user " +
"for organization specific calls.", ex);
}
}
LOG.error("User id not found for user: " + user.getLoggableMaskedUserId());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
* Copyright (c) 2016-2023, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand All @@ -26,6 +26,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.auth.service.AuthenticationContext;
import org.wso2.carbon.identity.auth.service.exception.AuthRuntimeException;
import org.wso2.carbon.identity.auth.service.handler.HandlerManager;
Expand All @@ -41,6 +42,7 @@
import org.wso2.carbon.identity.authz.valve.internal.AuthorizationValveServiceHolder;
import org.wso2.carbon.identity.authz.valve.util.Utils;
import org.wso2.carbon.identity.organization.management.authz.service.OrganizationManagementAuthorizationContext;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;

import java.io.IOException;
Expand Down Expand Up @@ -91,7 +93,7 @@ public void invoke(Request request, Response response) throws IOException, Servl
*/
Object scopeValidationEnabled = authenticationContext.getParameter(OAUTH2_VALIDATE_SCOPE);
if (scopeValidationEnabled != null && Boolean.parseBoolean(scopeValidationEnabled.toString())) {
if (!Utils.isUserBelongsToRequestedTenant(authenticationContext, request)) {
if (!Utils.isUserAuthorizedOrganization(authenticationContext, request)) {
if (log.isDebugEnabled()) {
log.debug("Authorization to " + request.getRequestURI() +
" is denied because the used access token issued from a different tenant domain: " +
Expand Down Expand Up @@ -158,7 +160,19 @@ public void invoke(Request request, Response response) throws IOException, Servl
try {
AuthorizationResult authorizationResult = authorizationManager.authorize(authorizationContext);
if (authorizationResult.getAuthorizationStatus().equals(AuthorizationStatus.GRANT)) {
getNext().invoke(request, response);
String authorizedOrganization = ((AuthenticatedUser)authorizationContext.getUser())
.getAccessingOrganization();
// Start tenant flow corresponds to the accessed organization.
if (StringUtils.isNotEmpty(authorizedOrganization)) {
try {
startOrganizationBoundTenantFlow(authorizedOrganization);
getNext().invoke(request, response);
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
} else {
getNext().invoke(request, response);
}
} else {
APIErrorResponseHandler.handleErrorResponse(authenticationContext, response,
HttpServletResponse.SC_FORBIDDEN, null);
Expand Down Expand Up @@ -282,4 +296,17 @@ private boolean isAuthorizationSkipped(String authHandlerName, String requestUri
throw new AuthRuntimeException("Error normalizing URL path: " + requestUri, e);
}
}

private void startOrganizationBoundTenantFlow(String authorizedOrganization) {

PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setOrganizationId(authorizedOrganization);
try {
String authorizedTenantDomain = AuthorizationValveServiceHolder.getInstance().getOrganizationManager()
.resolveTenantDomain(authorizedOrganization);
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(authorizedTenantDomain, true);
} catch (OrganizationManagementException e) {
throw new AuthRuntimeException("Error while resolving tenant domain by organization.", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;

@Component(
name = "org.wso2.carbon.identity.authz.valve",
Expand Down Expand Up @@ -63,5 +64,24 @@ protected void unsetAuthorizationManager(AuthorizationManager authorizationManag
List<AuthorizationManager> authorizationManagerList = AuthorizationValveServiceHolder.getInstance().getAuthorizationManagerList();
authorizationManagerList.remove(authorizationManager);
}

@Reference(
name = "organization.service",
service = OrganizationManager.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetOrganizationManager"
)
protected void setOrganizationManager(OrganizationManager organizationManager) {

log.debug("Setting the organization management service.");
AuthorizationValveServiceHolder.getInstance().setOrganizationManager(organizationManager);
}

protected void unsetOrganizationManager(OrganizationManager organizationManager) {

log.debug("Unset organization management service.");
AuthorizationValveServiceHolder.getInstance().setOrganizationManager(null);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.wso2.carbon.identity.authz.valve.internal;

import org.wso2.carbon.identity.authz.service.AuthorizationManager;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -30,6 +31,7 @@ public class AuthorizationValveServiceHolder {

private static AuthorizationValveServiceHolder authorizationValveServiceHolder = new
AuthorizationValveServiceHolder();
private OrganizationManager organizationManager;

private List<AuthorizationManager> authorizationManagerList = new ArrayList<>();

Expand All @@ -47,4 +49,15 @@ public List<AuthorizationManager> getAuthorizationManagerList() {
public void setAuthorizationManagerList(List<AuthorizationManager> authorizationManagerList) {
this.authorizationManagerList = authorizationManagerList;
}

public OrganizationManager getOrganizationManager() {

return organizationManager;
}

public void setOrganizationManager(
OrganizationManager organizationManager) {

this.organizationManager = organizationManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
package org.wso2.carbon.identity.authz.valve.util;

import org.apache.catalina.connector.Request;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.application.common.model.User;
import org.wso2.carbon.identity.auth.service.AuthenticationContext;
import org.wso2.carbon.identity.auth.service.util.Constants;
Expand Down Expand Up @@ -50,6 +52,22 @@ public static String getTenantDomainFromURLMapping(Request request) {
return domain;
}

public static String getOrganizationIdFromURLMapping(Request request) {

String requestURI = request.getRequestURI();
String organizationId = StringUtils.EMPTY;

if (requestURI.contains("/o/")) {
String temp = requestURI.substring(requestURI.indexOf("/o/") + 3);
int index = temp.indexOf('/');
if (index != -1) {
temp = temp.substring(0, index);
organizationId = temp;
}
}
return organizationId;
}

/**
* Checks whether the tenantDomain from URL mapping and the tenantDomain get from the user name are same.
*
Expand All @@ -73,6 +91,19 @@ public static boolean isUserBelongsToRequestedTenant(AuthenticationContext authe
return tenantDomainFromURLMapping.equals(tenantDomain);
}

public static boolean isUserAuthorizedOrganization(AuthenticationContext authenticationContext, Request request) {

User user = authenticationContext.getUser();
if (user == null) {
return false;
}
String authorizedOrganization = ((AuthenticatedUser) user).getAccessingOrganization();
if (StringUtils.isNotEmpty(authorizedOrganization)) {
return getOrganizationIdFromURLMapping(request).equals(authorizedOrganization);
}
return isUserBelongsToRequestedTenant(authenticationContext, request);
}

/**
* Checks whether cross-tenant-access is allowed for the given tenant.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.MDC;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.base.IdentityRuntimeException;
Expand Down Expand Up @@ -92,6 +91,7 @@ public void invoke(Request request, Response response) throws IOException, Servl
boolean isContextRewrite = false;
boolean isWebApp = false;

String contextTenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
//Get the rewrite contexts and check whether request URI contains any of rewrite contains.
for (RewriteContext context : contextsToRewrite) {
Pattern patternTenant = context.getTenantContextPattern();
Expand All @@ -103,8 +103,7 @@ public void invoke(Request request, Response response) throws IOException, Servl
break;
} else if (isTenantQualifiedUrlsEnabled && (patternSuperTenant.matcher(requestURI).find() ||
patternSuperTenant.matcher(requestURI + "/").find())) {
IdentityUtil.threadLocalProperties.get().put(TENANT_NAME_FROM_CONTEXT, MultitenantConstants
.SUPER_TENANT_DOMAIN_NAME);
IdentityUtil.threadLocalProperties.get().put(TENANT_NAME_FROM_CONTEXT, contextTenantDomain);
break;
}
}
Expand Down Expand Up @@ -143,7 +142,7 @@ public void invoke(Request request, Response response) throws IOException, Servl
IdentityUtil.threadLocalProperties.get().put(TENANT_NAME_FROM_CONTEXT, tenantDomain);

if (isWebApp) {
String dispatchLocation = "/" + requestURI.replace("/t/" + tenantDomain + contextToForward, "");
String dispatchLocation = "/" + requestURI.replaceAll("/t/.*" + contextToForward, "");
if (contextListToOverwriteDispatch.contains(contextToForward) && !isIgnorePath(dispatchLocation)) {
dispatchLocation = "/";
}
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@
<org.wso2.carbon.identity.cors.valve.version>${project.version}</org.wso2.carbon.identity.cors.valve.version>

<!--Carbon identity version-->
<identity.framework.version>5.25.358</identity.framework.version>
<identity.framework.version>5.25.390-SNAPSHOT</identity.framework.version>
<carbon.identity.package.import.version.range>[5.17.8, 7.0.0)</carbon.identity.package.import.version.range>

<org.wso2.carbon.identity.oauth.version>6.11.128</org.wso2.carbon.identity.oauth.version>
Expand Down

0 comments on commit e4dc253

Please sign in to comment.