Skip to content

Commit

Permalink
Merge pull request #2595 from amanda-ariyaratne/issue-26702
Browse files Browse the repository at this point in the history
Persist OIDC claims before scope to claim mapping
  • Loading branch information
amanda-ariyaratne authored Nov 1, 2024
2 parents 7acf35a + 1ce8cf2 commit 24a8d22
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ExternalClaim> oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT,
tenantDomain);
List<String> 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);
Expand Down Expand Up @@ -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<ExternalClaim> oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT,
tenantDomain);
List<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -124,7 +125,7 @@ public class OAuth2ServiceComponentHolder {
private List<ImpersonationValidator> impersonationValidators = new ArrayList<>();
private ConfigurationManager configurationManager;
private static AccountLockService accountLockService;

private ClaimMetadataManagementService claimMetadataManagementService;

private OAuth2ServiceComponentHolder() {

Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,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.IdentityKeyStoreResolver;
import org.wso2.carbon.identity.core.ServiceURLBuilder;
Expand Down Expand Up @@ -2100,13 +2103,26 @@ public static void initiateOIDCScopes(int tenantId) {

List<ScopeDTO> scopeClaimsList = OAuth2ServiceComponentHolder.getInstance().getOIDCScopesClaims();
try {
String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId);
ClaimMetadataManagementService claimService = OAuth2ServiceComponentHolder.getInstance()
.getClaimMetadataManagementService();
List<ExternalClaim> oidcDialectClaims = claimService.getExternalClaims(OAuthConstants.OIDC_DIALECT,
tenantDomain);
Set<String> 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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 {

Expand Down Expand Up @@ -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<ExternalClaim> oidcDialectClaims) throws Exception {

try (MockedStatic<OAuthTokenPersistenceFactory> mockedOAuthTokenPersistenceFactory =
mockStatic(OAuthTokenPersistenceFactory.class);
MockedStatic<OAuth2ServiceComponentHolder> 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<ExternalClaim> oidcDialectClaims) throws Exception {

try (MockedStatic<OAuthTokenPersistenceFactory> mockedOAuthTokenPersistenceFactory =
mockStatic(OAuthTokenPersistenceFactory.class);
MockedStatic<OAuth2ServiceComponentHolder> 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<ExternalClaim> 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}
};
}
}
Loading

0 comments on commit 24a8d22

Please sign in to comment.