Skip to content

Commit 4ef2d95

Browse files
committed
feat: refactor role and permission constants for better organization and context handling
1 parent 15be337 commit 4ef2d95

10 files changed

Lines changed: 22 additions & 236 deletions

File tree

src/authz-module/constants.ts

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import {
2+
libraryRolesMetadata,
3+
libraryResourceTypes,
4+
libraryPermissions,
5+
} from '@src/authz-module/roles-permissions/libraries/constants';
6+
17
export const ROUTES = {
28
LIBRARIES_TEAM_PATH: '/libraries/:libraryId',
39
LIBRARIES_USER_PATH: '/libraries/:libraryId/:username',
@@ -12,65 +18,6 @@ export enum RoleOperationErrorStatus {
1218
ROLE_REMOVAL_ERROR = 'role_removal_error',
1319
}
1420

15-
// Content Library Permission Keys
16-
export const CONTENT_LIBRARY_PERMISSIONS = {
17-
DELETE_LIBRARY: 'content_libraries.delete_library',
18-
MANAGE_LIBRARY_TAGS: 'content_libraries.manage_library_tags',
19-
VIEW_LIBRARY: 'content_libraries.view_library',
20-
21-
EDIT_LIBRARY_CONTENT: 'content_libraries.edit_library_content',
22-
PUBLISH_LIBRARY_CONTENT: 'content_libraries.publish_library_content',
23-
REUSE_LIBRARY_CONTENT: 'content_libraries.reuse_library_content',
24-
25-
CREATE_LIBRARY_COLLECTION: 'content_libraries.create_library_collection',
26-
EDIT_LIBRARY_COLLECTION: 'content_libraries.edit_library_collection',
27-
DELETE_LIBRARY_COLLECTION: 'content_libraries.delete_library_collection',
28-
29-
MANAGE_LIBRARY_TEAM: 'content_libraries.manage_library_team',
30-
VIEW_LIBRARY_TEAM: 'content_libraries.view_library_team',
31-
};
32-
33-
// Note: this information will eventually come from the backend API
34-
// but for the MVP we decided to manage it in the frontend
35-
export const libraryRolesMetadata = [
36-
{
37-
role: 'library_admin', name: 'Library Admin', description: 'Can create and manage content libraries, including access and structure.', contextType: 'library',
38-
},
39-
{
40-
role: 'library_author', name: 'Library Author', description: 'Can create and edit library content, but cannot manage access.', contextType: 'library',
41-
},
42-
{
43-
role: 'library_contributor', name: 'Library Contributor', description: 'Can contribute and update library content shared with them.', contextType: 'library',
44-
},
45-
{
46-
role: 'library_user', name: 'Library User', description: 'Can view and use library content, but cannot edit it.', contextType: 'library',
47-
},
48-
];
49-
50-
export const libraryResourceTypes = [
51-
{ key: 'library', label: 'Library', description: 'Permissions related to the library as a whole.' },
52-
{ key: 'library_content', label: 'Content', description: 'Permissions to create, edit, delete, and publish individual content items within the library.' },
53-
{ key: 'library_collection', label: 'Collection', description: 'Permissions to create, edit, and delete content collections within the library.' },
54-
{ key: 'library_team', label: 'Team', description: 'Permissions to manage user access and roles within the library.' },
55-
];
56-
57-
export const libraryPermissions = [
58-
{ key: CONTENT_LIBRARY_PERMISSIONS.DELETE_LIBRARY, resource: 'library', description: 'Allows the user to delete the library and all its contents.' },
59-
{ key: CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TAGS, resource: 'library', description: 'Add or remove tags from content.' },
60-
{ key: CONTENT_LIBRARY_PERMISSIONS.VIEW_LIBRARY, resource: 'library', description: 'View content, search, filter, and sort within the library.' },
61-
62-
{ key: CONTENT_LIBRARY_PERMISSIONS.EDIT_LIBRARY_CONTENT, resource: 'library_content', description: 'Edit content in draft mode' },
63-
{ key: CONTENT_LIBRARY_PERMISSIONS.PUBLISH_LIBRARY_CONTENT, resource: 'library_content', description: 'Publish content, making it available for reuse' },
64-
{ key: CONTENT_LIBRARY_PERMISSIONS.REUSE_LIBRARY_CONTENT, resource: 'library_content', description: 'Reuse published content within a course.' },
65-
66-
{ key: CONTENT_LIBRARY_PERMISSIONS.CREATE_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Create new collections within a library.' },
67-
{ key: CONTENT_LIBRARY_PERMISSIONS.EDIT_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Add or remove content from existing collections.' },
68-
{ key: CONTENT_LIBRARY_PERMISSIONS.DELETE_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Delete entire collections from the library.' },
69-
70-
{ key: CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM, resource: 'library_team', description: 'Add, remove, and assign roles to users within the library.' },
71-
{ key: CONTENT_LIBRARY_PERMISSIONS.VIEW_LIBRARY_TEAM, resource: 'library_team', description: 'View the list of users who have access to the library.' },
72-
];
73-
7421
// Course Permission Keys
7522
export const COURSE_PERMISSIONS = {
7623
// View permissions (Course Auditor)

src/authz-module/libraries-manager/LibrariesTeamManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import AuthZLayout from '../components/AuthZLayout';
1212
import RoleCard from '../components/RoleCard';
1313
import PermissionTable from '../components/PermissionTable';
1414
import { useLibraryAuthZ } from './context';
15-
import { buildPermissionMatrixByResource, buildPermissionMatrixByRole } from './utils';
15+
import { buildPermissionMatrixByResource, buildPermissionMatrixByRole } from '@src/authz-module/roles-permissions/libraries/utils';
1616

1717
import messages from './messages';
1818

src/authz-module/libraries-manager/components/TeamTable/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { renderWrapper } from '@src/setupTest';
44
import { useTeamMembers } from '@src/authz-module/data/hooks';
55
import { useLibraryAuthZ } from '@src/authz-module/libraries-manager/context';
66
import { ToastManagerProvider } from '@src/authz-module/libraries-manager/ToastManagerContext';
7-
import { CONTENT_LIBRARY_PERMISSIONS } from '@src/authz-module/constants';
7+
import { CONTENT_LIBRARY_PERMISSIONS } from '@src/authz-module/roles-permissions/libraries/constants';
88
import TeamTable from './index';
99

1010
const mockNavigate = jest.fn();

src/authz-module/libraries-manager/constants.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
import {
2-
CONTENT_LIBRARY_PERMISSIONS,
3-
libraryRolesMetadata,
4-
libraryResourceTypes,
5-
libraryPermissions,
6-
} from '../constants';
7-
81
export {
92
CONTENT_LIBRARY_PERMISSIONS,
103
libraryRolesMetadata,
114
libraryResourceTypes,
125
libraryPermissions,
13-
};
6+
} from '@src/authz-module/roles-permissions/libraries/constants';
147

158
export const DEFAULT_TOAST_DELAY = 5000;
169
export const RETRY_TOAST_DELAY = 120_000; // 2 minutes

src/authz-module/libraries-manager/context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { PermissionMetadata, ResourceMetadata, Role } from 'types';
99
import { CustomErrors } from '@src/constants';
1010
import {
1111
CONTENT_LIBRARY_PERMISSIONS, libraryPermissions, libraryResourceTypes, libraryRolesMetadata,
12-
} from './constants';
12+
} from '@src/authz-module/roles-permissions/libraries/constants';
1313

1414
const LIBRARY_TEAM_PERMISSIONS = [
1515
CONTENT_LIBRARY_PERMISSIONS.VIEW_LIBRARY_TEAM,
Lines changed: 1 addition & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1 @@
1-
import { IntlShape } from '@edx/frontend-platform/i18n';
2-
import { actionKeys } from '@src/authz-module/components/RoleCard/constants';
3-
import {
4-
EnrichedPermission, PermissionMetadata, PermissionsResourceGrouped,
5-
PermissionsRoleGrouped, ResourceMetadata, Role, RoleResourceGroup,
6-
} from '@src/types';
7-
import actionMessages from '../components/RoleCard/messages';
8-
9-
/**
10-
* Derives the localized label and action key for a given permission.
11-
*
12-
* This function enhance the permissions metadata mapping the key to a list of prefefined actions
13-
* to add visual elemments (icons) and a localized label.
14-
* If a label is already defined in the permission metadata, that is returned as-is.
15-
*
16-
* Special handling is applied for action keys like `'tag'` and `'team'`, which are
17-
* normalized to `'manage'` and given a custom resource string for translation.
18-
*
19-
* @param permission - The permission metadata, typically containing a key and optional label.
20-
* @param intl - The `IntlShape` object used to generate localized labels.
21-
*
22-
* @returns An object containing:
23-
* - `label`: The human-readable, localized label for the permission.
24-
* - `actionKey`: A string representing icon to be displayed (e.g., `'Read'`, `'Edit'`), or '' if not matched.
25-
*/
26-
const getPermissionMetadata = (permission: PermissionMetadata, intl: IntlShape): EnrichedPermission => {
27-
const actionKey = actionKeys.find(action => permission.key.includes(action)) || '';
28-
let messageKey = `authz.permissions.actions.${actionKey}`;
29-
let messageResource = '';
30-
31-
if (actionKey === 'tag' || actionKey === 'team') {
32-
messageKey = 'authz.permissions.actions.manage';
33-
messageResource = actionKey === 'tag' ? 'Tags' : '';
34-
}
35-
36-
const messageDescriptor = actionMessages[messageKey];
37-
const label = permission.label || (messageDescriptor
38-
? intl.formatMessage(messageDescriptor, { resource: messageResource })
39-
: permission.key);
40-
41-
return { ...permission, label, actionKey };
42-
};
43-
44-
type BuildPermissionsMatrixProps = {
45-
roles: Role[];
46-
permissions: PermissionMetadata[];
47-
resources: ResourceMetadata[];
48-
intl: IntlShape;
49-
};
50-
51-
/**
52-
* Builds a permission matrix from the given roles, permissions, and resources.
53-
*
54-
* The matrix groups permissions under their respective resources and maps
55-
* each permission to which roles have access to it.
56-
*
57-
* @param roles - List of roles, each containing a list of granted permission keys.
58-
* @param permissions - Metadata describing each permission, including its associated resource.
59-
* @param resources - List of resource metadata used to group permissions.
60-
* @param intl - The internationalization object used to localize permission labels.
61-
*
62-
* @returns A permission matrix grouped by resource, with role mappings per permission.
63-
*/
64-
const buildPermissionMatrixByResource = ({
65-
roles, permissions, resources, intl,
66-
}: BuildPermissionsMatrixProps): PermissionsResourceGrouped[] => {
67-
const enrichedPermissions = permissions.reduce((acc, perm) => {
68-
acc[perm.key] = getPermissionMetadata(perm, intl);
69-
return acc;
70-
}, {} as Record<string, EnrichedPermission>);
71-
72-
const permissionsByResource = permissions.reduce<Record<string, PermissionMetadata[]>>((acc, perm) => {
73-
if (!acc[perm.resource]) { acc[perm.resource] = []; }
74-
acc[perm.resource].push(perm);
75-
return acc;
76-
}, {});
77-
78-
return resources.map(resource => {
79-
const perms = permissionsByResource[resource.key] || [];
80-
81-
const permissionRows = perms.map(permission => {
82-
const enriched = enrichedPermissions[permission.key];
83-
const rolesMap = roles.reduce((acc, role) => {
84-
acc[role.name] = role.permissions.includes(permission.key);
85-
return acc;
86-
}, {} as Record<string, boolean>);
87-
88-
return {
89-
...enriched,
90-
roles: rolesMap,
91-
};
92-
});
93-
94-
return {
95-
...resource,
96-
permissions: permissionRows,
97-
};
98-
});
99-
};
100-
101-
/**
102-
* Builds a permission matrix for grouped by roles.
103-
*
104-
* Builds a permission matrix grouped by resource, mapping each action to its display label
105-
* and enabled/disabled state based on the role's allowed permissions.
106-
*
107-
* @param roles - Array of roles metadata.
108-
* @param permissions - Permissions metadata.
109-
* @param resources - Resources metadata.
110-
* @param intl - the i18n function to enable label translations.
111-
* @returns An array of permission groupings by role and resource with action-level details.
112-
*/
113-
const buildPermissionMatrixByRole = ({
114-
roles, permissions, resources, intl,
115-
}: BuildPermissionsMatrixProps): PermissionsRoleGrouped[] => {
116-
const enrichedPermissions = permissions.reduce((acc, perm) => {
117-
acc[perm.key] = getPermissionMetadata(perm, intl);
118-
return acc;
119-
}, {} as Record<string, EnrichedPermission>);
120-
121-
return roles.map(role => {
122-
const allowed = new Set(role.permissions);
123-
const permissionsGroupedByResource: Record<string, RoleResourceGroup> = {};
124-
125-
permissions.forEach(permission => {
126-
const enriched = enrichedPermissions[permission.key];
127-
const { resource } = permission;
128-
129-
if (!enriched.actionKey) { return; }
130-
131-
if (!permissionsGroupedByResource[resource]) {
132-
const resourceInfo = resources.find(r => r.key === resource);
133-
if (!resourceInfo) { return; }
134-
135-
permissionsGroupedByResource[resource] = {
136-
key: resourceInfo.key,
137-
label: resourceInfo.label,
138-
description: resourceInfo.description,
139-
permissions: [],
140-
};
141-
}
142-
143-
permissionsGroupedByResource[resource].permissions.push({
144-
...enriched,
145-
description: permission.description,
146-
disabled: !allowed.has(permission.key),
147-
});
148-
});
149-
150-
return {
151-
...role,
152-
resources: Object.values(permissionsGroupedByResource),
153-
};
154-
});
155-
};
156-
157-
export { buildPermissionMatrixByResource, buildPermissionMatrixByRole };
1+
export { buildPermissionMatrixByResource, buildPermissionMatrixByRole } from '@src/authz-module/roles-permissions/libraries/utils';

src/authz-module/roles-permissions/libraries/constants.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ export const CONTENT_LIBRARY_PERMISSIONS = {
2626
// Note: this information will eventually come from the backend API
2727
// but for the MVP we decided to manage it in the frontend
2828
export const libraryRolesMetadata: RoleMetadata[] = [
29-
{ role: 'library_admin', name: 'Library Admin', description: 'The Library Admin has full control over the library, including managing users, modifying content, and handling publishing workflows. They ensure content is properly maintained and accessible as needed.' },
30-
{ role: 'library_author', name: 'Library Author', description: 'The Library Author is responsible for creating, editing, and publishing content within a library. They can manage tags and collections but cannot delete libraries or manage users.' },
31-
{ role: 'library_contributor', name: 'Library Contributor', description: 'The Library Contributor can create and edit content within a library but cannot publish it. They support the authoring process while leaving final publishing to Authors or Admins.' },
32-
{ role: 'library_user', name: 'Library User', description: 'The Library User can view and reuse content but cannot edit or delete any resource.' },
29+
{ role: 'library_admin', name: 'Library Admin', description: 'The Library Admin has full control over the library, including managing users, modifying content, and handling publishing workflows. They ensure content is properly maintained and accessible as needed.', contextType: 'library' },
30+
{ role: 'library_author', name: 'Library Author', description: 'The Library Author is responsible for creating, editing, and publishing content within a library. They can manage tags and collections but cannot delete libraries or manage users.', contextType: 'library' },
31+
{ role: 'library_contributor', name: 'Library Contributor', description: 'The Library Contributor can create and edit content within a library but cannot publish it. They support the authoring process while leaving final publishing to Authors or Admins.', contextType: 'library' },
32+
{ role: 'library_user', name: 'Library User', description: 'The Library User can view and reuse content but cannot edit or delete any resource.', contextType: 'library' },
3333
];
3434

3535
export const libraryResourceTypes: ResourceMetadata[] = [

src/authz-module/roles-permissions/libraries/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ const getPermissionMetadata = (permission: PermissionMetadata, intl: IntlShape):
2727
const actionKey = actionKeys.find(action => permission.key.includes(action)) || '';
2828
let messageKey = `authz.permissions.actions.${actionKey}`;
2929

30-
if (actionKey === 'team') {
30+
let messageResource = '';
31+
if (actionKey === 'tag' || actionKey === 'team') {
3132
messageKey = 'authz.permissions.actions.manage';
33+
messageResource = actionKey === 'tag' ? 'Tags' : '';
3234
}
3335

3436
const messageDescriptor = actionMessages[messageKey];
3537
const label = permission.label || (messageDescriptor
36-
? intl.formatMessage(messageDescriptor, { resource: '' })
38+
? intl.formatMessage(messageDescriptor, { resource: messageResource })
3739
: permission.key);
3840

3941
return { ...permission, label, actionKey };

src/authz-module/wizard/AssignRoleWizard.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ import { SpinnerSimple } from '@openedx/paragon/icons';
99
import SelectUsersAndRoleStep from './SelectUsersAndRoleStep';
1010
import DefineApplicationScopeStep from './DefineApplicationScopeStep';
1111
import { useValidateUsers } from '../data/hooks';
12-
import {
13-
CONTENT_LIBRARY_PERMISSIONS, COURSE_PERMISSIONS, courseRolesMetadata, libraryRolesMetadata,
14-
} from '../constants';
12+
import { CONTENT_LIBRARY_PERMISSIONS, libraryRolesMetadata } from '../roles-permissions/libraries/constants';
13+
import { COURSE_PERMISSIONS, courseRolesMetadata } from '../constants';
1514
import { useValidateUserPermissions } from '../../data/hooks';
1615
import messages from './messages';
1716

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface RoleMetadata {
2828
role: string;
2929
name: string;
3030
description: string;
31+
contextType?: string;
3132
}
3233
export interface Role extends RoleMetadata {
3334
userCount: number;

0 commit comments

Comments
 (0)