diff --git a/components/org.wso2.carbon.identity.oauth/pom.xml b/components/org.wso2.carbon.identity.oauth/pom.xml
index e7d21d8352e..b6e53a8ba50 100644
--- a/components/org.wso2.carbon.identity.oauth/pom.xml
+++ b/components/org.wso2.carbon.identity.oauth/pom.xml
@@ -329,6 +329,14 @@
jaxp-ri
test
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.api.resource.mgt
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.role.v2.mgt.core
+
@@ -423,6 +431,8 @@
org.wso2.carbon.utils.multitenancy;version="${carbon.kernel.imp.pkg.version.range}",
org.wso2.carbon.identity.multi.attribute.login.mgt.*;
version="${carbon.identity.framework.imp.pkg.version.range}",
+ org.wso2.carbon.identity.api.resource.mgt; version="${carbon.identity.framework.imp.pkg.version.range}",
+ org.wso2.carbon.identity.role.v2.mgt.core; version="${carbon.identity.framework.imp.pkg.version.range}"
!org.wso2.carbon.identity.oauth.internal,
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java
index 209f72f6336..52480685d0b 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java
@@ -31,6 +31,7 @@
import org.wso2.carbon.identity.oauth2.dao.TokenManagementDAO;
import org.wso2.carbon.identity.oauth2.token.handlers.response.AccessTokenResponseHandler;
import org.wso2.carbon.identity.oauth2.validators.scope.ScopeValidator;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService;
import org.wso2.carbon.identity.role.mgt.core.RoleManagementService;
@@ -57,6 +58,7 @@ public class OAuthComponentServiceHolder {
private List tokenBindingMetaDataDTOs = new ArrayList<>();
private OAuthAdminServiceImpl oAuthAdminService;
private List scopeValidators = new ArrayList<>();
+ private List scopeValidationHandlers = new ArrayList<>();
private Map oAuthApplicationMgtListeners = new TreeMap<>();
private RoleManagementService roleManagementService;
private OrganizationUserResidentResolverService organizationUserResidentResolverService;
@@ -105,6 +107,46 @@ public void setScopeValidators(List scopeValidators) {
this.scopeValidators = scopeValidators;
}
+ /**
+ * Get the list of scope validation handler implementations available.
+ *
+ * @return ScopeValidationHandler returns a list ot scope validation policy handler.
+ */
+ public List getScopeValidationHandlers() {
+
+ return scopeValidationHandlers;
+ }
+
+ /**
+ * Add scope validation handler implementation.
+ *
+ * @param scopeValidationHandler Scope validation handler implementation.
+ */
+ public void addScopeValidationHandler(ScopeValidationHandler scopeValidationHandler) {
+
+ scopeValidationHandlers.add(scopeValidationHandler);
+ }
+
+ /**
+ * Remove scope validation policy implementation.
+ *
+ * @param scopeValidationHandler Scope validation policy implementation.
+ */
+ public void removeScopeValidationHandler(ScopeValidationHandler scopeValidationHandler) {
+
+ scopeValidationHandlers.remove(scopeValidationHandler);
+ }
+
+ /**
+ * Set a list of scope validation handler implementations.
+ *
+ * @param scopeValidationHandlers List of Scope validation handler implementation.
+ */
+ public void setScopeValidatorPolicyHandlers(List scopeValidationHandlers) {
+
+ this.scopeValidationHandlers = scopeValidationHandlers;
+ }
+
private OAuthComponentServiceHolder() {
}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/AuthorizationHandlerManager.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/AuthorizationHandlerManager.java
index a02d0cd4aaa..ec24550a8f6 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/AuthorizationHandlerManager.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/AuthorizationHandlerManager.java
@@ -41,7 +41,9 @@
import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO;
import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeRespDTO;
import org.wso2.carbon.identity.oauth2.model.OAuth2Parameters;
+import org.wso2.carbon.identity.oauth2.util.AuthzUtil;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.oauth2.validators.DefaultOAuth2ScopeValidator;
import org.wso2.carbon.identity.oauth2.validators.JDBCPermissionBasedInternalScopeValidator;
import org.wso2.carbon.identity.oauth2.validators.RoleBasedInternalScopeValidator;
import org.wso2.carbon.utils.CarbonUtils;
@@ -276,15 +278,23 @@ private void validateRequestedScopes(OAuthAuthzReqMessageContext authzReqMsgCtx,
List requestedAllowedScopes = getAllowedScopesFromRequestedScopes(authzReqMsgCtx);
// Remove the system level allowed scopes from requested scopes for further validation.
removeAllowedScopesFromRequestedScopes(authzReqMsgCtx, requestedAllowedScopes);
- // If it is management app, we validate internal scopes in the requested scopes.
- String[] authorizedInternalScopes = new String[0];
- log.debug("Handling the internal scope validation.");
- authorizedInternalScopes = getAuthorizedInternalScopes(authzReqMsgCtx);
-
- // Remove the internal scopes from requested scopes for further validation.
- removeInternalScopesFromRequestedScopes(authzReqMsgCtx);
- // Adding the authorized internal scopes to tokReqMsgCtx for any special validators to use.
- authzReqMsgCtx.setAuthorizedInternalScopes(authorizedInternalScopes);
+ List authorizedScopes = null;
+ // Switch the scope validators dynamically based on the authorization runtime.
+ if (AuthzUtil.isLegacyAuthzRuntime()) {
+ // If it is management app, we validate internal scopes in the requested scopes.
+ String[] authorizedInternalScopes = new String[0];
+ log.debug("Handling the internal scope validation.");
+ authorizedInternalScopes = getAuthorizedInternalScopes(authzReqMsgCtx);
+
+ // Remove the internal scopes from requested scopes for further validation.
+ removeInternalScopesFromRequestedScopes(authzReqMsgCtx);
+ // Adding the authorized internal scopes to tokReqMsgCtx for any special validators to use.
+ authzReqMsgCtx.setAuthorizedInternalScopes(authorizedInternalScopes);
+ } else {
+ // Engage new scope validator
+ authorizedScopes = getAuthorizedScopes(authzReqMsgCtx);
+ removeAuthorizedScopesFromRequestedScopes(authzReqMsgCtx, authorizedScopes);
+ }
boolean isDropUnregisteredScopes = OAuthServerConfiguration.getInstance().isDropUnregisteredScopes();
if (isDropUnregisteredScopes) {
if (log.isDebugEnabled()) {
@@ -296,8 +306,12 @@ private void validateRequestedScopes(OAuthAuthzReqMessageContext authzReqMsgCtx,
boolean isValid = validateScopes(authzReqMsgCtx, authzHandler);
boolean isValidatedScopesContainsInRequestedScopes = isValidatedScopesContainsInRequestedScopes(authzReqMsgCtx);
if (isValid && isValidatedScopesContainsInRequestedScopes) {
- // Add authorized internal scopes to the request for sending in the response.
- addAuthorizedInternalScopes(authzReqMsgCtx, authzReqMsgCtx.getAuthorizedInternalScopes());
+ if (AuthzUtil.isLegacyAuthzRuntime()) {
+ // Add authorized internal scopes to the request for sending in the response.
+ addAuthorizedInternalScopes(authzReqMsgCtx, authzReqMsgCtx.getAuthorizedInternalScopes());
+ } else {
+ addAuthorizedScopes(authzReqMsgCtx, authorizedScopes);
+ }
// Add scopes that filtered from the allowed scopes list.
addAllowedScopes(authzReqMsgCtx, requestedAllowedScopes.toArray(new String[0]));
} else {
@@ -359,6 +373,19 @@ private String[] getAuthorizedInternalScopes(OAuthAuthzReqMessageContext authzRe
return authorizedInternalScopes;
}
+ /**
+ * get authorized scopes.
+ *
+ * @param authzReqMsgCtx authzReqMsgCtx
+ * @return - authorizedInternalScopes scopes list
+ */
+ private List getAuthorizedScopes(OAuthAuthzReqMessageContext authzReqMsgCtx)
+ throws IdentityOAuth2Exception {
+
+ DefaultOAuth2ScopeValidator scopeValidator = new DefaultOAuth2ScopeValidator();
+ return scopeValidator.validateScope(authzReqMsgCtx);
+ }
+
/**
* Eemove internal scopes from requested scopes.
*
@@ -379,6 +406,27 @@ private void removeInternalScopesFromRequestedScopes(OAuthAuthzReqMessageContext
authzReqMsgCtx.getAuthorizationReqDTO().setScopes(scopes.toArray(new String[0]));
}
+ /**
+ * Remove authorized scopes from requested scopes.
+ *
+ * @param authzReqMsgCtx authzReqMsgCtx
+ * @param authorizedScopes Authorized Scopes
+ */
+ private void removeAuthorizedScopesFromRequestedScopes(OAuthAuthzReqMessageContext authzReqMsgCtx,
+ List authorizedScopes) {
+
+ if (authzReqMsgCtx.getAuthorizationReqDTO().getScopes() == null) {
+ return;
+ }
+ List scopes = new ArrayList<>();
+ for (String scope : authzReqMsgCtx.getAuthorizationReqDTO().getScopes()) {
+ if (!authorizedScopes.contains(scope) && !scope.equalsIgnoreCase(SYSTEM_SCOPE)) {
+ scopes.add(scope);
+ }
+ }
+ authzReqMsgCtx.getAuthorizationReqDTO().setScopes(scopes.toArray(new String[0]));
+ }
+
/**
* Remove the system level allowed scopes from requested scopes.
*
@@ -419,6 +467,14 @@ private void addAuthorizedInternalScopes(OAuthAuthzReqMessageContext authzReqMsg
authzReqMsgCtx.setApprovedScope(scopesToReturn);
}
+ private void addAuthorizedScopes(OAuthAuthzReqMessageContext authzReqMsgCtx, List authorizedScopes) {
+
+ String[] scopes = authzReqMsgCtx.getApprovedScope();
+ String[] scopesToReturn = (String[]) ArrayUtils.addAll(scopes, authorizedScopes.toArray());
+ authzReqMsgCtx.setApprovedScope(scopesToReturn);
+ }
+
+
private void addRequestedOIDCScopes(OAuthAuthzReqMessageContext authzReqMsgCtx,
String[] requestedOIDCScopes) {
Set scopesToReturn = new HashSet<>(Arrays.asList(authzReqMsgCtx.getApprovedScope()));
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java
index 320cc873477..cab7d829939 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java
@@ -31,11 +31,13 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceManager;
import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticationService;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticationDataPublisher;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticationMethodNameTranslator;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementService;
import org.wso2.carbon.identity.application.mgt.listener.ApplicationMgtListener;
import org.wso2.carbon.identity.consent.server.configs.mgt.services.ConsentServerConfigsManagementService;
import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager;
@@ -80,6 +82,10 @@
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.identity.oauth2.validators.scope.RoleBasedScopeIssuer;
import org.wso2.carbon.identity.oauth2.validators.scope.ScopeValidator;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.impl.M2MScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.impl.NoPolicyScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.impl.RoleBasedScopeValidationHandler;
import org.wso2.carbon.identity.openidconnect.OpenIDConnectClaimFilter;
import org.wso2.carbon.identity.openidconnect.OpenIDConnectClaimFilterImpl;
import org.wso2.carbon.identity.openidconnect.dao.ScopeClaimMappingDAO;
@@ -88,6 +94,7 @@
import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService;
+import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService;
import org.wso2.carbon.identity.user.store.configuration.listener.UserStoreConfigListener;
import org.wso2.carbon.idp.mgt.IdpManager;
import org.wso2.carbon.registry.core.service.RegistryService;
@@ -332,6 +339,9 @@ protected void activate(ComponentContext context) {
bundleContext.registerService(OAuth2ScopeService.class.getName(), oAuth2ScopeService, null);
// Registering OAuth2ScopeService under ScopeService interface as the default service.
bundleContext.registerService(ScopeMetadataService.class, oAuth2ScopeService, null);
+ bundleContext.registerService(ScopeValidationHandler.class, new RoleBasedScopeValidationHandler(), null);
+ bundleContext.registerService(ScopeValidationHandler.class, new NoPolicyScopeValidationHandler(), null);
+ bundleContext.registerService(ScopeValidationHandler.class, new M2MScopeValidationHandler(), null);
// Note : DO NOT add any activation related code below this point,
// to make sure the server doesn't start up if any activation failures occur
@@ -607,6 +617,29 @@ protected void removeScopeValidatorService(ScopeValidator scopeValidator) {
OAuthComponentServiceHolder.getInstance().removeScopeValidator(scopeValidator);
}
+ @Reference(
+ name = "scope.validator.handler",
+ service = ScopeValidationHandler.class,
+ cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "removeScopeValidationHandler"
+ )
+ protected void addScopeValidationHandler(ScopeValidationHandler scopeValidationHandler) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Adding the Scope validation handler Service : " + scopeValidationHandler.getName());
+ }
+ OAuthComponentServiceHolder.getInstance().addScopeValidationHandler(scopeValidationHandler);
+ }
+
+ protected void removeScopeValidationHandler(ScopeValidationHandler scopeValidationHandler) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Removing the Scope validator Service : " + scopeValidationHandler.getName());
+ }
+ OAuthComponentServiceHolder.getInstance().removeScopeValidationHandler(scopeValidationHandler);
+ }
+
@Reference(
name = "IdentityProviderManager",
service = org.wso2.carbon.idp.mgt.IdpManager.class,
@@ -1222,4 +1255,85 @@ protected void unsetRealmService(RealmService realmService) {
OAuth2ServiceComponentHolder.getInstance().setRealmService(null);
}
+
+ @Reference(
+ name = "identity.authorized.api.management.component",
+ service = AuthorizedAPIManagementService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetAuthorizedAPIManagementService"
+ )
+ protected void setAuthorizedAPIManagementService(AuthorizedAPIManagementService authorizedAPIManagementService) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Adding Authorized API Management Service: " + authorizedAPIManagementService.getClass()
+ .getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance()
+ .setAuthorizedAPIManagementService(authorizedAPIManagementService);
+ }
+
+ protected void unsetAuthorizedAPIManagementService(AuthorizedAPIManagementService authorizedAPIManagementService) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Removing Authorized API Management Service: " + authorizedAPIManagementService.getClass()
+ .getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance().setAuthorizedAPIManagementService(null);
+ }
+
+ @Reference(
+ name = "api.resource.mgt.service.component",
+ service = APIResourceManager.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetAPIResourceManagerService"
+ )
+ protected void setAPIResourceManagerService(APIResourceManager apiResourceManager) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Adding API Resource Manager: " + apiResourceManager.getClass().getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance().setApiResourceManager(apiResourceManager);
+ }
+ protected void unsetAPIResourceManagerService(APIResourceManager apiResourceManager) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Removing API Resource Manager: " + apiResourceManager.getClass().getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance().setApiResourceManager(null);
+ }
+
+ /**
+ * Set role management service V2 implementation.
+ *
+ * @param roleManagementService RoleManagementServiceV2.
+ */
+ @Reference(
+ name = "org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService",
+ service = org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetRoleManagementServiceV2")
+ protected void setRoleManagementServiceV2(RoleManagementService roleManagementService) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Adding Role Management Service V2: " + roleManagementService.getClass().getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance().setRoleManagementServiceV2(roleManagementService);
+ }
+
+ /**
+ * Unset role management service V2 implementation.
+ *
+ * @param roleManagementService RoleManagementServiceV2
+ */
+ protected void unsetRoleManagementServiceV2(RoleManagementService roleManagementService) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Removing Role Management Service V2: " + roleManagementService.getClass().getName());
+ }
+ OAuth2ServiceComponentHolder.getInstance().setRoleManagementServiceV2(null);
+ }
+
}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java
index cdf408a151e..fde082095a8 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java
@@ -18,10 +18,12 @@
package org.wso2.carbon.identity.oauth2.internal;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceManager;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticationDataPublisher;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticationMethodNameTranslator;
import org.wso2.carbon.identity.application.authentication.framework.UserSessionManagementService;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementService;
import org.wso2.carbon.identity.consent.server.configs.mgt.services.ConsentServerConfigsManagementService;
import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager;
import org.wso2.carbon.identity.core.handler.HandlerComparator;
@@ -48,6 +50,7 @@
import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService;
+import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService;
import org.wso2.carbon.idp.mgt.IdpManager;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.core.service.RealmService;
@@ -104,6 +107,9 @@ public class OAuth2ServiceComponentHolder {
private RefreshTokenGrantProcessor refreshTokenGrantProcessor;
private OAuth2RevocationProcessor revocationProcessor;
private AccessTokenProvider accessTokenProvider;
+ private AuthorizedAPIManagementService authorizedAPIManagementService;
+ private APIResourceManager apiResourceManager;
+ private RoleManagementService roleManagementServiceV2;
private OAuth2ServiceComponentHolder() {
@@ -738,4 +744,52 @@ public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {
this.accessTokenProvider = accessTokenProvider;
}
+
+
+ public AuthorizedAPIManagementService getAuthorizedAPIManagementService() {
+
+ return authorizedAPIManagementService;
+ }
+
+ public void setAuthorizedAPIManagementService(AuthorizedAPIManagementService authorizedAPIManagementService) {
+
+ this.authorizedAPIManagementService = authorizedAPIManagementService;
+ }
+
+ /**
+ * Get APIResourceManager osgi service.
+ *
+ * @return APIResourceManager.
+ */
+ public APIResourceManager getApiResourceManager() {
+ return apiResourceManager;
+ }
+ /**
+ * Set APIResourceManager osgi service.
+ *
+ * @param apiResourceManager APIResourceManager.
+ */
+ public void setApiResourceManager(APIResourceManager apiResourceManager) {
+ this.apiResourceManager = apiResourceManager;
+ }
+
+ /**
+ * Get {@link RoleManagementService}.
+ *
+ * @return Instance of {@link RoleManagementService}.
+ */
+ public RoleManagementService getRoleManagementServiceV2() {
+
+ return roleManagementServiceV2;
+ }
+
+ /**
+ * Set {@link RoleManagementService}.
+ *
+ * @param roleManagementServiceV2 Instance of {@link RoleManagementService}.
+ */
+ public void setRoleManagementServiceV2(RoleManagementService roleManagementServiceV2) {
+
+ this.roleManagementServiceV2 = roleManagementServiceV2;
+ }
}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/AccessTokenExtendedAttributes.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/AccessTokenExtendedAttributes.java
index 0b13bdf76e5..7e6854e1733 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/AccessTokenExtendedAttributes.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/AccessTokenExtendedAttributes.java
@@ -18,8 +18,8 @@
package org.wso2.carbon.identity.oauth2.model;
-import com.hazelcast.com.fasterxml.jackson.annotation.JsonIgnore;
-import com.hazelcast.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.io.Serializable;
import java.util.Map;
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 c442520c1c3..2e1398b7782 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
@@ -66,7 +66,9 @@
import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding;
import org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationGrantHandler;
import org.wso2.carbon.identity.oauth2.token.handlers.response.AccessTokenResponseHandler;
+import org.wso2.carbon.identity.oauth2.util.AuthzUtil;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.oauth2.validators.DefaultOAuth2ScopeValidator;
import org.wso2.carbon.identity.oauth2.validators.JDBCPermissionBasedInternalScopeValidator;
import org.wso2.carbon.identity.oauth2.validators.RoleBasedInternalScopeValidator;
import org.wso2.carbon.identity.openidconnect.IDTokenBuilder;
@@ -645,6 +647,7 @@ private boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) throws I
List requestedAllowedScopes = new ArrayList<>();
String[] authorizedInternalScopes = new String[0];
String[] requestedScopes = tokReqMsgCtx.getScope();
+ List authorizedScopes = null;
if (GrantType.CLIENT_CREDENTIALS.toString().equals(grantType) && !isManagementApp) {
log.debug("Application is not configured as Management App and the grant type is client credentials. " +
"Hence skipping internal scope validation to stop issuing internal scopes for the client : " +
@@ -676,15 +679,24 @@ private boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) throws I
if (log.isDebugEnabled()) {
log.debug("Handling the internal scope validation.");
}
- // Execute Internal SCOPE Validation.
- JDBCPermissionBasedInternalScopeValidator scopeValidator = new JDBCPermissionBasedInternalScopeValidator();
- authorizedInternalScopes = scopeValidator.validateScope(tokReqMsgCtx);
- // Execute internal console scopes validation.
- if (IdentityUtil.isSystemRolesEnabled()) {
- RoleBasedInternalScopeValidator roleBasedInternalScopeValidator = new RoleBasedInternalScopeValidator();
- String[] roleBasedInternalConsoleScopes = roleBasedInternalScopeValidator.validateScope(tokReqMsgCtx);
- authorizedInternalScopes = (String[]) ArrayUtils
- .addAll(authorizedInternalScopes, roleBasedInternalConsoleScopes);
+ // Switch the scope validators dynamically based on the authorization runtime.
+ if (AuthzUtil.isLegacyAuthzRuntime()) {
+ // Execute Internal SCOPE Validation.
+ JDBCPermissionBasedInternalScopeValidator scopeValidator =
+ new JDBCPermissionBasedInternalScopeValidator();
+ authorizedInternalScopes = scopeValidator.validateScope(tokReqMsgCtx);
+ // Execute internal console scopes validation.
+ if (IdentityUtil.isSystemRolesEnabled()) {
+ RoleBasedInternalScopeValidator roleBasedInternalScopeValidator =
+ new RoleBasedInternalScopeValidator();
+ String[] roleBasedInternalConsoleScopes = roleBasedInternalScopeValidator
+ .validateScope(tokReqMsgCtx);
+ authorizedInternalScopes = (String[]) ArrayUtils
+ .addAll(authorizedInternalScopes, roleBasedInternalConsoleScopes);
+ }
+ } else {
+ // Engage new scope validator
+ authorizedScopes = getAuthorizedScopes(tokReqMsgCtx);
}
if (isManagementApp && GrantType.CLIENT_CREDENTIALS.toString().equals(grantType) &&
ArrayUtils.contains(requestedScopes, SYSTEM_SCOPE)) {
@@ -705,10 +717,15 @@ private boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) throws I
Those scopes should not send to the other scopes validators. Thus remove the scopes from the tokReqMsgCtx.
Will be added to the response after executing the other scope validators.
*/
- removeInternalScopes(tokReqMsgCtx);
+ if (AuthzUtil.isLegacyAuthzRuntime()) {
+ removeInternalScopes(tokReqMsgCtx);
+
+ // Adding the authorized internal scopes to tokReqMsgCtx for any special validators to use.
+ tokReqMsgCtx.setAuthorizedInternalScopes(authorizedInternalScopes);
+ } else {
+ removeAuthorizedScopes(tokReqMsgCtx, authorizedScopes);
+ }
- // Adding the authorized internal scopes to tokReqMsgCtx for any special validators to use.
- tokReqMsgCtx.setAuthorizedInternalScopes(authorizedInternalScopes);
boolean isDropUnregisteredScopes = OAuthServerConfiguration.getInstance().isDropUnregisteredScopes();
if (isDropUnregisteredScopes) {
@@ -725,7 +742,11 @@ private boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) throws I
boolean isValidScope = authzGrantHandler.validateScope(tokReqMsgCtx);
if (isValidScope) {
// Add authorized internal scopes to the request for sending in the response.
- addAuthorizedInternalScopes(tokReqMsgCtx, tokReqMsgCtx.getAuthorizedInternalScopes());
+ if (AuthzUtil.isLegacyAuthzRuntime()) {
+ addAuthorizedInternalScopes(tokReqMsgCtx, tokReqMsgCtx.getAuthorizedInternalScopes());
+ } else {
+ addAuthorizedScopes(tokReqMsgCtx, authorizedScopes);
+ }
addAllowedScopes(tokReqMsgCtx, requestedAllowedScopes.toArray(new String[0]));
if (LoggerUtils.isDiagnosticLogsEnabled()) {
LoggerUtils.triggerDiagnosticLogEvent(new DiagnosticLog.DiagnosticLogBuilder(
@@ -744,6 +765,13 @@ private boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) throws I
return isValidScope;
}
+ private List getAuthorizedScopes(OAuthTokenReqMessageContext tokReqMsgCtx)
+ throws IdentityOAuth2Exception {
+
+ DefaultOAuth2ScopeValidator scopeValidator = new DefaultOAuth2ScopeValidator();
+ return scopeValidator.validateScope(tokReqMsgCtx);
+ }
+
private List getScopeList(String[] scopes) {
return ArrayUtils.isEmpty(scopes) ? Collections.emptyList() : Arrays.asList(scopes);
@@ -925,6 +953,19 @@ private void addAuthorizedInternalScopes(OAuthTokenReqMessageContext tokReqMsgCt
.distinct().toArray(String[]::new));
}
+ private void addAuthorizedScopes(OAuthTokenReqMessageContext tokReqMsgCtx, List authorizedScopes) {
+
+ String[] scopes = tokReqMsgCtx.getScope();
+ if (scopes == null) {
+ scopes = new String[0];
+ }
+ if (authorizedScopes == null) {
+ authorizedScopes = new ArrayList<>();
+ }
+ tokReqMsgCtx.setScope(Stream.concat(Arrays.stream(scopes), authorizedScopes.stream())
+ .distinct().toArray(String[]::new));
+ }
+
private void addRequestedOIDCScopes(OAuthTokenReqMessageContext tokReqMsgCtx,
String[] requestedOIDCScopes) {
@@ -960,6 +1001,20 @@ private void removeInternalScopes(OAuthTokenReqMessageContext tokReqMsgCtx) {
tokReqMsgCtx.setScope(scopes.toArray(new String[0]));
}
+ private void removeAuthorizedScopes(OAuthTokenReqMessageContext tokReqMsgCtx, List authorizedScopes) {
+
+ if (tokReqMsgCtx.getScope() == null) {
+ return;
+ }
+ List scopes = new ArrayList<>();
+ for (String scope : tokReqMsgCtx.getScope()) {
+ if (!authorizedScopes.contains(scope) && !scope.equalsIgnoreCase(SYSTEM_SCOPE)) {
+ scopes.add(scope);
+ }
+ }
+ tokReqMsgCtx.setScope(scopes.toArray(new String[0]));
+ }
+
/**
* Handle token binding for the grant type.
*
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java
new file mode 100644
index 00000000000..9fdc6b7893d
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.CarbonConstants;
+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.authentication.framework.util.FrameworkConstants;
+import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
+import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
+import org.wso2.carbon.identity.application.common.model.ClaimMapping;
+import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder;
+import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
+import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
+import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException;
+import org.wso2.carbon.user.api.UserStoreException;
+import org.wso2.carbon.user.api.UserStoreManager;
+import org.wso2.carbon.user.core.NotImplementedException;
+import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
+import org.wso2.carbon.user.core.common.Group;
+import org.wso2.carbon.user.core.service.RealmService;
+import org.wso2.carbon.user.core.util.UserCoreUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.APPLICATION;
+import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.ORGANIZATION;
+import static org.wso2.carbon.user.core.UserCoreConstants.APPLICATION_DOMAIN;
+import static org.wso2.carbon.user.core.UserCoreConstants.INTERNAL_DOMAIN;
+
+/**
+ * Utility methods for the authorization related functionality.
+ */
+public class AuthzUtil {
+
+ private static final Log LOG = LogFactory.getLog(AuthzUtil.class);
+
+ /**
+ * Get the user roles.
+ *
+ * @param authenticatedUser AuthenticatedUser.
+ * @return User roles.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving user roles.
+ */
+ public static List getUserRoles(AuthenticatedUser authenticatedUser, String appId)
+ throws IdentityOAuth2Exception {
+
+ if (authenticatedUser.isFederatedUser()) {
+ if (StringUtils.isNotBlank(authenticatedUser.getAccessingOrganization())) {
+ if (!authenticatedUser.getAccessingOrganization()
+ .equals(authenticatedUser.getUserResidentOrganization())) {
+ // Handle switching organization scenario.
+ return getSwitchUserRoles(authenticatedUser);
+ }
+ }
+ // Handler federated user scenario.
+ return getFederatedUserRoles(authenticatedUser, appId);
+ }
+ if (StringUtils.isNotBlank(authenticatedUser.getAccessingOrganization())) {
+ if (!authenticatedUser.getAccessingOrganization()
+ .equals(authenticatedUser.getUserResidentOrganization())) {
+ return getSwitchUserRoles(authenticatedUser);
+ }
+ }
+ return getRoles(getUserId(authenticatedUser), authenticatedUser.getTenantDomain());
+ }
+
+ /**
+ * Get switching user roles.
+ *
+ * @param authenticatedUser Authenticated User.
+ * @return Switching user roles.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of switching user.
+ */
+ private static List getSwitchUserRoles(AuthenticatedUser authenticatedUser) throws IdentityOAuth2Exception {
+
+ String accessingTenantDomain = getAccessingTenantDomain(authenticatedUser);
+ String accessingUserId = getAccessingUserId(authenticatedUser);
+ return getRoles(accessingUserId, accessingTenantDomain);
+ }
+
+ /**
+ * Get the role ids.
+ *
+ * @param userId User ID.
+ * @param tenantDomain Tenant domain.
+ * @return Role ids of user.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of user.
+ */
+ private static List getRoles(String userId, String tenantDomain) throws IdentityOAuth2Exception {
+
+ List roleIds = new ArrayList<>(getRoleIdsOfUser(userId, tenantDomain));
+ List groups = getUserGroups(userId, tenantDomain);
+ if (!groups.isEmpty()) {
+ roleIds.addAll(getRoleIdsOfGroups(groups, tenantDomain));
+ }
+ return roleIds;
+ }
+
+ /**
+ * Get the federated role ids.
+ *
+ * @param authenticatedUser Authenticated user.
+ * @return Federated role ids of user.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of user.
+ */
+ private static List getFederatedUserRoles(AuthenticatedUser authenticatedUser, String appId)
+ throws IdentityOAuth2Exception {
+
+ String tenantDomain = authenticatedUser.getTenantDomain();
+ String roleNamesString = null;
+ Map claimMappingStringMap = authenticatedUser.getUserAttributes();
+ if (claimMappingStringMap == null) {
+ return new ArrayList<>();
+ }
+ for (Map.Entry entry : claimMappingStringMap.entrySet()) {
+ if (FrameworkConstants.LOCAL_ROLE_CLAIM_URI.equals(entry.getKey().getLocalClaim().getClaimUri())) {
+ roleNamesString = entry.getValue();
+ break;
+ }
+ }
+ List roleNames = null;
+ if (StringUtils.isNotBlank(roleNamesString)) {
+ roleNames = Arrays.asList(roleNamesString.split(FrameworkUtils.getMultiAttributeSeparator()));
+ }
+ if (roleNames == null || roleNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ String allowedAppAudience = getApplicationAllowedAudience(appId, tenantDomain);
+ if (ORGANIZATION.equalsIgnoreCase(allowedAppAudience)) {
+
+ return getRoleIdsFromNames(roleNames, ORGANIZATION, getOrganizationId(tenantDomain), tenantDomain);
+ }
+ return getRoleIdsFromNames(roleNames, APPLICATION, appId, tenantDomain);
+ }
+
+ /**
+ * Get accessing tenant domain of authenticated user.
+ *
+ * @param authenticatedUser Authenticated user.
+ * @return Accessing tenant domain.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving accessing tenant domain.
+ */
+ private static String getAccessingTenantDomain(AuthenticatedUser authenticatedUser) throws IdentityOAuth2Exception {
+ try {
+ return OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+ .resolveTenantDomain(authenticatedUser.getAccessingOrganization());
+ } catch (OrganizationManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving accessing tenant domain", e);
+ }
+ }
+
+ /**
+ * Get accessing user id of authenticated user.
+ *
+ * @param authenticatedUser Authenticated user.
+ * @return Accessing tenant domain.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving accessing user id.
+ */
+ private static String getAccessingUserId(AuthenticatedUser authenticatedUser) throws IdentityOAuth2Exception {
+
+ // TODO: resolve accessing user id.
+ return getUserId(authenticatedUser);
+ }
+
+ /**
+ * Get the associated scopes for the roles.
+ *
+ * @param roles Roles.
+ * @param tenantDomain Tenant domain.
+ * @return List of associated scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving scope list of roles.
+ */
+ public static List getAssociatedScopesForRoles(List roles, String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ try {
+ return OAuth2ServiceComponentHolder.getInstance().getRoleManagementServiceV2()
+ .getPermissionListOfRoles(roles, tenantDomain);
+ } catch (IdentityRoleManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving scope list of roles : "
+ + StringUtils.join(roles, ",") + "tenant domain : " + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Get the role ids of user.
+ *
+ * @param userId User ID.
+ * @param tenantDomain Tenant domain.
+ * @return Role ids of user.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of user.
+ */
+ private static List getRoleIdsOfUser(String userId, String tenantDomain) throws IdentityOAuth2Exception {
+
+ try {
+ return OAuth2ServiceComponentHolder.getInstance().getRoleManagementServiceV2()
+ .getRoleIdListOfUser(userId, tenantDomain);
+ } catch (IdentityRoleManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving role id list of user : " + userId
+ + "tenant domain : " + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Get user id of the user
+ *
+ * @param authenticatedUser Authenticated user.
+ * @return User id.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving user id of user.
+ */
+ private static String getUserId(AuthenticatedUser authenticatedUser) throws IdentityOAuth2Exception {
+
+ try {
+ return authenticatedUser.getUserId();
+ } catch (UserIdNotFoundException e) {
+ throw new IdentityOAuth2Exception("Error while resolving user id of user" , e);
+ }
+ }
+
+ /**
+ * Get organization id
+ *
+ * @param tenantDomain Tenant domain.
+ * @return Organization Id.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving org id.
+ */
+ private static String getOrganizationId(String tenantDomain) throws IdentityOAuth2Exception {
+
+ try {
+ return OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+ .resolveOrganizationId(tenantDomain);
+ } catch (OrganizationManagementException e) {
+ throw new IdentityOAuth2Exception("Error while resolving org id of tenant : " + tenantDomain , e);
+ }
+ }
+
+ /**
+ * Get application allowed audience.
+ *
+ * @param appId App id.
+ * @param tenantDomain Tenant domain.
+ * @return Allowed audience of app.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving allowed audience of app.
+ */
+ private static String getApplicationAllowedAudience(String appId, String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ try {
+ return OAuth2ServiceComponentHolder.getApplicationMgtService()
+ .getAllowedAudienceForRoleAssociation(appId, tenantDomain);
+ } catch (IdentityApplicationManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving allowed audience of app : " + appId , e);
+ }
+ }
+
+ /**
+ * Get the role ids from role names.
+ *
+ * @param roleNames Role names.
+ * @param tenantDomain Tenant domain.
+ * @param roleAudience Role audience.
+ * @param roleAudienceId Role audience id.
+ * @return Role ids of idp groups.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of idp groups.
+ */
+ private static List getRoleIdsFromNames(List roleNames, String roleAudience, String roleAudienceId,
+ String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ List roleIds = new ArrayList<>();
+ try {
+ for (String roleName: roleNames) {
+ roleIds.add(OAuth2ServiceComponentHolder.getInstance().getRoleManagementServiceV2()
+ .getRoleIdByName(roleName, roleAudience, roleAudienceId, tenantDomain));
+ }
+ } catch (IdentityRoleManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving role ids of list of role anme : "
+ + StringUtils.join(roleNames, ",") + "tenant domain : " + tenantDomain, e);
+ }
+ return roleIds;
+ }
+
+ /**
+ * Get the groups of the authenticated user.
+ *
+ * @param userId User id.
+ * @param tenantDomain Tenant domain.
+ * @return - Groups of the user.
+ */
+ private static List getUserGroups(String userId, String tenantDomain) throws IdentityOAuth2Exception {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Started group fetching for scope validation.");
+ }
+ List userGroups = new ArrayList<>();
+ RealmService realmService = UserCoreUtil.getRealmService();
+ try {
+ int tenantId = OAuth2Util.getTenantId(tenantDomain);
+ UserStoreManager userStoreManager = realmService.getTenantUserRealm(tenantId).getUserStoreManager();
+ List groups =
+ ((AbstractUserStoreManager) userStoreManager).getGroupListOfUser(userId,
+ null, null);
+ for (Group group : groups) {
+ String groupName = group.getGroupName();
+ String groupDomainName = UserCoreUtil.extractDomainFromName(groupName);
+ if (!INTERNAL_DOMAIN.equalsIgnoreCase(groupDomainName) &&
+ !APPLICATION_DOMAIN.equalsIgnoreCase(groupDomainName)) {
+ userGroups.add(group.getGroupID());
+ }
+ }
+ } catch (IdentityOAuth2Exception e) {
+ throw new IdentityOAuth2Exception(e.getMessage(), e);
+ } catch (UserStoreException e) {
+ if (isDoGetGroupListOfUserNotImplemented(e)) {
+ return userGroups;
+ }
+ throw new IdentityOAuth2Exception(e.getMessage(), e);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Completed group fetching for scope validation.");
+ }
+ return userGroups;
+ }
+
+ /**
+ * Get the role ids of groups.
+ *
+ * @param groups Groups.
+ * @param tenantDomain Tenant domain.
+ * @return Role ids of groups.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving role id list of groups.
+ */
+ private static List getRoleIdsOfGroups(List groups, String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ try {
+ return OAuth2ServiceComponentHolder.getInstance().getRoleManagementServiceV2()
+ .getRoleIdListOfGroups(groups, tenantDomain);
+ } catch (IdentityRoleManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving role id list of groups : "
+ + StringUtils.join(groups, ",") + "tenant domain : " + tenantDomain, e);
+ }
+ }
+
+
+ /**
+ * Check if the UserStoreException occurred due to the doGetGroupListOfUser method not being implemented.
+ *
+ * @param e UserStoreException.
+ * @return true if the UserStoreException was caused by the doGetGroupListOfUser method not being implemented,
+ * false otherwise.
+ */
+ private static boolean isDoGetGroupListOfUserNotImplemented(UserStoreException e) {
+
+ Throwable cause = e.getCause();
+ while (cause != null) {
+ if (cause instanceof NotImplementedException) {
+ return true;
+ }
+ cause = cause.getCause();
+ }
+ return false;
+ }
+
+ /**
+ * Check whether legacy authorization runtime is enabled.
+ *
+ * @return True if legacy authorization runtime is enabled.
+ */
+ public static boolean isLegacyAuthzRuntime() {
+
+ return CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java
index eeda5802802..1637d7d6217 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java
@@ -5011,7 +5011,7 @@ public static String[] extractCredentialsFromAuthzHeader(HttpServletRequest requ
return OAuthUtils.decodeClientAuthenticationHeader(authorizationHeader);
}
-
+
/**
* Retrieve the list of client authentication methods supported by the server.
*
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/Oauth2ScopeUtils.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/Oauth2ScopeUtils.java
index 6999cfbca8f..b255171003d 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/Oauth2ScopeUtils.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/Oauth2ScopeUtils.java
@@ -55,6 +55,7 @@ public class Oauth2ScopeUtils {
public static final String OAUTH_APP_DO_PROPERTY_NAME = "OAuthAppDO";
private static final String OAUTH_ENABLE_SYSTEM_LEVEL_INTERNAL_SYSTEM_SCOPE_MANAGEMENT =
"OAuth.EnableSystemLevelInternalSystemScopeManagement";
+ private static final String LEGACY_RBAC_SCOPE_VALIDATOR = "Role based scope validator";
public static IdentityOAuth2ScopeServerException generateServerException(Oauth2ScopeConstants.ErrorMessages
error, String data)
@@ -259,6 +260,12 @@ private static boolean iterateOAuth2ScopeValidators(OAuthAuthzReqMessageContext
.getOAuth2ScopeValidators();
// Iterate through all available scope validators.
for (OAuth2ScopeValidator validator : oAuth2ScopeValidators) {
+
+ if (!AuthzUtil.isLegacyAuthzRuntime() && LEGACY_RBAC_SCOPE_VALIDATOR.equals(validator
+ .getValidatorName())) {
+ appScopeValidators.remove(validator.getValidatorName());
+ continue;
+ }
// Validate the scopes from the validator only if it's configured in the OAuth app.
if (validator != null && appScopeValidators.contains(validator.getValidatorName())) {
if (log.isDebugEnabled()) {
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
new file mode 100644
index 00000000000..5a8649debb1
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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;
+import org.wso2.carbon.identity.application.common.model.AuthorizedScopes;
+import org.wso2.carbon.identity.application.common.model.Scope;
+import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.oauth.IdentityOAuthAdminException;
+import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl;
+import org.wso2.carbon.identity.oauth.common.OAuthConstants;
+import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder;
+import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
+import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext;
+import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
+import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationContext;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandlerException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.wso2.carbon.identity.oauth2.Oauth2ScopeConstants.SYSTEM_SCOPE;
+import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.INTERNAL_LOGIN_SCOPE;
+
+/**
+ * Default oauth2 scope validator which validate application authorized scopes.
+ */
+public class DefaultOAuth2ScopeValidator {
+
+ public static final String CLIENT_TYPE = "oauth2";
+
+ private static final Log LOG = LogFactory.getLog(DefaultOAuth2ScopeValidator.class);
+
+ private static final String NO_POLICY_HANDLER = "NoPolicyScopeValidationHandler";
+
+ /**
+ * Validate scope.
+ *
+ * @param authzReqMessageContext AuthzReqMessageContext.
+ * @return List of scopes.
+ * @throws IdentityOAuth2Exception Error when performing the scope validation.
+ */
+ public List validateScope(OAuthAuthzReqMessageContext authzReqMessageContext)
+ throws IdentityOAuth2Exception {
+
+ if (isScopesEmpty(authzReqMessageContext.getAuthorizationReqDTO().getScopes())) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Requested scope list is empty. Therefore, default OAuth2 scope validation is skipped.");
+ }
+ return new ArrayList<>();
+ }
+ List requestedScopes = Arrays.asList(authzReqMessageContext.getAuthorizationReqDTO().getScopes());
+ String tenantDomain = authzReqMessageContext.getAuthorizationReqDTO().getTenantDomain();
+ String clientId = authzReqMessageContext.getAuthorizationReqDTO().getConsumerKey();
+ String appId = getApplicationId(clientId, tenantDomain);
+ List authorizedScopes = getAuthorizedScopes(requestedScopes, authzReqMessageContext
+ .getAuthorizationReqDTO().getUser(), appId, null, tenantDomain);
+ removeRegisteredScopes(authzReqMessageContext);
+ return authorizedScopes;
+ }
+
+ /**
+ * Validate scope.
+ *
+ * @param tokenReqMessageContext tokenReqMessageContext.
+ * @return List of scopes.
+ * @throws IdentityOAuth2Exception Error when performing the scope validation.
+ */
+ public List validateScope(OAuthTokenReqMessageContext tokenReqMessageContext)
+ throws IdentityOAuth2Exception {
+
+ if (isScopesEmpty(tokenReqMessageContext.getScope())) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Requested scope list is empty. Therefore, default OAuth2 scope validation is skipped.");
+ }
+ return new ArrayList<>();
+ }
+ List requestedScopes = Arrays.asList(tokenReqMessageContext.getScope());
+ String tenantDomain = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getTenantDomain();
+ String clientId = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId();
+ String appId = getApplicationId(clientId, tenantDomain);
+ String grantType = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getGrantType();
+ List authorizedScopes = getAuthorizedScopes(requestedScopes, tokenReqMessageContext
+ .getAuthorizedUser(), appId, grantType, tenantDomain);
+ removeRegisteredScopes(tokenReqMessageContext);
+ if (OAuthConstants.GrantTypes.CLIENT_CREDENTIALS.equals(grantType) && authorizedScopes.contains(SYSTEM_SCOPE)) {
+ authorizedScopes.remove(INTERNAL_LOGIN_SCOPE);
+ }
+ return authorizedScopes;
+ }
+
+ /**
+ * Get authorized scopes.
+ *
+ * @param requestedScopes Requested scopes.
+ * @param authenticatedUser Authenticated user.
+ * @param appId App ID.
+ * @param grantType Grant type.
+ * @param tenantDomain Tenant domain.
+ * @return Authorized scopes.
+ * @throws IdentityOAuth2Exception if any error occurs during getting authorized scopes.
+ */
+ private List getAuthorizedScopes(List requestedScopes, AuthenticatedUser authenticatedUser,
+ String appId, String grantType, String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ // Filter OIDC scopes and add to approved scopes list.
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Filtering OIDC scopes from requested scopes: " + StringUtils.join(requestedScopes, " "));
+ }
+ Set requestedOIDCScopes = getRequestedOIDCScopes(tenantDomain, requestedScopes);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Requested OIDC scopes : " + StringUtils.join(requestedOIDCScopes, " "));
+ }
+ /* Here, we add the user-requested OIDC scopes to the approved scope list and remove from requested scope list
+ before we pass the scopes to the authorization service. Otherwise, the OIDC scopes will be dropped from
+ the approved scope list. */
+ List approvedScopes = new ArrayList<>(requestedOIDCScopes);
+ requestedScopes = removeOIDCScopes(requestedScopes, requestedOIDCScopes);
+ if (requestedScopes.contains(SYSTEM_SCOPE)) {
+ requestedScopes.addAll(getInternalScopes(tenantDomain));
+ requestedScopes.addAll(getConsoleScopes(tenantDomain));
+ }
+ List authorizedScopesList = getAuthorizedScopes(appId, tenantDomain);
+ List scopeValidationHandlers =
+ OAuthComponentServiceHolder.getInstance().getScopeValidationHandlers();
+ Map> validatedScopesByHandler = new HashMap<>();
+ for (AuthorizedScopes authorizedScopes : authorizedScopesList) {
+ String policyId = authorizedScopes.getPolicyId();
+ ScopeValidationContext scopeValidationContext = new ScopeValidationContext();
+ scopeValidationContext.setAuthenticatedUser(authenticatedUser);
+ scopeValidationContext.setAppId(appId);
+ scopeValidationContext.setPolicyId(policyId);
+ scopeValidationContext.setGrantType(grantType);
+ for (ScopeValidationHandler scopeValidationHandler : scopeValidationHandlers) {
+ if (scopeValidationHandler.canHandle(scopeValidationContext)) {
+ scopeValidationContext.setValidatedScopesByHandler(validatedScopesByHandler);
+ List validatedScopes;
+ try {
+ validatedScopes = scopeValidationHandler.validateScopes(requestedScopes,
+ authorizedScopes.getScopes(), scopeValidationContext);
+ } catch (ScopeValidationHandlerException e) {
+ throw new IdentityOAuth2Exception("Error while validating policies roles from " +
+ "authorization service.", e);
+ }
+ validatedScopesByHandler.put(scopeValidationHandler.getName(), validatedScopes);
+ }
+ }
+ }
+
+ // If "NoPolicyScopeValidationHandler" exists, add all its scopes to the result
+ Set scopes = new HashSet<>(validatedScopesByHandler.getOrDefault(NO_POLICY_HANDLER,
+ Collections.emptyList()));
+
+ // Separate "NoPolicyScopeValidationHandler" and get the intersection of the rest of the scopes validated
+ // by other validators
+ List> otherHandlerScopes = new ArrayList<>(validatedScopesByHandler.values());
+ otherHandlerScopes.remove(validatedScopesByHandler.get(NO_POLICY_HANDLER));
+
+ List intersection = new ArrayList<>();
+ if (!otherHandlerScopes.isEmpty()) {
+ intersection = otherHandlerScopes.get(0);
+ for (int i = 1; i < otherHandlerScopes.size(); i++) {
+ intersection = intersection.stream().filter(otherHandlerScopes.get(i)::contains)
+ .collect(Collectors.toList());
+ }
+ }
+ scopes.addAll(intersection);
+ approvedScopes.addAll(scopes);
+ return approvedScopes;
+ }
+
+ /**
+ * Get the authorized scopes for the given appId and tenant domain.
+ *
+ * @param appId App id.
+ * @param tenantDomain Tenant domain.
+ * @return Authorized scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving authorized scopes for app.
+ */
+ private List getAuthorizedScopes(String appId, String tenantDomain)
+ throws IdentityOAuth2Exception {
+
+ try {
+ return OAuth2ServiceComponentHolder.getInstance()
+ .getAuthorizedAPIManagementService().getAuthorizedScopes(appId, tenantDomain);
+ } catch (IdentityApplicationManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving authorized scopes for app : " + appId
+ + "tenant domain : " + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Get the internal scopes.
+ *
+ * @param tenantDomain Tenant domain.
+ * @return Internal scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving internal scopes for tenant domain.
+ */
+ private List getInternalScopes(String tenantDomain) throws IdentityOAuth2Exception {
+
+ try {
+ List scopes = OAuth2ServiceComponentHolder.getInstance()
+ .getApiResourceManager().getScopesByTenantDomain(tenantDomain, "name sw internal_");
+ return scopes.stream().map(Scope::getName).collect(Collectors.toCollection(ArrayList::new));
+ } catch (APIResourceMgtException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving internal scopes for tenant domain : "
+ + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Get the Console scopes.
+ *
+ * @param tenantDomain Tenant domain.
+ * @return Console scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving console scopes for tenant domain.
+ */
+ private List getConsoleScopes(String tenantDomain) throws IdentityOAuth2Exception {
+
+ try {
+ List scopes = OAuth2ServiceComponentHolder.getInstance()
+ .getApiResourceManager().getScopesByTenantDomain(tenantDomain, "name sw console:");
+ return scopes.stream().map(Scope::getName).collect(Collectors.toCollection(ArrayList::new));
+ } catch (APIResourceMgtException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving console scopes for tenant domain : "
+ + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Get the registered scopes.
+ *
+ * @param tenantDomain Tenant domain.
+ * @return Registered scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving internal scopes for tenant domain.
+ */
+ private List getRegisteredScopes(String tenantDomain) throws IdentityOAuth2Exception {
+
+ try {
+ List scopes = OAuth2ServiceComponentHolder.getInstance()
+ .getApiResourceManager().getScopesByTenantDomain(tenantDomain, null);
+ return scopes.stream().map(Scope::getName).collect(Collectors.toCollection(ArrayList::new));
+ } catch (APIResourceMgtException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving internal scopes for tenant domain : "
+ + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Remove registered scopes.
+ *
+ * @param authzReqMessageContext OAuthAuthzReqMessageContext
+ * @throws IdentityOAuth2Exception Error while remove registered scopes.
+ */
+ private void removeRegisteredScopes(OAuthAuthzReqMessageContext authzReqMessageContext)
+ throws IdentityOAuth2Exception {
+
+ if (authzReqMessageContext.getAuthorizationReqDTO().getScopes() == null) {
+ return;
+ }
+ List registeredScopes = getRegisteredScopes(authzReqMessageContext.getAuthorizationReqDTO()
+ .getTenantDomain());
+ List scopes = new ArrayList<>();
+ for (String scope : authzReqMessageContext.getAuthorizationReqDTO().getScopes()) {
+ if (!registeredScopes.contains(scope)) {
+ scopes.add(scope);
+ }
+ }
+ authzReqMessageContext.getAuthorizationReqDTO().setScopes(scopes.toArray(new String[0]));
+ }
+
+ /**
+ * Remove registered scopes.
+ *
+ * @param tokenReqMessageContext OAuthTokenReqMessageContext
+ * @throws IdentityOAuth2Exception Error while remove registered scopes.
+ */
+ private void removeRegisteredScopes(OAuthTokenReqMessageContext tokenReqMessageContext)
+ throws IdentityOAuth2Exception {
+
+ if (tokenReqMessageContext.getScope() == null) {
+ return;
+ }
+ List registeredScopes = getRegisteredScopes(tokenReqMessageContext.getOauth2AccessTokenReqDTO()
+ .getTenantDomain());
+ List scopes = new ArrayList<>();
+ for (String scope : tokenReqMessageContext.getScope()) {
+ if (!registeredScopes.contains(scope)) {
+ scopes.add(scope);
+ }
+ }
+ tokenReqMessageContext.setScope(scopes.toArray(new String[0]));
+ }
+
+ /**
+ * Get the requested OIDC scopes
+ *
+ * @param tenantDomain Tenant domain.
+ * @param requestedScopes Requested scopes.
+ * @return Requested OIDC scopes.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving oidc scopes.
+ */
+ private Set getRequestedOIDCScopes(String tenantDomain, List requestedScopes)
+ throws IdentityOAuth2Exception {
+
+ OAuthAdminServiceImpl oAuthAdminServiceImpl = OAuth2ServiceComponentHolder.getInstance().getOAuthAdminService();
+ try {
+ List oidcScopes = oAuthAdminServiceImpl.getRegisteredOIDCScope(tenantDomain);
+ return requestedScopes.stream().distinct().filter(oidcScopes::contains).collect(Collectors.toSet());
+ } catch (IdentityOAuthAdminException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving oidc scopes for tenant domain : "
+ + tenantDomain, e);
+ }
+ }
+
+ /**
+ * Remove OIDC scopes from the list.
+ *
+ * @param requestedScopes Requested scopes.
+ * @param oidcScopes OIDC scopes.
+ * @return List of scopes.
+ */
+ private List removeOIDCScopes(List requestedScopes, Set oidcScopes) {
+
+ return requestedScopes.stream().distinct().filter(s -> !oidcScopes.contains(s)).collect(Collectors.toList());
+ }
+
+ /**
+ * Get the application resource id for the given client id
+ *
+ * @param clientId Client Id.
+ * @param tenantName Tenant name.
+ * @return Application resource id.
+ * @throws IdentityOAuth2Exception if an error occurs while retrieving application resource id.
+ */
+ private String getApplicationId(String clientId, String tenantName) throws IdentityOAuth2Exception {
+
+ ApplicationManagementService applicationMgtService = OAuth2ServiceComponentHolder.getApplicationMgtService();
+ try {
+ return applicationMgtService.getApplicationResourceIDByInboundKey(clientId, CLIENT_TYPE, tenantName);
+ } catch (IdentityApplicationManagementException e) {
+ throw new IdentityOAuth2Exception("Error while retrieving application resource id for client : " +
+ clientId + " tenant : " + tenantName, e);
+ }
+ }
+
+ /**
+ * Checks if the scopes list is empty
+ *
+ * @param scopes Scopes list
+ * @return true if scopes list is empty
+ */
+ private boolean isScopesEmpty(String[] scopes) {
+
+ return ArrayUtils.isEmpty(scopes);
+ }
+
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationContext.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationContext.java
new file mode 100644
index 00000000000..b7736493c1f
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationContext.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler;
+
+import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Scope Validation Context is where we pass scope validation context to the scope validation handlers.
+ */
+public class ScopeValidationContext {
+
+ private AuthenticatedUser authenticatedUser;
+ private String appId;
+ private String grantType;
+
+ private String policyId;
+ private Map> validatedScopesByHandler;
+
+ /**
+ * Get the authenticated user.
+ *
+ * @return AuthenticatedUser.
+ */
+
+ public AuthenticatedUser getAuthenticatedUser() {
+
+ return authenticatedUser;
+ }
+
+ /**
+ * Set the authenticated user.
+ *
+ * @param authenticatedUser AuthenticatedUser.
+ */
+ public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
+
+ this.authenticatedUser = authenticatedUser;
+ }
+
+ /**
+ * Get the application id.
+ *
+ * @return Application ID.
+ */
+ public String getAppId() {
+
+ return appId;
+ }
+
+ /**
+ * Set the application id.
+ *
+ * @param appId Application ID.
+ */
+ public void setAppId(String appId) {
+
+ this.appId = appId;
+ }
+
+ /**
+ * Get the validated scopes by handler
+ *
+ * @return Map of validated scopes.
+ */
+ public Map> getValidatedScopesByHandler() {
+
+ return validatedScopesByHandler;
+ }
+
+ /**
+ * Set the validated scopes by handler.
+ *
+ * @param validatedScopesByHandler Map of validated scopes.
+ */
+ public void setValidatedScopesByHandler(Map> validatedScopesByHandler) {
+
+ this.validatedScopesByHandler = validatedScopesByHandler;
+ }
+
+ /**
+ * Get the grant type.
+ *
+ * @return Grant type.
+ */
+ public String getGrantType() {
+
+ return grantType;
+ }
+
+ /**
+ * Set the grant type.
+ *
+ * @param grantType Grant type.
+ */
+ public void setGrantType(String grantType) {
+
+ this.grantType = grantType;
+ }
+
+ /**
+ * Get the policy id.
+ *
+ * @return Policy ID.
+ */
+ public String getPolicyId() {
+
+ return policyId;
+ }
+
+ /**
+ * Set the policy id.
+ *
+ * @param policyId Policy ID.
+ */
+ public void setPolicyId(String policyId) {
+
+ this.policyId = policyId;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandler.java
new file mode 100644
index 00000000000..7ab73cdfc0d
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler;
+
+import java.util.List;
+
+/**
+ * Each scope validation handler for authorized policies should implement this.
+ */
+public interface ScopeValidationHandler {
+
+ /**
+ * Check if the handler can handle the scope validation
+ *
+ * @param scopeValidationContext ScopeValidationContext.
+ * @return boolean
+ */
+ boolean canHandle(ScopeValidationContext scopeValidationContext);
+
+ /**
+ * Validate scopes.
+ *
+ * @param requestedScopes Requested scopes.
+ * @param appAuthorizedScopes Authorized scopes.
+ * @param scopeValidationContext ScopeValidationContext.
+ * @return List of scopes.
+ * @throws ScopeValidationHandlerException Error when performing the scope validation.
+ */
+ List validateScopes(List requestedScopes, List appAuthorizedScopes,
+ ScopeValidationContext scopeValidationContext) throws ScopeValidationHandlerException;
+
+ /**
+ * Get policy ID.
+ *
+ * @return Policy ID.
+ */
+ String getPolicyID();
+
+ /**
+ * Get handler name.
+ *
+ * @return Handler name.
+ */
+
+ String getName();
+
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandlerException.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandlerException.java
new file mode 100644
index 00000000000..ad5411cacb5
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/ScopeValidationHandlerException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler;
+
+/**
+ * ScopeValidatorPolicyHandlerException
+ */
+public class ScopeValidationHandlerException extends Exception {
+
+ /**
+ * Constructs a new exception with an error message.
+ *
+ * @param message The detail message.
+ */
+ public ScopeValidationHandlerException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the message and cause.
+ *
+ * @param message The detail message.
+ * @param cause The cause.
+ */
+ public ScopeValidationHandlerException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/M2MScopeValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/M2MScopeValidationHandler.java
new file mode 100644
index 00000000000..f59291075c3
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/M2MScopeValidationHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler.impl;
+
+import org.wso2.carbon.identity.oauth.common.OAuthConstants;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationContext;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandlerException;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * M2M scope validation handler engage for client credential grant to validate scopes.
+ */
+public class M2MScopeValidationHandler implements ScopeValidationHandler {
+
+ @Override
+ public boolean canHandle(ScopeValidationContext scopeValidationContext) {
+
+ return OAuthConstants.GrantTypes.CLIENT_CREDENTIALS.equals(scopeValidationContext.getGrantType()) &&
+ scopeValidationContext.getPolicyId().equals("RBAC");
+ }
+
+ @Override
+ public List validateScopes(List requestedScopes, List appAuthorizedScopes,
+ ScopeValidationContext scopeValidationContext)
+ throws ScopeValidationHandlerException {
+
+ return requestedScopes.stream().filter(appAuthorizedScopes::contains).collect(Collectors.toList());
+ }
+
+ @Override
+ public String getPolicyID() {
+
+ return "M2M";
+ }
+
+ @Override
+ public String getName() {
+
+ return "M2MScopeValidationHandler";
+ }
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/NoPolicyScopeValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/NoPolicyScopeValidationHandler.java
new file mode 100644
index 00000000000..48a38407a8b
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/NoPolicyScopeValidationHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler.impl;
+
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationContext;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandlerException;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * No policy scope validation handler handle authorized no policy scopes.
+ */
+public class NoPolicyScopeValidationHandler implements ScopeValidationHandler {
+
+ @Override
+ public boolean canHandle(ScopeValidationContext scopeValidationContext) {
+
+ return getPolicyID().equals(scopeValidationContext.getPolicyId());
+ }
+
+ @Override
+ public List validateScopes(List requestedScopes, List appAuthorizedScopes,
+ ScopeValidationContext scopeValidationContext)
+ throws ScopeValidationHandlerException {
+
+ return requestedScopes.stream().filter(appAuthorizedScopes::contains).collect(Collectors.toList());
+ }
+
+ @Override
+ public String getPolicyID() {
+
+ return "NO POLICY";
+ }
+
+ @Override
+ public String getName() {
+
+ return "NoPolicyScopeValidationHandler";
+ }
+}
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/RoleBasedScopeValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/RoleBasedScopeValidationHandler.java
new file mode 100644
index 00000000000..c1194c26389
--- /dev/null
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/validationhandler/impl/RoleBasedScopeValidationHandler.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 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
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.oauth2.validators.validationhandler.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
+import org.wso2.carbon.identity.application.common.model.RoleV2;
+import org.wso2.carbon.identity.oauth.common.OAuthConstants;
+import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
+import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
+import org.wso2.carbon.identity.oauth2.util.AuthzUtil;
+import org.wso2.carbon.identity.oauth2.validators.DefaultOAuth2ScopeValidator;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationContext;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandler;
+import org.wso2.carbon.identity.oauth2.validators.validationhandler.ScopeValidationHandlerException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Role based scope validation handler validate scopes based on users roles.
+ */
+public class RoleBasedScopeValidationHandler implements ScopeValidationHandler {
+
+ private static final Log LOG = LogFactory.getLog(DefaultOAuth2ScopeValidator.class);
+
+ @Override
+ public boolean canHandle(ScopeValidationContext scopeValidationContext) {
+
+ return getPolicyID().equals(scopeValidationContext.getPolicyId())
+ && !OAuthConstants.GrantTypes.CLIENT_CREDENTIALS.equals(scopeValidationContext.getGrantType());
+ }
+
+ @Override
+ public List validateScopes(List requestedScopes, List appAuthorizedScopes,
+ ScopeValidationContext scopeValidationContext)
+ throws ScopeValidationHandlerException {
+
+ try {
+ List userRoles = AuthzUtil.getUserRoles(scopeValidationContext.getAuthenticatedUser(),
+ scopeValidationContext.getAppId());
+ if (userRoles.isEmpty()) {
+ return new ArrayList<>();
+ }
+ List filteredRoleIds = getFilteredRoleIds(userRoles, scopeValidationContext.getAppId(),
+ scopeValidationContext.getAuthenticatedUser().getTenantDomain());
+ if (filteredRoleIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+ List associatedScopes = AuthzUtil.getAssociatedScopesForRoles(filteredRoleIds,
+ scopeValidationContext.getAuthenticatedUser().getTenantDomain());
+ List filteredScopes = appAuthorizedScopes.stream().filter(associatedScopes::contains)
+ .collect(Collectors.toList());
+ return requestedScopes.stream().filter(filteredScopes::contains).collect(Collectors.toList());
+ } catch (IdentityOAuth2Exception e) {
+ throw new ScopeValidationHandlerException("Error while validation scope with RBAC Scope Validation " +
+ "handler", e);
+ }
+ }
+
+ /**
+ * Get the filtered role ids.
+ *
+ * @param roleId Role id list.
+ * @param appId App id.
+ * @param tenantDomain Tenant domain.
+ * @return Filtered role ids.
+ * @throws ScopeValidationHandlerException if an error occurs while retrieving filtered role id list.
+ */
+ private List getFilteredRoleIds(List roleId, String appId, String tenantDomain)
+ throws ScopeValidationHandlerException {
+
+ List rolesAssociatedWithApp = getRoleIdsAssociatedWithApp(appId, tenantDomain);
+ return roleId.stream().distinct().filter(rolesAssociatedWithApp::contains).collect(Collectors.toList());
+ }
+
+ /**
+ * Get the role ids associated with app.
+ *
+ * @param appId App id.
+ * @param tenantDomain Tenant domain.
+ * @return Role ids associated with app.
+ * @throws ScopeValidationHandlerException if an error occurs while retrieving role id list of app.
+ */
+ private List getRoleIdsAssociatedWithApp(String appId, String tenantDomain)
+ throws ScopeValidationHandlerException {
+
+ try {
+ return OAuth2ServiceComponentHolder.getApplicationMgtService()
+ .getAssociatedRolesOfApplication(appId, tenantDomain).stream().map(RoleV2::getId)
+ .collect(Collectors.toCollection(ArrayList::new));
+ } catch (IdentityApplicationManagementException e) {
+ throw new ScopeValidationHandlerException("Error while retrieving role id list of app : " + appId
+ + "tenant domain : " + tenantDomain, e);
+ }
+ }
+
+ @Override
+ public String getPolicyID() {
+
+ return "RBAC";
+ }
+
+ @Override
+ public String getName() {
+
+ return "RoleBasedScopeValidationHandler";
+ }
+}
diff --git a/pom.xml b/pom.xml
index f8c07d8e295..486139b7f08 100644
--- a/pom.xml
+++ b/pom.xml
@@ -685,7 +685,16 @@
test
-
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.api.resource.mgt
+ ${carbon.identity.framework.version}
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.role.v2.mgt.core
+ ${carbon.identity.framework.version}
+
@@ -857,7 +866,7 @@
1.2.4
- 4.9.10
+ 4.9.16
4.9.7
[4.5.0, 5.0.0)
[1.0.1, 2.0.0)