From e29aa58e198105e45bf9ff8c387f2795e195cb26 Mon Sep 17 00:00:00 2001 From: Milan Perera Date: Tue, 24 Jan 2017 14:22:12 +0530 Subject: [PATCH 1/3] Removed tenant domain from Basic Authenticator --- .../framework/authenticator/BasicAuthAuthenticator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java index 908613cf359..3b06468fe92 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BasicAuthAuthenticator.java @@ -28,6 +28,7 @@ import org.apache.tomcat.util.buf.CharChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException; import org.wso2.carbon.webapp.authenticator.framework.AuthenticatorFrameworkDataHolder; import org.wso2.carbon.webapp.authenticator.framework.Constants; @@ -68,7 +69,8 @@ public class BasicAuthAuthenticator implements WebappAuthenticator { int tenantId = Utils.getTenantIdOFUser(credentials.getUsername()); UserStoreManager userStore = AuthenticatorFrameworkDataHolder.getInstance().getRealmService(). getTenantUserRealm(tenantId).getUserStoreManager(); - boolean authenticated = userStore.authenticate(credentials.getUsername(), credentials.getPassword()); + String username = MultitenantUtils.getTenantAwareUsername(credentials.getUsername()); + boolean authenticated = userStore.authenticate(username, credentials.getPassword()); if (authenticated) { authenticationInfo.setStatus(Status.CONTINUE); authenticationInfo.setUsername(credentials.getUsername()); From 21d8d14b7e05744f4c916b5a7a4c5c1de8825a7b Mon Sep 17 00:00:00 2001 From: Milan Perera Date: Thu, 26 Jan 2017 14:50:12 +0530 Subject: [PATCH 2/3] Fixed multy tenancy issues related to android device type --- .../extension/api/filter/ApiPermissionFilter.java | 4 +++- .../src/main/webapp/META-INF/permissions.xml | 4 ++-- .../core/internal/DeviceManagementServiceComponent.java | 6 +++--- .../src/main/resources/dbscripts/cdm/h2.sql | 7 +------ .../src/main/resources/dbscripts/cdm/mssql.sql | 4 +--- .../src/main/resources/dbscripts/cdm/mysql.sql | 7 +------ .../src/main/resources/dbscripts/cdm/oracle.sql | 5 +---- .../src/main/resources/dbscripts/cdm/postgresql.sql | 7 +------ 8 files changed, 13 insertions(+), 31 deletions(-) diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/java/org/wso2/carbon/apimgt/application/extension/api/filter/ApiPermissionFilter.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/java/org/wso2/carbon/apimgt/application/extension/api/filter/ApiPermissionFilter.java index 5f05dfb3373..1f043c7b86e 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/java/org/wso2/carbon/apimgt/application/extension/api/filter/ApiPermissionFilter.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/java/org/wso2/carbon/apimgt/application/extension/api/filter/ApiPermissionFilter.java @@ -23,6 +23,7 @@ import org.wso2.carbon.apimgt.application.extension.api.util.APIUtil; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.user.api.UserRealm; import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -120,7 +121,8 @@ public class ApiPermissionFilter implements Filter { try { UserRealm userRealm = APIUtil.getRealmService().getTenantUserRealm(PrivilegedCarbonContext .getThreadLocalCarbonContext().getTenantId()); - return userRealm.getAuthorizationManager().isUserAuthorized(username, permission, action); + String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username); + return userRealm.getAuthorizationManager().isUserAuthorized(tenantAwareUsername, permission, action); } catch (UserStoreException e) { String errorMsg = String.format("Unable to authorize the user : %s", username); log.error(errorMsg, e); diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/webapp/META-INF/permissions.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/webapp/META-INF/permissions.xml index 591725fa120..9c41774ce57 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/webapp/META-INF/permissions.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/src/main/webapp/META-INF/permissions.xml @@ -37,14 +37,14 @@ Register application - /device-mgt/api/application + /manage/api/subscribe /register POST application_user Delete application - /device-mgt/api/application + /manage/api/subscribe /unregister DELETE application_user diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java index adbf6cda436..ebf1e576543 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java @@ -404,13 +404,13 @@ public class DeviceManagementServiceComponent { } - protected void setDeviceTaskManagerService(DeviceTaskManagerService emailSenderService) { + protected void setDeviceTaskManagerService(DeviceTaskManagerService deviceTaskManagerService) { if (log.isDebugEnabled()) { } - DeviceManagementDataHolder.getInstance().setDeviceTaskManagerService(emailSenderService); + DeviceManagementDataHolder.getInstance().setDeviceTaskManagerService(deviceTaskManagerService); } - protected void unsetDeviceTaskManagerService(DeviceTaskManagerService emailSenderService) { + protected void unsetDeviceTaskManagerService(DeviceTaskManagerService deviceTaskManagerService) { if (log.isDebugEnabled()) { } DeviceManagementDataHolder.getInstance().setDeviceTaskManagerService(null); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql index eb30a07b12e..498a439c076 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -145,12 +145,7 @@ CREATE TABLE IF NOT EXISTS DM_PROFILE ( DEVICE_TYPE VARCHAR(300) NOT NULL , CREATED_TIME DATETIME NOT NULL , UPDATED_TIME DATETIME NOT NULL , - PRIMARY KEY (ID) , - CONSTRAINT DM_PROFILE_DEVICE_TYPE - FOREIGN KEY (DEVICE_TYPE, TENANT_ID) - REFERENCES DM_DEVICE_TYPE (NAME, PROVIDER_TENANT_ID) - ON DELETE NO ACTION - ON UPDATE NO ACTION + PRIMARY KEY (ID) ); CREATE TABLE IF NOT EXISTS DM_POLICY ( diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql index db405cdc1eb..b1a029dbbbb 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mssql.sql @@ -191,9 +191,7 @@ CREATE TABLE DM_PROFILE ( DEVICE_TYPE VARCHAR(300) NOT NULL , CREATED_TIME DATETIME NOT NULL , UPDATED_TIME DATETIME NOT NULL , - PRIMARY KEY (ID) , - CONSTRAINT DM_PROFILE_DEVICE_TYPE FOREIGN KEY (DEVICE_TYPE, TENANT_ID) REFERENCES - DM_DEVICE_TYPE (NAME, PROVIDER_TENANT_ID) ON DELETE NO ACTION ON UPDATE NO ACTION + PRIMARY KEY (ID) ); IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_POLICY]') AND TYPE IN (N'U')) diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql index 5d8faece3f2..f327b7b5993 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/mysql.sql @@ -168,12 +168,7 @@ CREATE TABLE IF NOT EXISTS DM_PROFILE ( DEVICE_TYPE VARCHAR(300) NOT NULL , CREATED_TIME DATETIME NOT NULL , UPDATED_TIME DATETIME NOT NULL , - PRIMARY KEY (ID) , - CONSTRAINT DM_PROFILE_DEVICE_TYPE - FOREIGN KEY (DEVICE_TYPE, TENANT_ID) - REFERENCES DM_DEVICE_TYPE (NAME, PROVIDER_TENANT_ID) - ON DELETE NO ACTION - ON UPDATE NO ACTION + PRIMARY KEY (ID) )ENGINE = InnoDB; diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql index f2e71c9c9c3..c544452f52b 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/oracle.sql @@ -268,10 +268,7 @@ CREATE TABLE DM_PROFILE ( DEVICE_TYPE VARCHAR2(300) NOT NULL , CREATED_TIME TIMESTAMP(0) NOT NULL , UPDATED_TIME TIMESTAMP(0) NOT NULL , - CONSTRAINT PK_DM_PROFILE PRIMARY KEY (ID) , - CONSTRAINT DM_PROFILE_DEVICE_TYPE - FOREIGN KEY (DEVICE_TYPE, TENANT_ID) - REFERENCES DM_DEVICE_TYPE (NAME, PROVIDER_TENANT_ID) + CONSTRAINT PK_DM_PROFILE PRIMARY KEY (ID) ) / -- Generate ID using sequence and trigger diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql index d5c035e26b1..9c1f7490a6c 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/src/main/resources/dbscripts/cdm/postgresql.sql @@ -148,12 +148,7 @@ CREATE TABLE IF NOT EXISTS DM_PROFILE ( TENANT_ID INTEGER NOT NULL , DEVICE_TYPE VARCHAR(300) NOT NULL , CREATED_TIME TIMESTAMP NOT NULL , - UPDATED_TIME TIMESTAMP NOT NULL , - CONSTRAINT DM_PROFILE_DEVICE_TYPE - FOREIGN KEY (DEVICE_TYPE, TENANT_ID) - REFERENCES DM_DEVICE_TYPE (NAME, PROVIDER_TENANT_ID) - ON DELETE NO ACTION - ON UPDATE NO ACTION + UPDATED_TIME TIMESTAMP NOT NULL ); CREATE TABLE IF NOT EXISTS DM_POLICY ( From 7ba1dc989a1b6fa052b18134247d4800a8ae1d23 Mon Sep 17 00:00:00 2001 From: Milan Perera Date: Thu, 26 Jan 2017 14:51:30 +0530 Subject: [PATCH 3/3] Implemented a scope delegation validator --- .../handlers/ScopeValidationHandler.java | 131 +++++++++++++++ .../OAuthExtensionServiceComponent.java | 34 ++++ .../internal/OAuthExtensionsDataHolder.java | 24 ++- .../ExtendedJDBCScopeValidator.java | 2 +- .../PermissionBasedScopeValidator.java | 159 ++++++++++++++++++ .../validators/RoleBasedScopeValidator.java | 158 +++++++++++++++++ 6 files changed, 506 insertions(+), 2 deletions(-) create mode 100644 components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/ScopeValidationHandler.java create mode 100644 components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/PermissionBasedScopeValidator.java create mode 100644 components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/RoleBasedScopeValidator.java diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/ScopeValidationHandler.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/ScopeValidationHandler.java new file mode 100644 index 00000000000..0c3964e9c0f --- /dev/null +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/ScopeValidationHandler.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.device.mgt.oauth.extensions.handlers; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.oauth.extensions.internal.OAuthExtensionsDataHolder; +import org.wso2.carbon.identity.oauth.cache.CacheEntry; +import org.wso2.carbon.identity.oauth.cache.OAuthCache; +import org.wso2.carbon.identity.oauth.cache.OAuthCacheKey; +import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dao.TokenMgtDAO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.model.ResourceScopeCacheEntry; +import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator; + +import java.util.Map; + +public class ScopeValidationHandler extends OAuth2ScopeValidator { + + private final static Log log = LogFactory.getLog(ScopeValidationHandler.class); + private Map scopeValidators; + + private final String DEFAULT_PREFIX = "default"; + + public ScopeValidationHandler() { + scopeValidators = OAuthExtensionsDataHolder.getInstance().getScopeValidators(); + } + + public boolean validateScope(AccessTokenDO accessTokenDO, String resource) throws IdentityOAuth2Exception { + + //returns true if scope validators are not defined + if (scopeValidators == null || scopeValidators.isEmpty()) { + if(log.isDebugEnabled()){ + log.debug("OAuth2 scope validators are not loaded"); + } + return true; + } + + String resourceScope = getResourceScope(resource); + + //returns true if scope does not exist for the resource + if (resourceScope == null) { + if(log.isDebugEnabled()){ + log.debug("Resource '" + resource + "' is not protected with a scope"); + } + return true; + } + + String scope[] = resourceScope.split(":"); + String scopePrefix = scope[0]; + + OAuth2ScopeValidator scopeValidator = scopeValidators.get(scopePrefix); + + if (scopeValidator == null) { + if(log.isDebugEnabled()){ + log.debug("OAuth2 scope validator cannot be identified for '" + scopePrefix + "' scope prefix"); + } + + // loading default scope validator if matching validator is not found + scopeValidator = scopeValidators.get(DEFAULT_PREFIX); + if(log.isDebugEnabled()){ + log.debug("Loading default scope validator"); + } + + if (scopeValidator == null) { + if(log.isDebugEnabled()){ + log.debug("Default scope validator is not available"); + } + return true; + } + } + + // validate scope via relevant scope validator that matches with the prefix + return scopeValidator.validateScope(accessTokenDO, resourceScope); + } + + private String getResourceScope(String resource) { + + String resourceScope = null; + boolean cacheHit = false; + // Check the cache, if caching is enabled. + if (OAuthServerConfiguration.getInstance().isCacheEnabled()) { + OAuthCache oauthCache = OAuthCache.getInstance(); + OAuthCacheKey cacheKey = new OAuthCacheKey(resource); + CacheEntry result = oauthCache.getValueFromCache(cacheKey); + + //Cache hit + if (result instanceof ResourceScopeCacheEntry) { + resourceScope = ((ResourceScopeCacheEntry) result).getScope(); + cacheHit = true; + } + } + + TokenMgtDAO tokenMgtDAO = new TokenMgtDAO(); + if (!cacheHit) { + try { + resourceScope = tokenMgtDAO.findScopeOfResource(resource); + } catch (IdentityOAuth2Exception e) { + log.error("Error occurred while retrieving scope for resource '" + resource + "'"); + } + + if (OAuthServerConfiguration.getInstance().isCacheEnabled()) { + OAuthCache oauthCache = OAuthCache.getInstance(); + OAuthCacheKey cacheKey = new OAuthCacheKey(resource); + ResourceScopeCacheEntry cacheEntry = new ResourceScopeCacheEntry(resourceScope); + //Store resourceScope in cache even if it is null (to avoid database calls when accessing resources for + //which scopes haven't been defined). + oauthCache.addToCache(cacheKey, cacheEntry); + } + } + return resourceScope; + } + +} diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java index dfcdc2dff27..63b4d051da6 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java @@ -24,7 +24,10 @@ import org.osgi.service.component.ComponentContext; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.APIManagerConfiguration; +import org.wso2.carbon.device.mgt.oauth.extensions.validators.ExtendedJDBCScopeValidator; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; +import org.wso2.carbon.identity.oauth2.validators.JDBCScopeValidator; +import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; @@ -46,6 +49,12 @@ import java.util.List; * policy="dynamic" * bind="setOAuth2ValidationService" * unbind="unsetOAuth2ValidationService" + * * @scr.reference name="scope.validator.service" + * interface="org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator" + * cardinality="0..n" + * policy="dynamic" + * bind="addScopeValidator" + * unbind="removeScopeValidator" */ public class OAuthExtensionServiceComponent { @@ -53,6 +62,8 @@ public class OAuthExtensionServiceComponent { private static final String REPOSITORY = "repository"; private static final String CONFIGURATION = "conf"; private static final String APIM_CONF_FILE = "api-manager.xml"; + private static final String PERMISSION_SCOPE_PREFIX = "perm"; + private static final String DEFAULT_PREFIX = "default"; @SuppressWarnings("unused") @@ -87,6 +98,13 @@ public class OAuthExtensionServiceComponent { } OAuthExtensionsDataHolder.getInstance().setWhitelistedScopes(whiteList); + + ExtendedJDBCScopeValidator permissionBasedScopeValidator = new ExtendedJDBCScopeValidator(); + JDBCScopeValidator roleBasedScopeValidator = new JDBCScopeValidator(); + OAuthExtensionsDataHolder.getInstance().addScopeValidator(permissionBasedScopeValidator, + PERMISSION_SCOPE_PREFIX); + OAuthExtensionsDataHolder.getInstance().addScopeValidator(roleBasedScopeValidator, + DEFAULT_PREFIX); } catch (APIManagementException e) { log.error("Error occurred while loading DeviceMgtConfig configurations", e); } @@ -147,5 +165,21 @@ public class OAuthExtensionServiceComponent { OAuthExtensionsDataHolder.getInstance().setoAuth2TokenValidationService(null); } + /** + * Add scope validator to the map. + * @param scopesValidator + */ + protected void addScopeValidator(OAuth2ScopeValidator scopesValidator) { + OAuthExtensionsDataHolder.getInstance().addScopeValidator(scopesValidator, DEFAULT_PREFIX); + } + + /** + * unset scope validator. + * @param scopesValidator + */ + protected void removeScopeValidator(OAuth2ScopeValidator scopesValidator) { + OAuthExtensionsDataHolder.getInstance().removeScopeValidator(); + } + } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java index 4f401b3db56..2c4f25705d5 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java @@ -19,9 +19,12 @@ package org.wso2.carbon.device.mgt.oauth.extensions.internal; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; +import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator; import org.wso2.carbon.user.core.service.RealmService; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * This holds the OSGi service references required for oauth extensions bundle. @@ -31,10 +34,13 @@ public class OAuthExtensionsDataHolder { private RealmService realmService; private OAuth2TokenValidationService oAuth2TokenValidationService; private List whitelistedScopes; + private Map scopeValidators; private static OAuthExtensionsDataHolder thisInstance = new OAuthExtensionsDataHolder(); - private OAuthExtensionsDataHolder() {} + private OAuthExtensionsDataHolder() { + scopeValidators = new HashMap<>(); + } public static OAuthExtensionsDataHolder getInstance() { return thisInstance; @@ -71,4 +77,20 @@ public class OAuthExtensionsDataHolder { this.whitelistedScopes = whitelistedScopes; } + public Map getScopeValidators() { + return scopeValidators; + } + + public void setScopeValidators(Map scopeValidators) { + this.scopeValidators = scopeValidators; + } + + public void addScopeValidator(OAuth2ScopeValidator oAuth2ScopeValidator, String prefix) { + scopeValidators.put(prefix, oAuth2ScopeValidator); + } + + public void removeScopeValidator() { + scopeValidators = null; + } + } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/ExtendedJDBCScopeValidator.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/ExtendedJDBCScopeValidator.java index 8f5ffa25141..58c975bbec9 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/ExtendedJDBCScopeValidator.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/ExtendedJDBCScopeValidator.java @@ -45,7 +45,7 @@ import java.util.List; import java.util.Set; @SuppressWarnings("unused") -public class ExtendedJDBCScopeValidator extends OAuth2ScopeValidator{ +public class ExtendedJDBCScopeValidator extends OAuth2ScopeValidator { private static final Log log = LogFactory.getLog(ExtendedJDBCScopeValidator.class); private static final String UI_EXECUTE = "ui.execute"; diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/PermissionBasedScopeValidator.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/PermissionBasedScopeValidator.java new file mode 100644 index 00000000000..5be19a5a0c8 --- /dev/null +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/PermissionBasedScopeValidator.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.device.mgt.oauth.extensions.validators; + +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.device.mgt.oauth.extensions.internal.OAuthExtensionsDataHolder; +import org.wso2.carbon.identity.application.common.model.User; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dao.TokenMgtDAO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator; +import org.wso2.carbon.user.api.AuthorizationManager; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +@SuppressWarnings("unused") +public class PermissionBasedScopeValidator extends OAuth2ScopeValidator { + + private static final Log log = LogFactory.getLog(PermissionBasedScopeValidator.class); + private static final String UI_EXECUTE = "ui.execute"; + + + @Override + public boolean validateScope(AccessTokenDO accessTokenDO, String resourceScope) throws IdentityOAuth2Exception { + //Get the list of scopes associated with the access token + String[] scopes = accessTokenDO.getScope(); + + //If no scopes are associated with the token + if (scopes == null || scopes.length == 0) { + return true; + } + + TokenMgtDAO tokenMgtDAO = new TokenMgtDAO(); + + List scopeList = new ArrayList<>(Arrays.asList(scopes)); + + //If the access token does not bear the scope required for accessing the Resource. + if(!scopeList.contains(resourceScope)){ + if(log.isDebugEnabled()){ + log.debug("Access token '" + accessTokenDO.getAccessToken() + "' does not bear the scope '" + + resourceScope + "'"); + } + return false; + } + + try { + //Get the permissions associated with the scope, if any + Set permissionsOfScope = tokenMgtDAO.getRolesOfScopeByScopeKey(resourceScope); + + //If the scope doesn't have any permissions associated with it. + if(permissionsOfScope == null || permissionsOfScope.isEmpty()){ + if(log.isDebugEnabled()){ + log.debug("Did not find any roles associated to the scope " + resourceScope); + } + return true; + } + + if(log.isDebugEnabled()){ + StringBuilder logMessage = new StringBuilder("Found permissions of scope '" + resourceScope + "' "); + for(String permission : permissionsOfScope){ + logMessage.append(permission); + logMessage.append(", "); + } + log.debug(logMessage.toString()); + } + + User authorizedUser = accessTokenDO.getAuthzUser(); + RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); + + int tenantId = realmService.getTenantManager().getTenantId(authorizedUser.getTenantDomain()); + + if (tenantId == 0 || tenantId == -1) { + tenantId = IdentityTenantUtil.getTenantIdOfUser(authorizedUser.getUserName()); + } + + AuthorizationManager authorizationManager; + String[] userRoles; + boolean tenantFlowStarted = false; + + try{ + //If this is a tenant user + if(tenantId != MultitenantConstants.SUPER_TENANT_ID){ + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + realmService.getTenantManager().getDomain(tenantId),true); + tenantFlowStarted = true; + } + + authorizationManager = realmService.getTenantUserRealm(tenantId).getAuthorizationManager(); + + } finally { + if (tenantFlowStarted) { + PrivilegedCarbonContext.endTenantFlow(); + } + } + boolean status = false; + String username = MultitenantUtils.getTenantAwareUsername(authorizedUser.getUserName()); + for (String permission : permissionsOfScope) { + if (authorizationManager != null) { + String userStore = authorizedUser.getUserStoreDomain(); + + if (userStore != null) { + status = authorizationManager + .isUserAuthorized(userStore + "/" + username, permission, UI_EXECUTE); + } else { + status = authorizationManager.isUserAuthorized(username , permission, UI_EXECUTE); + } + if (status) { + break; + } + } + } + + if (status) { + if(log.isDebugEnabled()){ + log.debug("User '" + authorizedUser.getUserName() + "' is authorized"); + } + return true; + } + + if(log.isDebugEnabled()){ + log.debug("No permissions associated for the user " + authorizedUser.getUserName()); + } + return false; + + } catch (UserStoreException e) { + //Log and return since we do not want to stop issuing the token in case of scope validation failures. + log.error("Error when getting the tenant's UserStoreManager or when getting roles of user ", e); + return false; + } + } + +} diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/RoleBasedScopeValidator.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/RoleBasedScopeValidator.java new file mode 100644 index 00000000000..593d9d0c89d --- /dev/null +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/validators/RoleBasedScopeValidator.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.device.mgt.oauth.extensions.validators; + +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.device.mgt.oauth.extensions.internal.OAuthExtensionsDataHolder; +import org.wso2.carbon.identity.application.common.model.User; +import org.wso2.carbon.identity.base.IdentityConstants; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dao.TokenMgtDAO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeValidator; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.api.UserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + + +/** + * The JDBC Scope Validation implementation. This validates the Resource's scope (stored in IDN_OAUTH2_RESOURCE_SCOPE) + * against the Access Token's scopes. + */ +public class RoleBasedScopeValidator extends OAuth2ScopeValidator { + + Log log = LogFactory.getLog(RoleBasedScopeValidator.class); + + @Override + public boolean validateScope(AccessTokenDO accessTokenDO, String resourceScope) throws IdentityOAuth2Exception { + + //Get the list of scopes associated with the access token + String[] scopes = accessTokenDO.getScope(); + + //If no scopes are associated with the token + if (scopes == null || scopes.length == 0) { + return true; + } + + TokenMgtDAO tokenMgtDAO = new TokenMgtDAO(); + + List scopeList = new ArrayList<>(Arrays.asList(scopes)); + + //If the access token does not bear the scope required for accessing the Resource. + if(!scopeList.contains(resourceScope)){ + if(log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)){ + log.debug("Access token '" + accessTokenDO.getAccessToken() + "' does not bear the scope '" + + resourceScope + "'"); + } + return false; + } + + try { + //Get the roles associated with the scope, if any + Set rolesOfScope = tokenMgtDAO.getRolesOfScopeByScopeKey(resourceScope); + + //If the scope doesn't have any roles associated with it. + if(rolesOfScope == null || rolesOfScope.isEmpty()){ + if(log.isDebugEnabled()){ + log.debug("Did not find any roles associated to the scope " + resourceScope); + } + return true; + } + + if(log.isDebugEnabled()){ + StringBuilder logMessage = new StringBuilder("Found roles of scope '" + resourceScope + "' "); + for(String role : rolesOfScope){ + logMessage.append(role); + logMessage.append(", "); + } + log.debug(logMessage.toString()); + } + + User authzUser = accessTokenDO.getAuthzUser(); + RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); + + int tenantId = realmService.getTenantManager(). + getTenantId(authzUser.getTenantDomain()); + + if (tenantId == 0 || tenantId == -1) { + tenantId = IdentityTenantUtil.getTenantIdOfUser(authzUser.getUserName()); + } + + UserStoreManager userStoreManager; + String[] userRoles; + boolean tenantFlowStarted = false; + + try{ + //If this is a tenant user + if(tenantId != MultitenantConstants.SUPER_TENANT_ID){ + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + realmService.getTenantManager().getDomain(tenantId),true); + tenantFlowStarted = true; + } + + userStoreManager = realmService.getTenantUserRealm(tenantId).getUserStoreManager(); + userRoles = userStoreManager.getRoleListOfUser( + MultitenantUtils.getTenantAwareUsername(authzUser.getUserName())); + } finally { + if (tenantFlowStarted) { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + if(userRoles != null && userRoles.length > 0){ + if(log.isDebugEnabled()){ + StringBuilder logMessage = new StringBuilder("Found roles of user "); + logMessage.append(authzUser.getUserName()); + logMessage.append(" "); + for(String role : userRoles){ + logMessage.append(role); + logMessage.append(", "); + } + log.debug(logMessage.toString()); + } + //Check if the user still has a valid role for this scope. + rolesOfScope.retainAll(Arrays.asList(userRoles)); + return !rolesOfScope.isEmpty(); + } + else{ + if(log.isDebugEnabled()){ + log.debug("No roles associated for the user " + authzUser.getUserName()); + } + return false; + } + + } catch (UserStoreException e) { + //Log and return since we do not want to stop issuing the token in case of scope validation failures. + log.error("Error when getting the tenant's UserStoreManager or when getting roles of user ", e); + return false; + } + } +}