Skip to content

Commit

Permalink
Enable role creation and deletion capability in sub organization level
Browse files Browse the repository at this point in the history
  • Loading branch information
ShanChathusanda93 committed Jan 8, 2025
1 parent 2236b69 commit c01fab3
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 70 deletions.
7 changes: 7 additions & 0 deletions .changeset/three-knives-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wso2is/admin.roles.v2": patch
"@wso2is/core": patch
"@wso2is/i18n": patch
---

Add role create and delete ops to sub orgs
98 changes: 55 additions & 43 deletions features/admin.roles.v2/components/role-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
IdentifiableComponentInterface,
LoadableComponentInterface,
RoleListInterface,
RolePropertyInterface,
RolesInterface
} from "@wso2is/core/models";
import {
Expand Down Expand Up @@ -85,7 +86,6 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (props: RoleList

const {
handleRoleDelete,
isSubOrg,
onEmptyListPlaceholderActionClick,
onSearchQueryClear,
roleList,
Expand Down Expand Up @@ -168,7 +168,7 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (props: RoleList
return (
<EmptyPlaceholder
data-componentid={ `${ componentId }-empty-list-empty-placeholder` }
action={ !isSubOrg && (
action={ (
<Show when={ featureConfig?.userRoles?.scopes?.create }>
<PrimaryButton
data-componentid={ `${ componentId }-empty-list-empty-placeholder-add-button` }
Expand All @@ -182,21 +182,16 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (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" })
]
}
/>
);
Expand All @@ -215,30 +210,44 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (props: RoleList
dataIndex: "name",
id: "name",
key: "name",
render: (role: RolesInterface): ReactNode => (
<Header
image
as="h6"
className="header-with-icon"
data-componentid={ `${ componentId }-item-heading` }
>
<AppAvatar
image={ (
<AnimatedAvatar
name={ role?.displayName[ 0 ] }
size="mini"
data-componentid={ `${ componentId }-item-image-inner` }
/>
) }
size="mini"
spaced="right"
data-componentid={ `${ componentId }-item-image` }
/>
<Header.Content>
{ role?.displayName }
</Header.Content>
</Header>
),
render: (role: RolesInterface): ReactNode => {
const isSharedRole: boolean = role?.properties?.some(
(property: RolePropertyInterface) =>
property?.name === RoleConstants.IS_SHARED_ROLE &&
property?.value === "true");

return (
<Header
image
as="h6"
className="header-with-icon"
data-componentid={ `${ componentId }-item-heading` }
>
<AppAvatar
image={ (
<AnimatedAvatar
name={ role?.displayName[ 0 ] }
size="mini"
data-componentid={ `${ componentId }-item-image-inner` }
/>
) }
size="mini"
spaced="right"
data-componentid={ `${ componentId }-item-image` }
/>
<Header.Content>
{ role?.displayName }
{
isSharedRole && (
<Label size="mini">
{ t("roles:list.labels.shared") }
</Label>
)
}
</Header.Content>
</Header>
);
},
title: t("roles:list.columns.name")
},
{
Expand Down Expand Up @@ -296,8 +305,10 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (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 ||
Expand All @@ -306,7 +317,8 @@ export const RoleList: React.FunctionComponent<RoleListProps> = (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 => {
Expand Down
24 changes: 18 additions & 6 deletions features/admin.roles.v2/components/wizard-updated/role-basics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -111,6 +112,9 @@ export const RoleBasics: FunctionComponent<RoleBasicProps> = (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);
Expand Down Expand Up @@ -367,12 +371,20 @@ export const RoleBasics: FunctionComponent<RoleBasicProps> = (props: RoleBasicPr
? (
<Alert severity="info">
{
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")
)
}
</Alert>
) : (
Expand Down
5 changes: 5 additions & 0 deletions features/admin.roles.v2/constants/role-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,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";
}

/**
Expand Down
37 changes: 19 additions & 18 deletions features/admin.roles.v2/pages/role.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,27 +221,28 @@ const RolesPage: FunctionComponent<RolesPagePropsInterface> = (
return (
<PageLayout
action={
!isSubOrg && !isRolesListLoading && (rolesList?.totalResults > 0)
? (
<Show when={ featureConfig?.userRoles?.scopes?.create }>
<PrimaryButton
data-componentid={ `${componentId}-add-button` }
onClick={ () => handleCreateRole() }
>
<Icon
data-componentid={ `${componentId}-add-button-icon` }
name="add"
/>
{ t("roles:list.buttons.addButton", { type: "Role" }) }
</PrimaryButton>
</Show>
) : null
(
!isRolesListLoading && (rolesList?.totalResults > 0)
? (
<Show when={ featureConfig?.userRoles?.scopes?.create }>
<PrimaryButton
data-componentid={ `${componentId}-add-button` }
onClick={ () => handleCreateRole() }
>
<Icon
data-componentid={ `${componentId}-add-button-icon` }
name="add"
/>
{ t("roles:list.buttons.addButton", { type: "Role" }) }
</PrimaryButton>
</Show>
): null
)
}
title={ t("pages:roles.title") }
pageTitle={ t("pages:roles.title") }
description={ isSubOrg
? t("pages:roles.alternateSubTitle")
: (
description={
(
<>
{ t("pages:roles.subTitle") }
<DocumentationLink
Expand Down
6 changes: 6 additions & 0 deletions modules/core/src/models/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface RolesInterface {
permissions?: string[] | RolePermissionInterface[];
audience?: RoleAudiencesInterface;
associatedApplications?: RoleConnectedApplicationInterface[];
properties?: RolePropertyInterface[];
}

/**
Expand Down Expand Up @@ -101,3 +102,8 @@ export interface RoleConnectedApplicationInterface {
value?: string;
$ref?: string;
}

export interface RolePropertyInterface {
name: string;
value: string;
}
9 changes: 8 additions & 1 deletion modules/i18n/src/models/namespaces/roles-ns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,13 @@ export interface rolesNS {
};
};
notes: {
orgNote: string;
appNote: string;
cannotCreateRole: string;
orgNote: string;
subOrganization: {
appNote: string;
orgNote: string;
}
};
};
rolePermission: {
Expand Down Expand Up @@ -357,6 +361,9 @@ export interface rolesNS {
};
audience: string;
};
labels: {
shared: string;
}
confirmations: {
deleteItem: {
assertionHint: string;
Expand Down
17 changes: 15 additions & 2 deletions modules/i18n/src/translations/en-US/portals/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
*/
import { rolesNS } from "../../../models";

/**
* NOTES: No need to care about the max-len for this file since it's easier to
* translate the strings to other languages easily with editor translation tools.
*/
/* eslint-disable max-len */

export const roles: rolesNS = {
addRoleWizard: {
buttons: {
Expand Down Expand Up @@ -60,9 +66,13 @@ export const roles: rolesNS = {
}
},
notes: {

Check warning on line 68 in modules/i18n/src/translations/en-US/portals/roles.ts

View workflow job for this annotation

GitHub Actions / ⬣ ESLint (STATIC ANALYSIS) (lts/*, 8.7.4)

Expected object keys to be in ascending order. 'notes' should be before 'roleAudience'
orgNote: "When the role audience is organization, you can associate the role with an application which allows organization audience roles.",
appNote: "When the role audience is application, you can associate the role with an application which allows application audience roles.",
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</1> 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</1> 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: {

Check warning on line 77 in modules/i18n/src/translations/en-US/portals/roles.ts

View workflow job for this annotation

GitHub Actions / ⬣ ESLint (STATIC ANALYSIS) (lts/*, 8.7.4)

Expected object keys to be in ascending order. 'assignedApplication' should be before 'notes'
hint: "Assign an application for the role. Note that assigned application for this role cannot be edited after the role is created.",
Expand Down Expand Up @@ -364,6 +374,9 @@ export const roles: rolesNS = {
},
name: "Role"
},
labels: {
shared: "Shared role"
},
confirmations: {
deleteItem: {
assertionHint: "Please confirm your action.",
Expand Down

0 comments on commit c01fab3

Please sign in to comment.