Skip to content

Commit

Permalink
update hasRequiredScopes function
Browse files Browse the repository at this point in the history
  • Loading branch information
Achintha Isuru committed Oct 26, 2023
1 parent 72b6c71 commit 49bb0c9
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 77 deletions.
34 changes: 34 additions & 0 deletions apps/console/src/features/authorization/hooks/use-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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 { AuthorizationInterface } from "../models/authorization";
import getLegacyAuthzRuntime from "../utils/get-legacy-authz-runtime";

/**
* Authorization hook.
*
* @returns `AuthorizationInterface` Authorization details.
*/
const useAuthorization = (): AuthorizationInterface => {

const legacyAuthzRuntime: boolean = getLegacyAuthzRuntime();

return { legacyAuthzRuntime: legacyAuthzRuntime };
};

export default useAuthorization;
24 changes: 24 additions & 0 deletions apps/console/src/features/authorization/models/authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* 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.
*/

/**
* Interface to store authorization details.
*/
export interface AuthorizationInterface {
legacyAuthzRuntime: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 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.
*/

/**
* Gets the legacy authz runtime flag from the deployment config.
*
* @returns `boolean` True if legacy authz runtime is enabled and false if not.
*/
const getLegacyAuthzRuntime = (): boolean => {
return window["AppUtils"]?.getConfig()?.legacyAuthzRuntime;
};

export default getLegacyAuthzRuntime;
141 changes: 64 additions & 77 deletions modules/core/src/helpers/access-control.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
* Copyright (c) 2020, 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
Expand All @@ -18,18 +18,19 @@

import isEmpty from "lodash-es/isEmpty";
import { FeatureAccessConfigInterface } from "../models";
import { OrganizationType } from "../models/organization";
import { AuthenticateUtils } from "../utils";

/**
* Checks if the feature is enabled.
*
* @param {FeatureAccessConfigInterface} feature - Evaluating feature.
* @param {string | string[]} key - Feature key/keys to check.
* @param feature -`FeatureAccessConfigInterface` Evaluating feature.
* @param key -`string | string[]` Feature key/keys to check.
*
* @return {boolean} True is feature is enabled and false if not.
* @returns `boolean` True is feature is enabled and false if not.
*/
export const isFeatureEnabled = (feature: FeatureAccessConfigInterface, key: string | string[]): boolean => {
const isDefined = feature?.disabledFeatures
const isDefined: boolean = feature?.disabledFeatures
&& Array.isArray(feature.disabledFeatures)
&& feature.disabledFeatures.length > 0;

Expand All @@ -42,7 +43,7 @@ export const isFeatureEnabled = (feature: FeatureAccessConfigInterface, key: str
}

if (key instanceof Array) {
return !key.some((item) => feature.disabledFeatures.includes(item));
return !key.some((item: string) => feature.disabledFeatures.includes(item));
}

return true;
Expand All @@ -51,23 +52,55 @@ export const isFeatureEnabled = (feature: FeatureAccessConfigInterface, key: str
/**
* Checks if the required scopes are available to perform the desired CRUD operation.
*
* @param {FeatureAccessConfigInterface} feature - Evaluating feature.
* @param {string[]} scopes - Set of scopes to check.
* @param {string} allowedScopes - Set of allowed scopes.
* @param feature - `FeatureAccessConfigInterface` Evaluating feature.
* @param scopes - `string[]` Set of scopes to check.
* @param allowedScopes - `string` Set of allowed scopes.
* @param organzationType - `string` Organization type. This should be equals to the `OrganizationType` enum in
* `modules/common/src/constants/organization-constants.ts`.
* @param isLegacyRuntimeDisabled - `boolean` Is legacy runtime disabled. This is used to ensure backward compatibility.
*
* @return {boolean} True is scopes are enough and false if not.
* @returns `boolean` True is scopes are enough and false if not.
*/
export const hasRequiredScopes = (
feature: FeatureAccessConfigInterface, scopes: string[], allowedScopes: string
feature: FeatureAccessConfigInterface,
scopes: string[],
allowedScopes: string,
organzationType?: string,
isLegacyRuntimeEnabled?: boolean
): boolean => {
const isDefined = feature?.scopes && !isEmpty(feature.scopes) && scopes && !isEmpty(scopes);
const isDefined: boolean = feature?.scopes && !isEmpty(feature.scopes) && scopes && !isEmpty(scopes);

if (!isDefined) {
return true;
}

if (scopes instanceof Array) {
return scopes.every((scope) => AuthenticateUtils.hasScope(scope, allowedScopes));
if (isLegacyRuntimeEnabled ||
!organzationType ||
organzationType === OrganizationType.SUPER_ORGANIZATION ||
organzationType === OrganizationType.FIRST_LEVEL_ORGANIZATION ||
organzationType === OrganizationType.TENANT) {

return scopes.every((scope: string) => AuthenticateUtils.hasScope(scope, allowedScopes));

} else {
/**
* If the organization type is `SUBORGANIZATION`, the `internal_` scopes should be replaced with
* `internal_org_` scopes.
*/
const interal: string = "internal_";
const internalOrg: string = "internal_org_";
const internalLogin: string = "internal_login";

return scopes.every((scope: string) => {
// If the scope begins with `internal_`, replace it with `internal_org_`.
if (scope.startsWith(interal) && scope !== internalLogin) {
scope = scope.replace(interal, internalOrg);
}

return AuthenticateUtils.hasScope(scope, allowedScopes);
});
}
}

return true;
Expand All @@ -79,23 +112,35 @@ export const hasRequiredScopes = (
* @remarks
* Currently the check passes if at least one feature has the required read permissions.
*
* @param {FeatureAccessConfigInterface} featureConfig - Feature configuration.
* @param featureConfig - `FeatureAccessConfigInterface` Feature configuration.
* @param organzationType - `string` Organization type. This should be equals to the `OrganizationType` enum in
* `modules/common/src/constants/organization-constants.ts`.
* @param isLegacyRuntimeDisabled - `boolean` Is legacy runtime disabled. This is used to ensure backward compatibility.
*
* @return {boolean} True is access is granted, false if not.
* @returns `boolean` True is access is granted, false if not.
*/
export const isPortalAccessGranted = <T = {}>(featureConfig: T, allowedScopes: string): boolean => {
const isDefined = featureConfig && !isEmpty(featureConfig);
export const isPortalAccessGranted = <T = unknown>(
featureConfig: T,
allowedScopes: string,
organzationType?: string,
isLegacyRuntimeDisabled?: boolean
): boolean => {
const isDefined: boolean = featureConfig && !isEmpty(featureConfig);

if (!isDefined) {
return true;
}

let isAllowed = false;
let isAllowed: boolean = false;

for (const value of Object.values(featureConfig)) {
const feature: FeatureAccessConfigInterface = value;

if (hasRequiredScopes(feature, feature?.scopes?.read, allowedScopes)) {
if (hasRequiredScopes(feature,
feature?.scopes?.read,
allowedScopes,
organzationType,
isLegacyRuntimeDisabled)) {
isAllowed = true;

break;
Expand All @@ -105,61 +150,3 @@ export const isPortalAccessGranted = <T = {}>(featureConfig: T, allowedScopes: s
return isAllowed;
};

/**
* This is a temporary util method added to conditionally render the manage tab depending on the
* scopes of the user.
*
* Git issue to track - https://github.com/wso2/product-is/issues/11319
*
* @param featureConfig
* @param allowedScopes
*/
export const hasRequiredScopesForAdminView = (featureConfig: any, allowedScopes: string): boolean => {

let feature = null;
let isAllowed = null;

for (const [ key, value ] of Object.entries(featureConfig)) {
feature = value;

if (value) {

if (key === "attributeDialects" || key === "userStores" || key === "roles") {

const hasReadAccess = hasRequiredManageScopes(feature, feature.scopes?.read, allowedScopes);
const hasUpdateAccess = hasRequiredManageScopes(feature, feature.scopes?.update, allowedScopes);

if (!(hasReadAccess && hasUpdateAccess)) {
isAllowed = false;
}
}

isAllowed = hasRequiredManageScopes(feature, feature.scopes?.read, allowedScopes);
}
}

return isAllowed;
};

/**
* This is a temporary util method added to specially handle the scopes of
* "Application Developer" role.
*
* Git issue to track - https://github.com/wso2/product-is/issues/11319
*
* @param feature
* @param scopes
* @param allowedScopes
*/
export const hasRequiredManageScopes = (
feature: FeatureAccessConfigInterface, scopes: string[], allowedScopes: string
): boolean => {

let hasScope = true;

if (scopes instanceof Array) {
hasScope = scopes.every((scope) => AuthenticateUtils.hasScope(scope, allowedScopes));
}

return hasScope;
};
28 changes: 28 additions & 0 deletions modules/core/src/models/organization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 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.
*/

/**
* Contains all the possible types of organizations. This should be equals to the `OrganizationType` enum in
* `modules/common/src/constants/organization-constants.ts`.
*/
export enum OrganizationType {
SUBORGANIZATION = "SUBORGANIZATION",
TENANT = "TENANT",
FIRST_LEVEL_ORGANIZATION = "FIRST_LEVEL_ORGANIZATION",
SUPER_ORGANIZATION= "SUPER_ORGANIZATION"
}

0 comments on commit 49bb0c9

Please sign in to comment.