From ecb8ca89ee3da4ad46087470ad1067b16232823e Mon Sep 17 00:00:00 2001 From: Shan Chathusanda Jayathilaka Date: Fri, 1 Nov 2024 19:07:57 +0530 Subject: [PATCH] Allow sub organization applications to issue tokens to access the resources in sub organizations --- .../carbon/identity/oauth2/OAuth2Service.java | 16 ++++++++++- .../BasicAuthClientAuthenticator.java | 18 ++++++++++++- .../OAuthClientAuthnService.java | 14 ++++++++++ .../oauth2/dao/AccessTokenDAOImpl.java | 15 +++++++++-- .../oauth2/dao/TokenManagementDAOImpl.java | 18 +++++++++++-- .../oauth2/token/AccessTokenIssuer.java | 22 +++++++++++++-- .../identity/oauth2/token/JWTTokenIssuer.java | 27 +++++++++++++++++-- .../AbstractAuthorizationGrantHandler.java | 23 ++++++++++++++-- .../handlers/grant/PasswordGrantHandler.java | 22 ++++++++++++++- .../handlers/grant/RefreshGrantHandler.java | 10 ++++--- .../carbon/identity/oauth2/util/JWTUtils.java | 12 +++++++++ .../DefaultOAuth2ScopeValidator.java | 5 +++- .../validators/TokenValidationHandler.java | 12 +++++++++ .../openidconnect/DefaultIDTokenBuilder.java | 9 +++++-- 14 files changed, 203 insertions(+), 20 deletions(-) diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java index d32a2abc8c..231bedfa91 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.message.types.GrantType; import org.owasp.encoder.Encode; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.core.AbstractAdmin; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; @@ -63,6 +64,7 @@ import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinder; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import org.wso2.carbon.identity.openidconnect.model.Constants; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.user.api.Claim; import org.wso2.carbon.user.core.UserStoreManager; import org.wso2.carbon.utils.DiagnosticLog; @@ -981,7 +983,19 @@ public Claim[] getUserClaims(String accessTokenIdentifier) { public String getOauthApplicationState(String consumerKey) { try { - OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(consumerKey); + String tenantDomain = IdentityTenantUtil.getTenantDomain(IdentityTenantUtil.getLoginTenantId()); + String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(appOrgId)) { + try { + tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(appOrgId); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain for the organization ID: " + + appOrgId, e); + } + } + OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain); return appDO.getState(); } catch (IdentityOAuth2Exception e) { log.error("Error while finding application state for application with client_id: " + consumerKey, e); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java index 891fe5f1b1..4de98aa49a 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java @@ -24,13 +24,17 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.OAuth; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.oauth.IdentityOAuthAdminException; import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes; import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; +import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext; import org.wso2.carbon.identity.oauth2.model.ClientAuthenticationMethodModel; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import java.util.ArrayList; import java.util.Base64; @@ -89,8 +93,20 @@ public boolean authenticateClient(HttpServletRequest request, Map log.debug("Authenticating client : " + oAuthClientAuthnContext.getClientId() + " with client " + "secret."); } + String tenantDomain = IdentityTenantUtil.resolveTenantDomain(); + String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(appOrgId)) { + try { + tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(appOrgId); + } catch (OrganizationManagementException e) { + throw new InvalidOAuthClientException("Error while resolving tenant domain for the organization " + + "ID: " + appOrgId, e); + } + } return OAuth2Util.authenticateClient(oAuthClientAuthnContext.getClientId(), - (String) oAuthClientAuthnContext.getParameter(OAuth.OAUTH_CLIENT_SECRET)); + (String) oAuthClientAuthnContext.getParameter(OAuth.OAUTH_CLIENT_SECRET), tenantDomain); } catch (IdentityOAuthAdminException e) { throw new OAuthClientAuthnException("Error while authenticating client", OAuth2ErrorCodes.INVALID_CLIENT, e); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java index a36fe3f125..ebcfa6d88a 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java @@ -21,16 +21,19 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes; import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; +import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext; import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.oauth2.model.ClientAuthenticationMethodModel; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import java.util.ArrayList; import java.util.Arrays; @@ -321,6 +324,17 @@ private List getConfiguredClientAuthMethods(String cli throws OAuthClientAuthnException, InvalidOAuthClientException { String tenantDomain = IdentityTenantUtil.resolveTenantDomain(); + String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(appOrgId)) { + try { + tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(appOrgId); + } catch (OrganizationManagementException e) { + throw new InvalidOAuthClientException("Error while resolving tenant domain for the organization ID: " + + appOrgId, e); + } + } List configuredClientAuthMethods = new ArrayList<>(); try { OAuthAppDO oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, tenantDomain); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java index bc9cbdd406..f8f576cea4 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; @@ -251,6 +252,18 @@ private void insertAccessToken(String accessToken, String consumerKey, AccessTok insertTokenPrepStmt.setString(19, authorizedOrganization); int appTenantId = IdentityTenantUtil.getLoginTenantId(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + try { + String tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + appTenantId = OAuth2Util.getTenantId(tenantDomain); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: " + + applicationResidentOrgId, e); + } + } if (OAuth2ServiceComponentHolder.isIDPIdColumnEnabled()) { if (OAuth2ServiceComponentHolder.isConsentedTokenColumnEnabled()) { insertTokenPrepStmt.setString(20, Boolean.toString(accessTokenDO.isConsentedToken())); @@ -276,8 +289,6 @@ private void insertAccessToken(String accessToken, String consumerKey, AccessTok insertTokenPrepStmt.setInt(21, appTenantId); } } - insertTokenPrepStmt.executeUpdate(); - String accessTokenId = accessTokenDO.getTokenId(); addScopePrepStmt = connection.prepareStatement(sqlAddScopes); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java index aa7fe011a8..d91f83de13 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.tuple.Pair; 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.application.common.IdentityApplicationManagementException; import org.wso2.carbon.identity.application.common.model.ServiceProvider; @@ -163,7 +164,20 @@ public RefreshTokenValidationDataDO validateRefreshToken(String consumerKey, Str prepStmt = connection.prepareStatement(sql); prepStmt.setString(1, getPersistenceProcessor().getProcessedClientId(consumerKey)); - prepStmt.setInt(2, IdentityTenantUtil.getLoginTenantId()); + int tenantId = IdentityTenantUtil.getLoginTenantId(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + try { + String tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + tenantId = IdentityTenantUtil.getTenantId(tenantDomain); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: " + + applicationResidentOrgId, e); + } + } + prepStmt.setInt(2, tenantId); if (refreshToken != null) { prepStmt.setString(3, getHashingPersistenceProcessor().getProcessedRefreshToken(refreshToken)); } @@ -183,7 +197,7 @@ public RefreshTokenValidationDataDO validateRefreshToken(String consumerKey, Str validationDataDO.setAccessToken(resultSet.getString(1)); } String userName = resultSet.getString(2); - int tenantId = resultSet.getInt(3); + tenantId = resultSet.getInt(3); String userDomain = resultSet.getString(4); String tenantDomain = OAuth2Util.getTenantDomain(tenantId); validationDataDO.setRefreshToken(refreshToken); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java index 16f539f4ec..de5c399712 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java @@ -26,6 +26,7 @@ import org.apache.oltu.oauth2.common.error.OAuthError; import org.apache.oltu.oauth2.common.message.types.GrantType; import org.owasp.encoder.Encode; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; @@ -167,7 +168,7 @@ public static AccessTokenIssuer getInstance() throws IdentityOAuth2Exception { public OAuth2AccessTokenRespDTO issue(OAuth2AccessTokenReqDTO tokenReqDTO) throws IdentityException { - String grantType = tokenReqDTO.getGrantType(); + String grantType = tokenReqDTO.getGrantType(); // This is the place that we need to check the authorized user. OAuth2AccessTokenRespDTO tokenRespDTO = null; AuthorizationGrantHandler authzGrantHandler = authzGrantHandlers.get(grantType); @@ -308,6 +309,11 @@ public OAuth2AccessTokenRespDTO issue(OAuth2AccessTokenReqDTO tokenReqDTO) if (!isOfTypeApplicationUser) { tokReqMsgCtx.setAuthorizedUser(oAuthAppDO.getAppOwner()); tokReqMsgCtx.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + tokReqMsgCtx.getAuthorizedUser().setAccessingOrganization(applicationResidentOrgId); + } } else { tokReqMsgCtx.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER); } @@ -1355,7 +1361,19 @@ private void setResponseHeaders(OAuthTokenReqMessageContext tokReqMsgCtx, private OAuthAppDO getOAuthApplication(String consumerKey) throws InvalidOAuthClientException, IdentityOAuth2Exception { - OAuthAppDO authAppDO = OAuth2Util.getAppInformationByClientId(consumerKey); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + try { + tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: " + + applicationResidentOrgId, e); + } + } + OAuthAppDO authAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain); String appState = authAppDO.getState(); if (StringUtils.isEmpty(appState)) { if (log.isDebugEnabled()) { diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java index c7ac52fedb..6d1fe6c146 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java @@ -35,6 +35,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.base.IdentityConstants; @@ -54,6 +55,7 @@ import org.wso2.carbon.identity.openidconnect.CustomClaimsCallbackHandler; import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil; import org.wso2.carbon.identity.openidconnect.util.ClaimHandlerUtil; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import java.security.Key; import java.security.cert.Certificate; @@ -411,7 +413,11 @@ private String getSigningTenantDomain(String clientID, AuthenticatedUser authent throws IdentityOAuth2Exception { String tenantDomain; - if (OAuthServerConfiguration.getInstance().getUseSPTenantDomainValue()) { + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + } else if (OAuthServerConfiguration.getInstance().getUseSPTenantDomainValue()) { if (log.isDebugEnabled()) { log.debug("Using the tenant domain of the SP to sign the token"); } @@ -558,8 +564,20 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe // loading the stored application data OAuthAppDO oAuthAppDO; + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); try { - oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + try { + tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: " + + applicationResidentOrgId, e); + } + } + oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain); } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + consumerKey, e); } @@ -576,6 +594,11 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe spTenantDomain = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getTenantDomain(); } + // Need a proper way to handle this. + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + spTenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();; + } + boolean isMTLSrequest; if (authAuthzReqMessageContext != null) { /* If the auth request is originated from a request object reference(ex: PAR), then that endpoint should be diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java index c7bfab496b..d666da7322 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java @@ -64,6 +64,7 @@ import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeHandler; import org.wso2.carbon.identity.oauth2.validators.scope.ScopeValidator; import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.utils.DiagnosticLog; import java.sql.Timestamp; @@ -783,10 +784,19 @@ private OAuth2AccessTokenRespDTO createResponseWithTokenBean(AccessTokenDO exist OAuthAppDO oAuthAppDO; String consumerKey = existingAccessTokenDO.getConsumerKey(); try { - oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + } + oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain); } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while retrieving app information for client_id : " + consumerKey, e); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: ", e); } if (issueRefreshToken(existingAccessTokenDO.getTokenType()) && @@ -830,7 +840,14 @@ private OAuthCacheKey getOAuthCacheKey(String scope, String consumerKey, String private OAuthAppDO getoAuthApp(String consumerKey) throws IdentityOAuth2Exception { OAuthAppDO oAuthAppBean; try { - oAuthAppBean = OAuth2Util.getAppInformationByClientId(consumerKey); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + } + oAuthAppBean = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain); if (log.isDebugEnabled()) { log.debug("Service Provider specific expiry time enabled for application : " + consumerKey + ". Application access token expiry time : " + oAuthAppBean.getApplicationAccessTokenExpiryTime() @@ -839,6 +856,8 @@ private OAuthAppDO getoAuthApp(String consumerKey) throws IdentityOAuth2Exceptio } } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + consumerKey, e); + } catch (OrganizationManagementException e) { + throw new RuntimeException("Error while resolving tenant domain from the organization id: ", e); } return oAuthAppBean; } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java index 0bf8bb7c04..08390abf24 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.AuthenticationDataPublisher; import org.wso2.carbon.identity.application.authentication.framework.AuthenticationService; import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus; @@ -65,6 +66,7 @@ import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserStoreClientException; @@ -271,13 +273,25 @@ private void setPropertiesForTokenGeneration(OAuthTokenReqMessageContext tokReqM tokReqMsgCtx.setScope(tokenReq.getScope()); } - private String getFullQualifiedUsername(OAuth2AccessTokenReqDTO tokenReq, ServiceProvider serviceProvider) { + private String getFullQualifiedUsername(OAuth2AccessTokenReqDTO tokenReq, ServiceProvider serviceProvider) + throws IdentityOAuth2Exception { boolean isEmailUserNameEnabled = MultitenantUtils.isEmailUserName(); boolean isSaasApp = serviceProvider.isSaasApp(); boolean isLegacySaaSAuthenticationEnabled = IdentityTenantUtil.isLegacySaaSAuthenticationEnabled(); String usernameFromRequest = tokenReq.getResourceOwnerUsername(); String tenantDomainFromContext = IdentityTenantUtil.resolveTenantDomain(); + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + try { + tenantDomainFromContext = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager() + .resolveTenantDomain(applicationResidentOrgId); + } catch (OrganizationManagementException e) { + throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: " + + applicationResidentOrgId, e); + } + } if (!isSaasApp) { /* @@ -378,6 +392,12 @@ private AuthenticatedUser validateUserCredentials(OAuth2AccessTokenReqDTO tokenR tenantAwareUserName, isPublishPasswordGrantLoginEnabled, userTenantDomain, serviceProvider); } if (authenticatedUser.isPresent()) { + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + authenticatedUser.get().setAccessingOrganization(applicationResidentOrgId); + authenticatedUser.get().setUserResidentOrganization(applicationResidentOrgId); + } return authenticatedUser.get(); } triggerPasswordExpiryValidationEvent(PASSWORD_GRANT_POST_AUTHENTICATION_EVENT, tenantAwareUserName, diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java index 6e8c689684..404565eefc 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java @@ -511,11 +511,11 @@ private ResponseHeader[] getResponseHeaders(OAuthTokenReqMessageContext tokReqMs return respHeaders; } - private OAuthAppDO getOAuthApp(String clientId) throws IdentityOAuth2Exception { + private OAuthAppDO getOAuthApp(String clientId, String tenantDomain) throws IdentityOAuth2Exception { OAuthAppDO oAuthAppDO; try { - oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId); + oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, tenantDomain); } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + clientId, e); @@ -579,7 +579,8 @@ private void setTokenData(AccessTokenDO accessTokenDO, OAuthTokenReqMessageConte RefreshTokenValidationDataDO validationBean, OAuth2AccessTokenReqDTO tokenReq, Timestamp timestamp) throws IdentityOAuth2Exception { - OAuthAppDO oAuthAppDO = getOAuthApp(tokenReq.getClientId()); + OAuthAppDO oAuthAppDO = getOAuthApp(tokenReq.getClientId(), validationBean.getAuthorizedUser(). + getTenantDomain()); createTokens(accessTokenDO, tokReqMsgCtx); setRefreshTokenData(accessTokenDO, tokenReq, validationBean, oAuthAppDO, accessTokenDO.getRefreshToken(), timestamp, tokReqMsgCtx); @@ -799,7 +800,8 @@ private boolean checkExecutePreIssueAccessTokensActions(RefreshTokenValidationDa OAuthTokenReqMessageContext tokenReqMessageContext) throws IdentityOAuth2Exception { - OAuthAppDO oAuthAppBean = getOAuthApp(tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId()); + OAuthAppDO oAuthAppBean = getOAuthApp(tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId(), + refreshTokenValidationDataDO.getAuthorizedUser().getTenantDomain()); String grantType = refreshTokenValidationDataDO.getGrantType(); // Allow if refresh token is issued for token requests from following grant types and, diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java index 627c1de8e2..398ff12f8e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java @@ -30,6 +30,7 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty; import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; @@ -54,6 +55,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.text.ParseException; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -298,6 +300,16 @@ public static String getSigningTenantDomain(JWTClaimsSet claimsSet, AccessTokenD if (log.isDebugEnabled()) { log.debug("Getting signing tenant domain from OAuth app."); } + // Check if the OAuth application is a sub-organization application. Based on that we can define what + // is the tenant that signed the JWT. In this case the signing tenant is the root organization. + String appTenantDomain = IdentityTenantUtil.getTenantDomain(accessTokenDO.getTenantID()); + ServiceProviderProperty[] serviceProviderProperties = OAuth2Util.getServiceProvider( + accessTokenDO.getConsumerKey(), appTenantDomain).getSpProperties(); + if (serviceProviderProperties != null && Arrays.stream(serviceProviderProperties) + .anyMatch(property -> "isSubOrgApp".equals(property.getName()) + && Boolean.parseBoolean(property.getValue()))) { + return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + } return OAuth2Util.getTenantDomainOfOauthApp(accessTokenDO.getConsumerKey()); } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while getting tenant domain from OAuth app with consumer key: " diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java index 1025c723f3..ddfa2bf865 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java @@ -23,6 +23,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; @@ -130,7 +131,9 @@ public List validateScope(OAuthTokenReqMessageContext tokenReqMessageCon String clientId = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId(); String appId = getApplicationId(clientId, tenantDomain); // When user is not accessing the resident organization, resolve the application id from the shared app table. - if (!AuthzUtil.isUserAccessingResidentOrganization(tokenReqMessageContext.getAuthorizedUser())) { + if (!AuthzUtil.isUserAccessingResidentOrganization(tokenReqMessageContext.getAuthorizedUser()) && + StringUtils.isEmpty(PrivilegedCarbonContext.getThreadLocalCarbonContext(). + getApplicationResidentOrganizationId())) { String orgId = tokenReqMessageContext.getAuthorizedUser().getAccessingOrganization(); String appResideOrgId = resolveOrgIdByTenantDomain(tenantDomain); appId = SharedAppResolveDAO.resolveSharedApplication(appResideOrgId, appId, orgId); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java index fabb785dbb..3b1ebd4584 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java @@ -28,6 +28,7 @@ import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; import org.wso2.carbon.identity.application.common.model.ServiceProvider; import org.wso2.carbon.identity.application.mgt.ApplicationConstants; +import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty; import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; @@ -50,6 +51,7 @@ import org.wso2.carbon.utils.DiagnosticLog; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -507,6 +509,16 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(validationRequest.getAccessToken().getIdentifier(), false); + // Check if the OAuth application is a sub-organization application. Based on that we can define what + // is the tenant domain that should be used. + String appTenantDomain = IdentityTenantUtil.getTenantDomain(accessTokenDO.getTenantID()); + ServiceProviderProperty[] serviceProviderProperties = OAuth2Util.getServiceProvider( + accessTokenDO.getConsumerKey(), appTenantDomain).getSpProperties(); + if (serviceProviderProperties != null && Arrays.stream(serviceProviderProperties) + .anyMatch(property -> "isSubOrgApp".equals(property.getName()) + && Boolean.parseBoolean(property.getValue()))) { + tenantDomain = appTenantDomain; + } boolean isCrossTenantTokenIntrospectionAllowed = OAuthServerConfiguration.getInstance().isCrossTenantTokenIntrospectionAllowed(); if (!isCrossTenantTokenIntrospectionAllowed && accessTokenDO != null && diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java index 4387e23c98..9bfe7b39fc 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java @@ -30,6 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.AuthenticationMethodNameTranslator; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache; @@ -120,7 +121,7 @@ public String buildIDToken(OAuthTokenReqMessageContext tokenReqMsgCtxt, // Initialize OAuthAppDO using the client ID. OAuthAppDO oAuthAppDO; try { - oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId); + oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, spTenantDomain); } catch (InvalidOAuthClientException e) { String error = "Error occurred while getting app information for client_id: " + clientId; throw new IdentityOAuth2Exception(error, e); @@ -417,7 +418,11 @@ private JWTClaimsSet handleOIDCCustomClaims(OAuthTokenReqMessageContext tokReqMs private String getSigningTenantDomain(OAuthTokenReqMessageContext tokReqMsgCtx) { boolean isJWTSignedWithSPKey = OAuthServerConfiguration.getInstance().isJWTSignedWithSPKey(); - if (isJWTSignedWithSPKey) { + String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getApplicationResidentOrganizationId(); + if (StringUtils.isNotEmpty(applicationResidentOrgId)) { + return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + } else if (isJWTSignedWithSPKey) { return (String) tokReqMsgCtx.getProperty(MultitenantConstants.TENANT_DOMAIN); } else { return tokReqMsgCtx.getAuthorizedUser().getTenantDomain();