From 07b2d41b0bf01fc1c7f846d8f5d6b7dd890d8e00 Mon Sep 17 00:00:00 2001 From: SujanSanjula96 Date: Fri, 27 Oct 2023 12:46:08 +0530 Subject: [PATCH 1/3] Fix API Resource UI access control to support system APIs --- .../permission-api-resource.tsx | 2 +- .../components/api-resources-list.tsx | 46 +++++---- .../constants/api-resources-constants.ts | 5 + .../api-resources/models/api-resources.ts | 4 + .../api-resources/pages/api-resource-edit.tsx | 8 +- .../api-resources/utils/api-resource-utils.ts | 99 +++++++++++++++++++ 6 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 apps/console/src/features/api-resources/utils/api-resource-utils.ts diff --git a/apps/console/src/features/api-resources/components/api-resource-panes/permission-api-resource.tsx b/apps/console/src/features/api-resources/components/api-resource-panes/permission-api-resource.tsx index 5575cbac770..42301280d2c 100644 --- a/apps/console/src/features/api-resources/components/api-resource-panes/permission-api-resource.tsx +++ b/apps/console/src/features/api-resources/components/api-resource-panes/permission-api-resource.tsx @@ -137,7 +137,7 @@ export const PermissionAPIResource: FunctionComponent { - permissionList?.length !== 0 + permissionList?.length !== 0 && !isReadOnly && ( = ( { "data-componentid": `${componentId}-item-edit-button`, hidden: () => { - const hasScopesRead: boolean = !hasRequiredScopes(featureConfig?.apiResources, - featureConfig?.apiResources?.scopes?.read, allowedScopes); + const readForbidden: boolean = !APIResourceUtils.isAPIResourceReadAllowed( + featureConfig, allowedScopes); + const updateForbidden: boolean = !APIResourceUtils.isAPIResourceUpdateAllowed( + featureConfig, allowedScopes); - const hasScopesUpdate: boolean = !hasRequiredScopes(featureConfig?.apiResources, - featureConfig?.apiResources?.scopes?.update, allowedScopes); - - return hasScopesUpdate || hasScopesRead; + return readForbidden && updateForbidden; }, - icon: (): SemanticICONS => { - return !hasRequiredScopes(featureConfig?.applications, - featureConfig?.applications?.scopes?.update, allowedScopes) - ? "eye" - : "pencil alternate"; + icon: (apiResource: APIResourceInterface): SemanticICONS => { + const updateAllowed: boolean = APIResourceUtils.isAPIResourceUpdateAllowed( + featureConfig, allowedScopes); + const isSystemAPIResource: boolean = APIResourceUtils.isSystemAPI(apiResource?.type); + const canUpdate: boolean = !isSystemAPIResource && updateAllowed; + + return canUpdate ? "pencil alternate" : "eye"; }, onClick: (e: SyntheticEvent, apiResource: APIResourceInterface): void => { handleAPIResourceEdit(apiResource, e); }, - popupText: (): string => { - return !hasRequiredScopes(featureConfig?.applications, - featureConfig?.applications?.scopes?.update, allowedScopes) - ? t("common:view") - : t("common:edit"); + popupText: (apiResource: APIResourceInterface): string => { + const updateAllowed: boolean = APIResourceUtils.isAPIResourceUpdateAllowed( + featureConfig, allowedScopes); + const isSystemAPIResource: boolean = APIResourceUtils.isSystemAPI(apiResource?.type); + const canUpdate: boolean = !isSystemAPIResource && updateAllowed; + + return canUpdate ? t("common:edit") : t("common:view"); }, renderer: "semantic-icon" }, { "data-componentid": `${componentId}-item-delete-button`, - hidden: () => { - const hasScopes: boolean = !hasRequiredScopes(featureConfig?.apiResources, - featureConfig?.apiResources?.scopes?.delete, allowedScopes); + hidden: (apiResource: APIResourceInterface) => { + const deleteForbidden: boolean = !APIResourceUtils.isAPIResourceDeleteAllowed( + featureConfig, allowedScopes); + const isSystemAPIResource: boolean = APIResourceUtils.isSystemAPI(apiResource?.type); - return hasScopes; + return deleteForbidden || isSystemAPIResource; }, icon: (): SemanticICONS => "trash alternate", onClick: (e: SyntheticEvent, apiResource: APIResourceInterface): void => { diff --git a/apps/console/src/features/api-resources/constants/api-resources-constants.ts b/apps/console/src/features/api-resources/constants/api-resources-constants.ts index ccbbc61b096..6b6b6c84d94 100644 --- a/apps/console/src/features/api-resources/constants/api-resources-constants.ts +++ b/apps/console/src/features/api-resources/constants/api-resources-constants.ts @@ -37,6 +37,11 @@ export class APIResourcesConstants { public static readonly DEFAULT_TOTAL_PAGES: number = 10; public static readonly API_RESOURCE_DIR: string = "api-resources"; + // API Resource types. + public static readonly SYSTEM: string = "SYSTEM"; + public static readonly SYSTEM_ORG: string = "SYSTEM_ORG"; + public static readonly SYSTEM_FEATURE: string = "SYSTEM_FEATURE"; + /** * Get the API resource paths as a map. * diff --git a/apps/console/src/features/api-resources/models/api-resources.ts b/apps/console/src/features/api-resources/models/api-resources.ts index eb7f4c3d6a1..c0681f37861 100644 --- a/apps/console/src/features/api-resources/models/api-resources.ts +++ b/apps/console/src/features/api-resources/models/api-resources.ts @@ -57,6 +57,10 @@ export interface APIResourceInterface { * Required authorization. */ requiresAuthorization?: boolean; + /** + * Type of the API resource. + */ + type?: string; /** * List of scopes associate with the API resource. */ diff --git a/apps/console/src/features/api-resources/pages/api-resource-edit.tsx b/apps/console/src/features/api-resources/pages/api-resource-edit.tsx index 7fb200e51e6..3c7821a3c5b 100644 --- a/apps/console/src/features/api-resources/pages/api-resource-edit.tsx +++ b/apps/console/src/features/api-resources/pages/api-resource-edit.tsx @@ -16,7 +16,6 @@ * under the License. */ -import { hasRequiredScopes } from "@wso2is/core/helpers"; import { AlertInterface, AlertLevels, IdentifiableComponentInterface } from "@wso2is/core/models"; import { addAlert } from "@wso2is/core/store"; import { EmptyPlaceholder, TabPageLayout } from "@wso2is/react-components"; @@ -28,6 +27,7 @@ import { AppState, FeatureConfigInterface,getEmptyPlaceholderIllustrations, hist import { useAPIResourceDetails } from "../api"; import { EditAPIResource } from "../components"; import { APIResourcesConstants } from "../constants"; +import { APIResourceUtils } from "../utils/api-resource-utils"; /** * Prop-types for the API resources page component. @@ -75,8 +75,10 @@ const APIResourcesEditPage: FunctionComponent = ( * The following useEffect is used to handle if the user has the required scopes to update the API resource */ useEffect(() => { - if (!hasRequiredScopes( - featureConfig?.apiResources, featureConfig?.apiResources?.scopes?.update, allowedScopes)) { + const updateForbidden: boolean = !APIResourceUtils.isAPIResourceUpdateAllowed(featureConfig, allowedScopes); + const isSytemAPIResource: boolean = APIResourceUtils.isSystemAPI(apiResourceData?.type); + + if (updateForbidden || isSytemAPIResource) { setReadOnly(true); } }, [ apiResourceData ]); diff --git a/apps/console/src/features/api-resources/utils/api-resource-utils.ts b/apps/console/src/features/api-resources/utils/api-resource-utils.ts new file mode 100644 index 00000000000..d5db49d1377 --- /dev/null +++ b/apps/console/src/features/api-resources/utils/api-resource-utils.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://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. + */ + +import { hasRequiredScopes } from "@wso2is/core/helpers"; +import { APIResourcesConstants } from "../constants/api-resources-constants"; +import { FeatureConfigInterface } from "../../core"; + +export class APIResourceUtils { + + /** + * Private constructor to avoid object instantiation from outside + * the class. + */ + private constructor() { } + + /** + * Check whether the API resource read is allowed. + * + * @param featureConfig - Feature config. + * @param allowedScopes - Allowed scopes. + * @returns True if the API resource read is allowed. + */ + public static isAPIResourceReadAllowed(featureConfig: FeatureConfigInterface, + allowedScopes: string): boolean { + + return hasRequiredScopes(featureConfig?.apiResources, + featureConfig?.apiResources?.scopes?.read, allowedScopes); + } + + /** + * Check whether the API resource update is allowed. + * + * @param featureConfig - Feature config. + * @param allowedScopes - Allowed scopes. + * @returns True if the API resource update is allowed. + */ + public static isAPIResourceUpdateAllowed(featureConfig: FeatureConfigInterface, + allowedScopes: string): boolean { + + return hasRequiredScopes(featureConfig?.apiResources, + featureConfig?.apiResources?.scopes?.update, allowedScopes); + } + + /** + * Check whether the API resource create is allowed. + * + * @param featureConfig - Feature config. + * @param allowedScopes - Allowed scopes. + * @returns True if the API resource create is allowed. + */ + public static isAPIResourceCreateAllowed(featureConfig: FeatureConfigInterface, + allowedScopes: string): boolean { + + return hasRequiredScopes(featureConfig?.apiResources, + featureConfig?.apiResources?.scopes?.create, allowedScopes); + } + + /** + * Check whether the API resource delete is allowed. + * + * @param featureConfig - Feature config. + * @param allowedScopes - Allowed scopes. + * @returns True if the API resource delete is allowed. + */ + public static isAPIResourceDeleteAllowed(featureConfig: FeatureConfigInterface, + allowedScopes: string): boolean { + + return hasRequiredScopes(featureConfig?.apiResources, + featureConfig?.apiResources?.scopes?.delete, allowedScopes); + } + + /** + * Check whether the API resource is a system API. + * + * @param type - API Resource type. + * @returns True if the API resource is a system API. + */ + public static isSystemAPI(type: string): boolean { + + return type === APIResourcesConstants.SYSTEM + || type === APIResourcesConstants.SYSTEM_ORG + || type === APIResourcesConstants.SYSTEM_FEATURE; + } + } From 94cbef42f04dcf5b47ed48fae67459bd38987a8d Mon Sep 17 00:00:00 2001 From: SujanSanjula96 Date: Fri, 27 Oct 2023 12:47:23 +0530 Subject: [PATCH 2/3] =?UTF-8?q?Add=20changeset=20=F0=9F=A6=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/metal-clouds-flash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/metal-clouds-flash.md diff --git a/.changeset/metal-clouds-flash.md b/.changeset/metal-clouds-flash.md new file mode 100644 index 00000000000..d5aad9ec536 --- /dev/null +++ b/.changeset/metal-clouds-flash.md @@ -0,0 +1,5 @@ +--- +"@wso2is/console": patch +--- + +Fix API Resource UI access control to support system APIs From 003ca9383044b4d81bbff410605a49fd17f33bdd Mon Sep 17 00:00:00 2001 From: SujanSanjula96 Date: Fri, 27 Oct 2023 12:56:31 +0530 Subject: [PATCH 3/3] fix lint issues --- .../src/features/api-resources/utils/api-resource-utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/console/src/features/api-resources/utils/api-resource-utils.ts b/apps/console/src/features/api-resources/utils/api-resource-utils.ts index d5db49d1377..21b08f22dc8 100644 --- a/apps/console/src/features/api-resources/utils/api-resource-utils.ts +++ b/apps/console/src/features/api-resources/utils/api-resource-utils.ts @@ -17,8 +17,8 @@ */ import { hasRequiredScopes } from "@wso2is/core/helpers"; -import { APIResourcesConstants } from "../constants/api-resources-constants"; import { FeatureConfigInterface } from "../../core"; +import { APIResourcesConstants } from "../constants/api-resources-constants"; export class APIResourceUtils { @@ -96,4 +96,4 @@ export class APIResourceUtils { || type === APIResourcesConstants.SYSTEM_ORG || type === APIResourcesConstants.SYSTEM_FEATURE; } - } +}