From 04cb15abea14c3ac668770e89a5a6b18baf3ce52 Mon Sep 17 00:00:00 2001 From: ChanikaRuchini Date: Fri, 6 Oct 2023 20:56:03 +0530 Subject: [PATCH] Restrict specific claim management endpoints for sub-organizations --- .../pom.xml | 5 ++ .../common/ClaimManagementDataHolder.java | 24 ++++++++- .../claim/management/common/Constant.java | 9 +++- ...anizationManagementOSGIServiceFactory.java | 54 +++++++++++++++++++ .../pom.xml | 5 ++ .../v1/core/ServerClaimManagementService.java | 41 +++++++++++++- .../cxf/claim-management-server-v1-cxf.xml | 8 ++- pom.xml | 4 +- 8 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/factory/OrganizationManagementOSGIServiceFactory.java diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/pom.xml b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/pom.xml index 25e8e7cb74..7d6eec20e8 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/pom.xml @@ -45,6 +45,11 @@ org.wso2.carbon.identity.claim.metadata.mgt provided + + org.wso2.carbon.identity.organization.management.core + org.wso2.carbon.identity.organization.management.service + provided + org.springframework spring-web diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/ClaimManagementDataHolder.java b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/ClaimManagementDataHolder.java index 34db4413eb..6dda201330 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/ClaimManagementDataHolder.java +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/ClaimManagementDataHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) (2019-2023), WSO2 LLC. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.wso2.carbon.identity.api.server.claim.management.common; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.organization.management.service.OrganizationManager; /** * Service holder class for identity governance. @@ -24,6 +25,7 @@ public class ClaimManagementDataHolder { private static ClaimMetadataManagementService claimMetadataManagementService; + private static OrganizationManager organizationManager; public static ClaimMetadataManagementService getClaimMetadataManagementService() { @@ -35,4 +37,24 @@ public static void setClaimMetadataManagementService( ClaimManagementDataHolder.claimMetadataManagementService = claimMetadataManagementService; } + + /** + * Get organizationManager OSGi service. + * + * @return organization Manager. + */ + public static OrganizationManager getOrganizationManager() { + + return organizationManager; + } + + /** + * Set organizationManager OSGi service. + * + * @param organizationManager Organization Manager. + */ + public static void setOrganizationManager(OrganizationManager organizationManager) { + + ClaimManagementDataHolder.organizationManager = organizationManager; + } } diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/Constant.java b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/Constant.java index a9863bd7af..75e33ecc37 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/Constant.java +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/Constant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) (2019-2023), WSO2 LLC. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -201,7 +201,12 @@ public enum ErrorMessage { ERROR_CODE_USERSTORE_NOT_SPECIFIED_IN_MAPPINGS("CMT-60005", "Userstore not specified", "Mapped userstore cannot be empty"), ERROR_CODE_EMPTY_ATTRIBUTE_MAPPINGS("CMT-60006", "Attribute mapping not specified", - "Attribute mapping cannot be empty"); + "Attribute mapping cannot be empty"), + ERROR_CODE_UNAUTHORIZED_ORG_FOR_CLAIM_MANAGEMENT("CMT-60007", "Claim modification is not " + + "allowed for this organization.", "Unable to execute the requested organization claim " + + "management task."), + ERROR_CODE_ERROR_RESOLVING_ORGANIZATION("CMT-60008", "Error resolving organization", + "Unable to resolve organization."); private final String code; private final String message; diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/factory/OrganizationManagementOSGIServiceFactory.java b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/factory/OrganizationManagementOSGIServiceFactory.java new file mode 100644 index 0000000000..cec4ed1e4f --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.api.server.claim.management.common/src/main/java/org/wso2/carbon/identity/api/server/claim/management/common/factory/OrganizationManagementOSGIServiceFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.claim.management.common.factory; + +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.organization.management.service.OrganizationManager; + +/** + * Factory Beans serves as a factory for creating other beans within the IOC container. This factory bean is used to + * instantiate the OrganizationManagementService type of object inside the container. + */ +public class OrganizationManagementOSGIServiceFactory extends AbstractFactoryBean { + + private OrganizationManager organizationManager; + + @Override + public Class getObjectType() { + + return Object.class; + } + + @Override + protected OrganizationManager createInstance() throws Exception { + + if (this.organizationManager == null) { + OrganizationManager orgMgtService = (OrganizationManager) PrivilegedCarbonContext. + getThreadLocalCarbonContext().getOSGiService(OrganizationManager.class, null); + if (orgMgtService == null) { + throw new Exception("Unable to retrieve Organization Management service."); + } + this.organizationManager = orgMgtService; + } + return this.organizationManager; + } +} + + diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/pom.xml index 3ae82b772c..fedaa1ced4 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/pom.xml @@ -140,5 +140,10 @@ org.wso2.carbon.identity.api.server.claim.management.common provided + + org.wso2.carbon.identity.organization.management.core + org.wso2.carbon.identity.organization.management.service + provided + diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/java/org/wso2/carbon/identity/rest/api/server/claim/management/v1/core/ServerClaimManagementService.java b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/java/org/wso2/carbon/identity/rest/api/server/claim/management/v1/core/ServerClaimManagementService.java index 51369de3a1..698a99da19 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/java/org/wso2/carbon/identity/rest/api/server/claim/management/v1/core/ServerClaimManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/java/org/wso2/carbon/identity/rest/api/server/claim/management/v1/core/ServerClaimManagementService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) (2019-2023), WSO2 LLC. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.api.server.claim.management.common.Constant; import org.wso2.carbon.identity.api.server.common.ContextLoader; import org.wso2.carbon.identity.api.server.common.FileContent; @@ -43,6 +44,7 @@ import org.wso2.carbon.identity.claim.metadata.mgt.model.LocalClaim; import org.wso2.carbon.identity.claim.metadata.mgt.util.ClaimConstants; import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.identity.rest.api.server.claim.management.v1.dto.AttributeMappingDTO; import org.wso2.carbon.identity.rest.api.server.claim.management.v1.dto.ClaimDialectReqDTO; import org.wso2.carbon.identity.rest.api.server.claim.management.v1.dto.ClaimDialectResDTO; @@ -85,6 +87,7 @@ import javax.xml.bind.Unmarshaller; import static org.wso2.carbon.identity.api.server.claim.management.common.ClaimManagementDataHolder.getClaimMetadataManagementService; +import static org.wso2.carbon.identity.api.server.claim.management.common.ClaimManagementDataHolder.getOrganizationManager; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.CMT_PATH_COMPONENT; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_ATTRIBUTE_FILTERING_NOT_IMPLEMENTED; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_CLAIMS_NOT_FOUND_FOR_DIALECT; @@ -115,6 +118,7 @@ import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_LOCAL_CLAIM_NOT_FOUND; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_PAGINATION_NOT_IMPLEMENTED; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_SORTING_NOT_IMPLEMENTED; +import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_UNAUTHORIZED_ORG_FOR_CLAIM_MANAGEMENT; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.ErrorMessage.ERROR_CODE_USERSTORE_NOT_SPECIFIED_IN_MAPPINGS; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.LOCAL_DIALECT; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.LOCAL_DIALECT_PATH; @@ -133,6 +137,7 @@ import static org.wso2.carbon.identity.api.server.common.Constants.XML_FILE_EXTENSION; import static org.wso2.carbon.identity.api.server.common.Constants.YAML_FILE_EXTENSION; import static org.wso2.carbon.identity.api.server.common.ContextLoader.buildURIForBody; +import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.ErrorMessages.ERROR_CODE_ORGANIZATION_NOT_FOUND_FOR_TENANT; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.CONFLICT; @@ -162,6 +167,7 @@ public class ServerClaimManagementService { public String addClaimDialect(ClaimDialectReqDTO claimDialectReqDTO) { try { + validateClaimModificationEligibility(); getClaimMetadataManagementService().addClaimDialect( createClaimDialect(claimDialectReqDTO), ContextLoader.getTenantDomainFromContext()); @@ -182,6 +188,7 @@ public String addClaimDialect(ClaimDialectReqDTO claimDialectReqDTO) { public String addClaimDialect(String dialectURI) { try { + validateClaimModificationEligibility(); getClaimMetadataManagementService().addClaimDialect( createClaimDialect(dialectURI), ContextLoader.getTenantDomainFromContext()); @@ -201,6 +208,7 @@ public void deleteClaimDialect(String dialectId) { String claimDialectURI; try { + validateClaimModificationEligibility(); claimDialectURI = base64DecodeId(dialectId); } catch (Exception ignored) { // Ignoring the delete operation and return 204 response code, since the resource does not exist. @@ -281,6 +289,7 @@ public List getClaimDialects(Integer limit, Integer offset, public String updateClaimDialect(String dialectId, ClaimDialectReqDTO claimDialectReqDTO) { try { + validateClaimModificationEligibility(); // If the old and new dialect uri is the same we don't need to do a db update. if (!StringUtils.equals(base64DecodeId(dialectId), claimDialectReqDTO.getDialectURI())) { getClaimMetadataManagementService().renameClaimDialect( @@ -320,6 +329,7 @@ public String addLocalClaim(LocalClaimReqDTO localClaimReqDTO) { } try { + validateClaimModificationEligibility(); validateAttributeMappings(localClaimReqDTO.getAttributeMapping()); getClaimMetadataManagementService().addLocalClaim(createLocalClaim(localClaimReqDTO), ContextLoader.getTenantDomainFromContext()); @@ -343,6 +353,7 @@ public void deleteLocalClaim(String claimId) { String claimURI; try { + validateClaimModificationEligibility(); claimURI = base64DecodeId(claimId); } catch (Exception ignored) { // Ignoring the delete operation and return 204 response code, since the resource does not exist. @@ -425,6 +436,7 @@ public List getLocalClaims(Boolean excludeIdentityClaims, Stri public void updateLocalClaim(String claimId, LocalClaimReqDTO localClaimReqDTO) { try { + validateClaimModificationEligibility(); if (!StringUtils.equals(base64DecodeId(claimId), localClaimReqDTO.getClaimURI())) { throw handleClaimManagementClientError(ERROR_CODE_LOCAL_CLAIM_CONFLICT, CONFLICT, base64DecodeId(claimId)); @@ -699,6 +711,7 @@ private FileContent parseClaimDialectToYaml(ClaimDialectConfiguration dialectCon public String addExternalClaim(String dialectId, ExternalClaimReqDTO externalClaimReqDTO) { try { + validateClaimModificationEligibility(); if (!isDialectExists(dialectId)) { throw handleClaimManagementClientError(ERROR_CODE_INVALID_DIALECT_ID, NOT_FOUND, dialectId); } @@ -725,6 +738,7 @@ public void deleteExternalClaim(String dialectId, String claimId) { String externalClaimURI; String externalClaimDialectURI; try { + validateClaimModificationEligibility(); externalClaimURI = base64DecodeId(claimId); externalClaimDialectURI = base64DecodeId(dialectId); } catch (Exception ignored) { @@ -820,6 +834,7 @@ public List getExternalClaims(String dialectId, Integer lim public void updateExternalClaim(String dialectId, String claimId, ExternalClaimReqDTO externalClaimReqDTO) { try { + validateClaimModificationEligibility(); if (!StringUtils.equals(base64DecodeId(claimId), externalClaimReqDTO.getClaimURI())) { throw handleClaimManagementClientError(ERROR_CODE_EXTERNAL_CLAIM_CONFLICT, CONFLICT, base64DecodeId(claimId), dialectId); @@ -1444,4 +1459,28 @@ private void validateAttributeMappings(List attributeMappin } } } + + private void validateClaimModificationEligibility() throws ClaimMetadataClientException { + + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + try { + String organizationId = getOrganizationManager().resolveOrganizationId(tenantDomain); + boolean isPrimaryOrg = getOrganizationManager().isPrimaryOrganization(organizationId); + if (!isPrimaryOrg) { + throw handleClaimManagementClientError(ERROR_CODE_UNAUTHORIZED_ORG_FOR_CLAIM_MANAGEMENT, FORBIDDEN, + organizationId); + } + } catch (OrganizationManagementException e) { + // This is to handle the scenario where the tenant is not modeled as an organization. + if (ERROR_CODE_ORGANIZATION_NOT_FOUND_FOR_TENANT.getCode().equals(e.getErrorCode())) { + if (LOG.isDebugEnabled()) { + LOG.debug("Organization not found for the tenant: " + tenantDomain); + } + return; + } + throw new ClaimMetadataClientException(Constant.ErrorMessage.ERROR_CODE_ERROR_RESOLVING_ORGANIZATION. + getCode(), Constant.ErrorMessage.ERROR_CODE_ERROR_RESOLVING_ORGANIZATION.getDescription()); + } + + } } diff --git a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/resources/META-INF/cxf/claim-management-server-v1-cxf.xml b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/resources/META-INF/cxf/claim-management-server-v1-cxf.xml index eb6581cfbc..8997dfc24f 100644 --- a/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/resources/META-INF/cxf/claim-management-server-v1-cxf.xml +++ b/components/org.wso2.carbon.identity.api.server.claim.management/org.wso2.carbon.identity.rest.api.server.claim.management.v1/src/main/resources/META-INF/cxf/claim-management-server-v1-cxf.xml @@ -1,6 +1,6 @@ - 1.0.56 + 1.0.64 [1.0.0, 2.0.0) - 1.3.70 + 1.3.73