diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java index 1d75ab5aa3..8e94858554 100755 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java @@ -42,7 +42,9 @@ import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; +import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; +import org.wso2.carbon.identity.claim.metadata.mgt.model.ExternalClaim; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.cache.AppInfoCache; @@ -1085,8 +1087,23 @@ public void addScope(ScopeDTO scope) throws IdentityOAuthAdminException { addScopePreValidation(scope); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId); + ClaimMetadataManagementService claimService = OAuth2ServiceComponentHolder.getInstance() + .getClaimMetadataManagementService(); try { + List oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT, + tenantDomain); + List oidcClaimsMappedToScopes = Arrays.asList(scope.getClaim()); + for (ExternalClaim oidcClaim : oidcDialectClaims) { + if (oidcClaimsMappedToScopes.contains(oidcClaim.getClaimURI())) { + claimService.updateExternalClaim(oidcClaim, tenantDomain); + } + } OAuthTokenPersistenceFactory.getInstance().getScopeClaimMappingDAO().addScope(scope, tenantId); + } catch (ClaimMetadataException e) { + IdentityOAuth2Exception identityOAuth2Exception = new IdentityOAuth2Exception(String.format( + "Error while inserting OIDC scope: %s in tenant: %s", scope.getName(), tenantDomain), e); + throw handleErrorWithExceptionType(identityOAuth2Exception.getMessage(), identityOAuth2Exception); } catch (IdentityOAuth2Exception e) { throw handleErrorWithExceptionType(String.format("Error while inserting OIDC scope: %s, %s", scope.getName(), e.getMessage()), e); @@ -1252,13 +1269,27 @@ public void updateScope(String scope, String[] addClaims, String[] deleteClaims) public void updateScope(ScopeDTO updatedScope) throws IdentityOAuthAdminException { updateScopePreValidation(updatedScope); - // Check whether a scope exists with the provided scope name which to be deleted. + // Check whether a scope exists with the provided scope name which to be updated. validateScopeExistence(updatedScope.getName()); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId); + ClaimMetadataManagementService claimService = OAuth2ServiceComponentHolder.getInstance() + .getClaimMetadataManagementService(); try { - OAuthTokenPersistenceFactory.getInstance().getScopeClaimMappingDAO(). - updateScope(updatedScope, tenantId); + List oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT, + tenantDomain); + List oidcClaimsMappedToScopes = Arrays.asList(updatedScope.getClaim()); + for (ExternalClaim oidcClaim : oidcDialectClaims) { + if (oidcClaimsMappedToScopes.contains(oidcClaim.getClaimURI())) { + claimService.updateExternalClaim(oidcClaim, tenantDomain); + } + } + OAuthTokenPersistenceFactory.getInstance().getScopeClaimMappingDAO().updateScope(updatedScope, tenantId); + } catch (ClaimMetadataException e) { + IdentityOAuth2Exception identityOAuth2Exception = new IdentityOAuth2Exception(String.format( + "Error while updating the scope: %s in tenant: %s", updatedScope.getName(), tenantId), e); + throw handleErrorWithExceptionType(identityOAuth2Exception.getMessage(), identityOAuth2Exception); } catch (IdentityOAuth2Exception e) { throw handleErrorWithExceptionType(String.format("Error while updating the scope: %s in tenant: %s", updatedScope.getName(), tenantId), e); 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 4e3a7cd996..ef4ebcd277 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 @@ -40,6 +40,7 @@ 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.claim.metadata.mgt.ClaimMetadataManagementService; import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager; import org.wso2.carbon.identity.consent.server.configs.mgt.services.ConsentServerConfigsManagementService; import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager; @@ -1632,4 +1633,33 @@ protected void unsetAccountLockService(AccountLockService accountLockService) { OAuth2ServiceComponentHolder.setAccountLockService(null); log.debug("AccountLockService unset in OAuth2ServiceComponent bundle."); } + + /** + * Set the ClaimMetadataManagementService. + * + * @param claimMetadataManagementService The {@code ClaimMetadataManagementService} instance. + */ + @Reference( + name = "claim.metadata.mgt.service", + service = ClaimMetadataManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unregisterClaimMetadataManagementService" + ) + protected void registerClaimMetadataManagementService( + ClaimMetadataManagementService claimMetadataManagementService) { + + OAuth2ServiceComponentHolder.getInstance().setClaimMetadataManagementService(claimMetadataManagementService); + } + + /** + * Unset the ClaimMetadataManagementService. + * + * @param claimMetadataManagementService The {@code ClaimMetadataManagementService} instance. + */ + protected void unregisterClaimMetadataManagementService( + ClaimMetadataManagementService claimMetadataManagementService) { + + OAuth2ServiceComponentHolder.getInstance().setClaimMetadataManagementService(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 1f5f5b0b95..efa96b896e 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 @@ -24,6 +24,7 @@ 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.claim.metadata.mgt.ClaimMetadataManagementService; import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager; import org.wso2.carbon.identity.consent.server.configs.mgt.services.ConsentServerConfigsManagementService; import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager; @@ -124,7 +125,7 @@ public class OAuth2ServiceComponentHolder { private List impersonationValidators = new ArrayList<>(); private ConfigurationManager configurationManager; private static AccountLockService accountLockService; - + private ClaimMetadataManagementService claimMetadataManagementService; private OAuth2ServiceComponentHolder() { @@ -911,4 +912,24 @@ public static AccountLockService getAccountLockService() { return OAuth2ServiceComponentHolder.accountLockService; } + + /** + * Set the ClaimMetadataManagementService instance. + * + * @param claimMetadataManagementService ClaimMetadataManagementService instance. + */ + public void setClaimMetadataManagementService(ClaimMetadataManagementService claimMetadataManagementService) { + + this.claimMetadataManagementService = claimMetadataManagementService; + } + + /** + * Get the ClaimMetadataManagementService instance. + * + * @return ClaimMetadataManagementService instance. + */ + public ClaimMetadataManagementService getClaimMetadataManagementService() { + + return claimMetadataManagementService; + } } 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 f2a46773f6..08982d55aa 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 @@ -84,6 +84,9 @@ import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; +import org.wso2.carbon.identity.claim.metadata.mgt.model.ExternalClaim; import org.wso2.carbon.identity.consent.server.configs.mgt.exceptions.ConsentServerConfigsMgtException; import org.wso2.carbon.identity.core.ServiceURLBuilder; import org.wso2.carbon.identity.core.URLBuilderException; @@ -2105,13 +2108,26 @@ public static void initiateOIDCScopes(int tenantId) { List scopeClaimsList = OAuth2ServiceComponentHolder.getInstance().getOIDCScopesClaims(); try { + String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId); + ClaimMetadataManagementService claimService = OAuth2ServiceComponentHolder.getInstance() + .getClaimMetadataManagementService(); + List oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT, + tenantDomain); + Set oidcClaimsMappedToScopes = scopeClaimsList.stream() + .flatMap(scopeDTO -> Arrays.stream(scopeDTO.getClaim())) + .collect(Collectors.toSet()); + for (ExternalClaim oidcClaim : oidcDialectClaims) { + if (oidcClaimsMappedToScopes.contains(oidcClaim.getClaimURI())) { + claimService.updateExternalClaim(oidcClaim, tenantDomain); + } + } OAuthTokenPersistenceFactory.getInstance().getScopeClaimMappingDAO().initScopeClaimMapping(tenantId, scopeClaimsList); } catch (IdentityOAuth2ClientException e) { if (log.isDebugEnabled()) { log.debug(e.getMessage(), e); } - } catch (IdentityOAuth2Exception e) { + } catch (IdentityOAuth2Exception | ClaimMetadataException e) { log.error(e.getMessage(), e); } } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java index b1dedc37d1..42b7ce4ac7 100755 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java @@ -37,6 +37,9 @@ import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.common.model.ServiceProvider; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; +import org.wso2.carbon.identity.claim.metadata.mgt.model.ExternalClaim; import org.wso2.carbon.identity.core.internal.IdentityCoreServiceComponent; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; @@ -49,6 +52,7 @@ import org.wso2.carbon.identity.oauth.dto.OAuthAppRevocationRequestDTO; import org.wso2.carbon.identity.oauth.dto.OAuthConsumerAppDTO; import org.wso2.carbon.identity.oauth.dto.OAuthRevocationResponseDTO; +import org.wso2.carbon.identity.oauth.dto.ScopeDTO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.TestConstants; @@ -59,6 +63,7 @@ import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.openidconnect.dao.ScopeClaimMappingDAO; import org.wso2.carbon.user.api.RealmConfiguration; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserRealm; @@ -94,9 +99,15 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; +import static org.testng.Assert.assertThrows; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.ENABLE_CLAIMS_SEPARATION_FOR_ACCESS_TOKEN; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDC_DIALECT; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; public class OAuthAdminServiceImplTest { @@ -1177,4 +1188,117 @@ private Object invokePrivateMethod(Object object, String methodName, Class[] method.setAccessible(true); return method.invoke(object, params); } + + @Test(dataProvider = "addScopeDataProvider") + public void testAddScope(ScopeDTO scope, List oidcDialectClaims) throws Exception { + + try (MockedStatic mockedOAuthTokenPersistenceFactory = + mockStatic(OAuthTokenPersistenceFactory.class); + MockedStatic mockedOAuth2ServiceComponentHolder = + mockStatic(OAuth2ServiceComponentHolder.class)) { + + OAuth2ServiceComponentHolder mockServiceComponentHolder = mock(OAuth2ServiceComponentHolder.class); + OAuthTokenPersistenceFactory mockTokenPersistenceFactory = mock(OAuthTokenPersistenceFactory.class); + ClaimMetadataManagementService claimService = mock(ClaimMetadataManagementService.class); + ScopeClaimMappingDAO scopeClaimMappingDAO = mock(ScopeClaimMappingDAO.class); + + mockedOAuthTokenPersistenceFactory.when(OAuthTokenPersistenceFactory::getInstance) + .thenReturn(mockTokenPersistenceFactory); + mockedOAuth2ServiceComponentHolder.when(OAuth2ServiceComponentHolder::getInstance) + .thenReturn(mockServiceComponentHolder); + + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(SUPER_TENANT_ID); + identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(SUPER_TENANT_ID)) + .thenReturn(SUPER_TENANT_DOMAIN_NAME); + when(mockTokenPersistenceFactory.getScopeClaimMappingDAO()).thenReturn(scopeClaimMappingDAO); + doNothing().when(scopeClaimMappingDAO).addScope(scope, SUPER_TENANT_ID); + when(mockServiceComponentHolder.getClaimMetadataManagementService()).thenReturn(claimService); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)).thenReturn(oidcDialectClaims); + + OAuthAdminServiceImpl service = new OAuthAdminServiceImpl(); + service.addScope(scope); + verify(scopeClaimMappingDAO, times(1)).addScope(any(), anyInt()); + verify(claimService, times(2)).updateExternalClaim(any(), anyString()); + + ClaimMetadataException claimMetadataException = new ClaimMetadataException("error"); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)) + .thenThrow(claimMetadataException); + assertThrows(IdentityOAuthAdminException.class, () -> service.addScope(scope)); + } + } + + @Test(dataProvider = "addScopeDataProvider") + public void testUpdateScope(ScopeDTO scope, List oidcDialectClaims) throws Exception { + + try (MockedStatic mockedOAuthTokenPersistenceFactory = + mockStatic(OAuthTokenPersistenceFactory.class); + MockedStatic mockedOAuth2ServiceComponentHolder = + mockStatic(OAuth2ServiceComponentHolder.class)) { + + OAuth2ServiceComponentHolder mockServiceComponentHolder = mock(OAuth2ServiceComponentHolder.class); + OAuthTokenPersistenceFactory mockTokenPersistenceFactory = mock(OAuthTokenPersistenceFactory.class); + ClaimMetadataManagementService claimService = mock(ClaimMetadataManagementService.class); + ScopeClaimMappingDAO scopeClaimMappingDAO = mock(ScopeClaimMappingDAO.class); + + mockedOAuthTokenPersistenceFactory.when(OAuthTokenPersistenceFactory::getInstance) + .thenReturn(mockTokenPersistenceFactory); + mockedOAuth2ServiceComponentHolder.when(OAuth2ServiceComponentHolder::getInstance) + .thenReturn(mockServiceComponentHolder); + + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(SUPER_TENANT_ID); + identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(SUPER_TENANT_ID)) + .thenReturn(SUPER_TENANT_DOMAIN_NAME); + when(mockTokenPersistenceFactory.getScopeClaimMappingDAO()).thenReturn(scopeClaimMappingDAO); + doNothing().when(scopeClaimMappingDAO).addScope(scope, SUPER_TENANT_ID); + when(scopeClaimMappingDAO.isScopeExist(any(), anyInt())).thenReturn(true); + when(mockServiceComponentHolder.getClaimMetadataManagementService()).thenReturn(claimService); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)).thenReturn(oidcDialectClaims); + + OAuthAdminServiceImpl service = new OAuthAdminServiceImpl(); + service.updateScope(scope); + verify(scopeClaimMappingDAO, times(1)).updateScope(any(), anyInt()); + verify(claimService, times(2)).updateExternalClaim(any(), anyString()); + + ClaimMetadataException claimMetadataException = new ClaimMetadataException("error"); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)) + .thenThrow(claimMetadataException); + assertThrows(IdentityOAuthAdminException.class, () -> service.addScope(scope)); + } + } + + @DataProvider(name = "addScopeDataProvider") + public Object[][] addScopeDataProvider() { + + ScopeDTO scope = new ScopeDTO(); + scope.setName("dummy_claim"); + scope.setDisplayName("Dummy Claim"); + scope.setDescription("Dummy Claim Description"); + scope.setClaim(new String[] { + "http://wso2.org/oidc/claim/email", + "http://wso2.org/oidc/claim/profile" + }); + List oidcDialectClaims = new ArrayList<>(); + + ExternalClaim claim1 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/email", "http://wso2.org/claims/emailaddress"); + ExternalClaim claim2 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/profile", "http://wso2.org/claims/url"); + ExternalClaim claim3 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/first_name", "http://wso2.org/claims/givenname"); + ExternalClaim claim4 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/last_name", "http://wso2.org/claims/lastname"); + ExternalClaim claim5 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/phone_number", "http://wso2.org/claims/mobile"); + + oidcDialectClaims.add(claim1); + oidcDialectClaims.add(claim2); + oidcDialectClaims.add(claim3); + oidcDialectClaims.add(claim4); + oidcDialectClaims.add(claim5); + + return new Object[][]{ + {scope, oidcDialectClaims} + }; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java index 8ee514e70d..16b1a61d69 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java @@ -26,6 +26,8 @@ import org.apache.axis2.engine.AxisConfiguration; import org.apache.axis2.transport.http.HTTPConstants; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.utils.OAuthUtils; import org.mockito.Mock; import org.mockito.MockedConstruction; @@ -52,6 +54,9 @@ import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; +import org.wso2.carbon.identity.claim.metadata.mgt.model.ExternalClaim; import org.wso2.carbon.identity.common.testng.WithCarbonHome; import org.wso2.carbon.identity.core.internal.IdentityCoreServiceComponent; import org.wso2.carbon.identity.core.util.IdentityConfigParser; @@ -102,6 +107,7 @@ import org.wso2.carbon.utils.NetworkUtils; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.security.KeyStore; @@ -124,9 +130,12 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -135,8 +144,11 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OAuthError.AuthorizationResponsei18nKey.APPLICATION_NOT_FOUND; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDC_DIALECT; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.getIdTokenIssuer; import static org.wso2.carbon.identity.openidconnect.util.TestUtils.getKeyStoreFromFile; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; @Listeners(MockitoTestNGListener.class) @WithCarbonHome @@ -223,9 +235,6 @@ public class OAuth2UtilTest { @Mock private AccessTokenDAO accessTokenDAO; - @Mock - OAuth2ServiceComponentHolder mockOAuth2ServiceComponentHolder; - @Mock OAuthAdminServiceImpl oAuthAdminService; @@ -2838,6 +2847,107 @@ public void getSupportedTokenBindingTypes() { Assert.assertEquals(supportedTokenBindingTypes.size(), 3); } + @Test(dataProvider = "initiateOIDCScopesDataProvider") + public void testInitiateOIDCScopes(List scopeClaimsList, List oidcDialectClaims) + throws Exception { + + try (MockedStatic mockedOAuthTokenPersistenceFactory = + mockStatic(OAuthTokenPersistenceFactory.class); + MockedStatic mockedOAuth2ServiceComponentHolder = + mockStatic(OAuth2ServiceComponentHolder.class); + MockedStatic mockedLogFactory = mockStatic(LogFactory.class)) { + + OAuth2ServiceComponentHolder mockServiceComponentHolder = mock(OAuth2ServiceComponentHolder.class); + OAuthTokenPersistenceFactory mockTokenPersistenceFactory = mock(OAuthTokenPersistenceFactory.class); + ClaimMetadataManagementService claimService = mock(ClaimMetadataManagementService.class); + ScopeClaimMappingDAO scopeClaimMappingDAO = mock(ScopeClaimMappingDAO.class); + Log log = mock(Log.class); + + mockedOAuthTokenPersistenceFactory.when(OAuthTokenPersistenceFactory::getInstance) + .thenReturn(mockTokenPersistenceFactory); + mockedOAuth2ServiceComponentHolder.when(OAuth2ServiceComponentHolder::getInstance) + .thenReturn(mockServiceComponentHolder); + mockedLogFactory.when(() -> LogFactory.getLog(any(Class.class))).thenReturn(log); + setPrivateStaticFinalField(OAuth2Util.class, "log", log); + + when(mockTokenPersistenceFactory.getScopeClaimMappingDAO()).thenReturn(scopeClaimMappingDAO); + doNothing().when(scopeClaimMappingDAO).initScopeClaimMapping(SUPER_TENANT_ID, scopeClaimsList); + when(mockServiceComponentHolder.getClaimMetadataManagementService()).thenReturn(claimService); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)).thenReturn(oidcDialectClaims); + when(mockServiceComponentHolder.getOIDCScopesClaims()).thenReturn(scopeClaimsList); + + OAuth2Util.initiateOIDCScopes(SUPER_TENANT_ID); + verify(scopeClaimMappingDAO, times(1)) + .initScopeClaimMapping(SUPER_TENANT_ID, scopeClaimsList); + verify(claimService, times(4)).updateExternalClaim(any(), anyString()); + + ClaimMetadataException claimMetadataException = new ClaimMetadataException("error"); + when(claimService.getExternalClaims(OIDC_DIALECT, SUPER_TENANT_DOMAIN_NAME)) + .thenThrow(claimMetadataException); + OAuth2Util.initiateOIDCScopes(SUPER_TENANT_ID); + verify(log, times(1)) + .error(claimMetadataException.getMessage(), claimMetadataException); + } + } + + @DataProvider(name = "initiateOIDCScopesDataProvider") + public Object[][] initiateOIDCScopesDataProvider() { + + List scopeClaimsList = new ArrayList<>(); + + ScopeDTO scope1 = new ScopeDTO(); + scope1.setName("openid"); + scope1.setDescription("OpenID scope"); + scope1.setClaim(new String[] { + "http://wso2.org/oidc/claim/email", + "http://wso2.org/oidc/claim/profile" + }); + + ScopeDTO scope2 = new ScopeDTO(); + scope2.setName("profile"); + scope2.setDescription("Profile scope"); + scope2.setClaim(new String[] { + "http://wso2.org/oidc/claim/first_name", + "http://wso2.org/oidc/claim/last_name", + "http://wso2.org/oidc/claim/profile" + }); + + ScopeDTO scope3 = new ScopeDTO(); + scope3.setName("email"); + scope3.setDescription("Email scope"); + scope3.setClaim(new String[] { + "http://wso2.org/oidc/claim/email", + "http://wso2.org/oidc/claim/email_verified" + }); + + scopeClaimsList.add(scope1); + scopeClaimsList.add(scope2); + scopeClaimsList.add(scope3); + + List oidcDialectClaims = new ArrayList<>(); + + ExternalClaim claim1 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/email", "http://wso2.org/claims/emailaddress"); + ExternalClaim claim2 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/profile", "http://wso2.org/claims/url"); + ExternalClaim claim3 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/first_name", "http://wso2.org/claims/givenname"); + ExternalClaim claim4 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/last_name", "http://wso2.org/claims/lastname"); + ExternalClaim claim5 = new ExternalClaim("http://wso2.org/oidc", + "http://wso2.org/oidc/claim/phone_number", "http://wso2.org/claims/mobile"); + + oidcDialectClaims.add(claim1); + oidcDialectClaims.add(claim2); + oidcDialectClaims.add(claim3); + oidcDialectClaims.add(claim4); + oidcDialectClaims.add(claim5); + + return new Object[][]{ + {scopeClaimsList, oidcDialectClaims} + }; + } + @DataProvider(name = "isAppVersionAllowedDataProvider") public Object[][] isAppVersionAllowedDataProvider() { @@ -2863,4 +2973,16 @@ private void setPrivateField(Object object, String fieldName, Object value) thro field.setAccessible(true); field.set(object, value); } + + private void setPrivateStaticFinalField(Class clazz, String fieldName, Object value) throws Exception { + + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + + field.set(null, value); + } }