Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist OIDC claims before scope to claim mapping #2595

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1081,8 +1083,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 @@ -1248,13 +1265,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 @@ -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;
Expand Down Expand Up @@ -2107,13 +2110,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 @@ -36,6 +36,9 @@
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
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 @@ -47,6 +50,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 @@ -56,6 +60,7 @@
import org.wso2.carbon.identity.oauth2.dao.TokenManagementDAOImpl;
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
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 @@ -90,8 +95,14 @@
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.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 @@ -1095,4 +1106,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
Loading