diff --git a/.changeset/three-knives-sell.md b/.changeset/three-knives-sell.md new file mode 100644 index 00000000000..dbe9f3a2277 --- /dev/null +++ b/.changeset/three-knives-sell.md @@ -0,0 +1,7 @@ +--- +"@wso2is/admin.roles.v2": patch +"@wso2is/core": patch +"@wso2is/i18n": patch +--- + +Add role create and delete ops to sub orgs diff --git a/features/admin.roles.v2/components/role-list.tsx b/features/admin.roles.v2/components/role-list.tsx index 5b16a4c399a..874fab323a3 100644 --- a/features/admin.roles.v2/components/role-list.tsx +++ b/features/admin.roles.v2/components/role-list.tsx @@ -29,6 +29,7 @@ import { IdentifiableComponentInterface, LoadableComponentInterface, RoleListInterface, + RolePropertyInterface, RolesInterface } from "@wso2is/core/models"; import { @@ -85,7 +86,6 @@ export const RoleList: React.FunctionComponent = (props: RoleList const { handleRoleDelete, - isSubOrg, onEmptyListPlaceholderActionClick, onSearchQueryClear, roleList, @@ -168,7 +168,7 @@ export const RoleList: React.FunctionComponent = (props: RoleList return ( = (props: RoleList ) } image={ getEmptyPlaceholderIllustrations().newList } imageSize="tiny" - title={ !isSubOrg && t("roles:list.emptyPlaceholders.emptyRoleList.title", + title={ t("roles:list.emptyPlaceholders.emptyRoleList.title", { type: "role" }) } - subtitle={ isSubOrg - ? [ - t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.0", - { type: "roles" }) - ] - : [ - t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.0", - { type: "roles" }), - t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.1", - { type: "role" }), - t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.2", - { type: "role" }) - ] + subtitle={ [ + t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.0", + { type: "roles" }), + t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.1", + { type: "role" }), + t("roles:list.emptyPlaceholders.emptyRoleList.subtitles.2", + { type: "role" }) + ] } /> ); @@ -215,30 +210,44 @@ export const RoleList: React.FunctionComponent = (props: RoleList dataIndex: "name", id: "name", key: "name", - render: (role: RolesInterface): ReactNode => ( -
- - ) } - size="mini" - spaced="right" - data-componentid={ `${ componentId }-item-image` } - /> - - { role?.displayName } - -
- ), + render: (role: RolesInterface): ReactNode => { + const isSharedRole: boolean = role?.properties?.some( + (property: RolePropertyInterface) => + property?.name === RoleConstants.IS_SHARED_ROLE && + property?.value === "true"); + + return ( +
+ + ) } + size="mini" + spaced="right" + data-componentid={ `${ componentId }-item-image` } + /> + + { role?.displayName } + { + isSharedRole && ( + + ) + } + +
+ ); + }, title: t("roles:list.columns.name") }, { @@ -296,8 +305,10 @@ export const RoleList: React.FunctionComponent = (props: RoleList }, { hidden: (role: RolesInterface) => { - return isSubOrg - || role?.meta?.systemRole + const isSharedRole: boolean = role?.properties?.some((property: RolePropertyInterface) => + property?.name === RoleConstants.IS_SHARED_ROLE && property?.value === "true"); + + return role?.meta?.systemRole || ( role?.displayName === CommonRoleConstants.ADMIN_ROLE || role?.displayName === CommonRoleConstants.ADMIN_GROUP || @@ -306,7 +317,8 @@ export const RoleList: React.FunctionComponent = (props: RoleList || !isFeatureEnabled(userRolesFeatureConfig, RoleConstants.FEATURE_DICTIONARY.get("ROLE_DELETE")) || !hasRequiredScopes(userRolesFeatureConfig, - userRolesFeatureConfig?.scopes?.delete, allowedScopes); + userRolesFeatureConfig?.scopes?.delete, allowedScopes) + || isSharedRole; }, icon: (): SemanticICONS => "trash alternate", onClick: (e: SyntheticEvent, role: RolesInterface): void => { diff --git a/features/admin.roles.v2/components/wizard-updated/role-basics.tsx b/features/admin.roles.v2/components/wizard-updated/role-basics.tsx index e4de4087cc3..21c7c666131 100644 --- a/features/admin.roles.v2/components/wizard-updated/role-basics.tsx +++ b/features/admin.roles.v2/components/wizard-updated/role-basics.tsx @@ -23,8 +23,9 @@ import Alert from "@oxygen-ui/react/Alert"; import { useApplicationList } from "@wso2is/admin.applications.v1/api/application"; import { ApplicationManagementConstants } from "@wso2is/admin.applications.v1/constants/application-management"; import { ApplicationListItemInterface } from "@wso2is/admin.applications.v1/models/application"; -import { history, store } from "@wso2is/admin.core.v1"; +import { OrganizationType, history, store } from "@wso2is/admin.core.v1"; import { AppConstants } from "@wso2is/admin.core.v1/constants"; +import { useGetCurrentOrganizationType } from "@wso2is/admin.organizations.v1/hooks/use-get-organization-type"; import { IdentifiableComponentInterface } from "@wso2is/core/models"; import { Field, Form } from "@wso2is/form"; import { Link } from "@wso2is/react-components"; @@ -111,6 +112,9 @@ export const RoleBasics: FunctionComponent = (props: RoleBasicPr isValidating: isRolesListValidating } = useGetRolesList(undefined, undefined, roleNameSearchQuery, "users,groups,permissions,associatedApplications"); + const { organizationType } = useGetCurrentOrganizationType(); + const isSubOrg: boolean = organizationType === OrganizationType.SUBORGANIZATION; + useEffect(() => { if (applicationListFetchRequestError) { setIsDisplayNoAppScopeApplicatioError(true); @@ -367,12 +371,20 @@ export const RoleBasics: FunctionComponent = (props: RoleBasicPr ? ( { - roleAudience === RoleAudienceTypes.ORGANIZATION - ? t("roles:addRoleWizard.forms.roleBasicDetails.notes" + - ".orgNote") - : t("roles:addRoleWizard.forms.roleBasicDetails.notes" + - ".appNote") + !isSubOrg ? ( + roleAudience === RoleAudienceTypes.ORGANIZATION + ? t("roles:addRoleWizard.forms.roleBasicDetails.notes" + + ".orgNote") + : t("roles:addRoleWizard.forms.roleBasicDetails.notes" + + ".appNote") // TODO: need to add a learn more for this. + ) : ( + roleAudience === RoleAudienceTypes.ORGANIZATION + ? t("roles:addRoleWizard.forms.roleBasicDetails.notes.subOrganization" + + ".orgNote") + : t("roles:addRoleWizard.forms.roleBasicDetails.notes.subOrganization" + + ".appNote") + ) } ) : ( diff --git a/features/admin.roles.v2/constants/role-constants.ts b/features/admin.roles.v2/constants/role-constants.ts index 150122c5968..81b7a043615 100644 --- a/features/admin.roles.v2/constants/role-constants.ts +++ b/features/admin.roles.v2/constants/role-constants.ts @@ -91,6 +91,11 @@ export class RoleConstants { * filter query for audience type organization. */ public static readonly ROLE_AUDIENCE_ORGANIZATION_FILTER: string = "audience.type eq organization"; + + /** + * Property key for shared role. + */ + public static readonly IS_SHARED_ROLE: string = "isSharedRole"; } /** diff --git a/features/admin.roles.v2/pages/role.tsx b/features/admin.roles.v2/pages/role.tsx index d9225bf731f..aadf32fffe9 100644 --- a/features/admin.roles.v2/pages/role.tsx +++ b/features/admin.roles.v2/pages/role.tsx @@ -221,27 +221,28 @@ const RolesPage: FunctionComponent = ( return ( 0) - ? ( - - handleCreateRole() } - > - - { t("roles:list.buttons.addButton", { type: "Role" }) } - - - ) : null + ( + !isRolesListLoading && (rolesList?.totalResults > 0) + ? ( + + handleCreateRole() } + > + + { t("roles:list.buttons.addButton", { type: "Role" }) } + + + ): null + ) } title={ t("pages:roles.title") } pageTitle={ t("pages:roles.title") } - description={ isSubOrg - ? t("pages:roles.alternateSubTitle") - : ( + description={ + ( <> { t("pages:roles.subTitle") } create an application that supports application audience roles to proceed." + cannotCreateRole: "You cannot create a role with role audience as application because there are currently no applications that support application audience roles. Please <1>create an application that supports application audience roles to proceed.", + orgNote: "When the role audience is organization, you can associate the role with an application which allows organization audience roles.", + subOrganization: { + appNote: "When the role audience is application, you can associate the role with an application which allows application audience roles. You cannot associate the role with a shared application.", + orgNote: "When the role audience is organization, you can associate the role with an application which allows organization audience roles. You cannot associate the role with a shared application." + } }, assignedApplication: { hint: "Assign an application for the role. Note that assigned application for this role cannot be edited after the role is created.", @@ -364,6 +374,9 @@ export const roles: rolesNS = { }, name: "Role" }, + labels: { + shared: "Shared role" + }, confirmations: { deleteItem: { assertionHint: "Please confirm your action.",