Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/authz-module/audit-user/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@openedx/paragon';
import TableFooter from '@src/authz-module/components/TableFooter/TableFooter';
import {
AUTHZ_HOME_PATH, TABLE_DEFAULT_PAGE_SIZE,
ROUTES, TABLE_DEFAULT_PAGE_SIZE,
} from '@src/authz-module/constants';
import AuthZLayout from '@src/authz-module/components/AuthZLayout';
import { useNavigate, useParams } from 'react-router-dom';
Expand Down Expand Up @@ -94,7 +94,7 @@ const AuditUserPage = () => {
useEffect(() => {
if (!user && !isLoadingUser) {
if (!isErrorUser || getHttpErrorStatus(errorUser) === 404) {
navigate(AUTHZ_HOME_PATH);
navigate(ROUTES.HOME_PATH);
}
}
}, [user, isLoadingUser, navigate, isErrorUser, errorUser]);
Expand All @@ -109,7 +109,7 @@ const AuditUserPage = () => {
const navLinks = useMemo(() => [
{
label: formatMessage(baseMessages['authz.management.home.nav.link']),
to: AUTHZ_HOME_PATH,
to: ROUTES.HOME_PATH,
},
], [formatMessage]);

Expand Down Expand Up @@ -216,7 +216,7 @@ const AuditUserPage = () => {
});
handleCloseConfirmDeletionModal();
if (remainingRolesCount === 0) {
navigate(AUTHZ_HOME_PATH);
navigate(ROUTES.HOME_PATH);
}
},
onError: (error, retryVariables) => {
Expand Down
6 changes: 3 additions & 3 deletions src/authz-module/components/PermissionTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const mockRoles: Role[] = [
userCount: 0,
permissions: [],
role: '',
contextType: '',
contextType: 'course',
scope: '',
},
{
Expand All @@ -19,7 +19,7 @@ const mockRoles: Role[] = [
userCount: 0,
permissions: [],
role: '',
contextType: '',
contextType: 'course',
scope: '',
},
{
Expand All @@ -28,7 +28,7 @@ const mockRoles: Role[] = [
userCount: 0,
permissions: [],
role: '',
contextType: '',
contextType: 'course',
scope: '',
},
];
Expand Down
14 changes: 7 additions & 7 deletions src/authz-module/components/RenderAdminRole.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import RenderAdminRole from './RenderAdminRole';
describe('RenderAdminRole', () => {
const adminRole = 'course_admin';
const superuserRole = 'django.superuser';
const staffRole = 'django.globalstaff';
const staffRole = 'django.staff';
const instructorRole = 'instructor';
const emptyRole = '';
const mixedCaseAdminRole = 'Library_Admin';
Expand All @@ -27,13 +27,13 @@ describe('RenderAdminRole', () => {
expect(container.querySelector('.mb-0')).toBeInTheDocument();
});

it('displays admin message for roles containing admin', () => {
renderWrapper(<RenderAdminRole role={adminRole} />);
it('displays admin message for superuser role', () => {
renderWrapper(<RenderAdminRole role={superuserRole} />);
expect(screen.getByText(/super admins have full access/i)).toBeInTheDocument();
});

it('displays staff message for superuser role', () => {
renderWrapper(<RenderAdminRole role={superuserRole} />);
it('displays staff message for non-django admin roles', () => {
renderWrapper(<RenderAdminRole role={adminRole} />);
expect(screen.getByText(/global staff have access/i)).toBeInTheDocument();
});

Expand All @@ -57,9 +57,9 @@ describe('RenderAdminRole', () => {
expect(screen.getByText(/global staff have access/i)).toBeInTheDocument();
});

it('displays admin message for mixed case admin role', () => {
it('displays staff message for mixed case admin role', () => {
renderWrapper(<RenderAdminRole role={mixedCaseAdminRole} />);
expect(screen.getByText(/super admins have full access/i)).toBeInTheDocument();
expect(screen.getByText(/global staff have access/i)).toBeInTheDocument();
});

it('displays staff message for regular role without admin', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/authz-module/components/RenderAdminRole.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { SUPERUSER_ROLE } from '@src/authz-module/constants';
import messages from '@src/authz-module/audit-user/messages';

interface RenderAdminRoleProps {
Expand All @@ -7,8 +8,7 @@ interface RenderAdminRoleProps {

const RenderAdminRole = ({ role }: RenderAdminRoleProps) => {
const intl = useIntl();
// Determine which message to show based on role
const messageKey = role?.toLowerCase().includes('admin')

@dcoa dcoa Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is a small fix for the release too
Current message (Verawood sandbox)

Image

After the change

Image

@MaferMazu

const messageKey = role === SUPERUSER_ROLE
? 'authz.user.table.permissions.role.admin'
: 'authz.user.table.permissions.role.staff';

Expand Down
16 changes: 8 additions & 8 deletions src/authz-module/components/TableCells.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ describe('TableCells Components', () => {
const viewButton = screen.getByRole('button', { name: /view/i });
await user.click(viewButton);

expect(mockNavigate).toHaveBeenCalledWith('/authz/user/user+with@special.chars');
expect(mockNavigate).toHaveBeenCalledWith(`/authz/user/${encodeURIComponent('user+with@special.chars')}`);
});

it('disables the view action and shows a tooltip when course authoring is disabled for the course', async () => {
Expand Down Expand Up @@ -364,7 +364,7 @@ describe('TableCells Components', () => {
row: {
id: '0',
original: {
role: 'django.globalstaff', org: 'Test Org', scope: 'Test Scope', permissionCount: 1,
role: 'django.staff', org: 'Test Org', scope: 'Test Scope', permissionCount: 1,
},
},
column: { id: 'org' },
Expand Down Expand Up @@ -420,7 +420,7 @@ describe('TableCells Components', () => {
row: {
id: '0',
original: {
role: 'django.globalstaff', org: 'Test Org', scope: 'Test Scope', permissionCount: 1,
role: 'django.staff', org: 'Test Org', scope: 'Test Scope', permissionCount: 1,
},
},
column: { id: 'scope' },
Expand Down Expand Up @@ -476,7 +476,7 @@ describe('TableCells Components', () => {
row: {
id: '0',
original: {
role: 'django.globalstaff',
role: 'django.staff',
permissionCount: 5,
org: 'Test Org',
scope: 'Test Scope',
Expand Down Expand Up @@ -516,7 +516,7 @@ describe('TableCells Components', () => {
original: {
role: 'library_admin',
org: 'Test Org',
scope: 'Test Scope',
scope: 'lib:Org:TestScope',
permissionCount: 1,
canManageScope: true,
},
Expand All @@ -538,7 +538,7 @@ describe('TableCells Components', () => {
expect(deleteButton).toBeInTheDocument();

await user.click(deleteButton);
expect(mockOnClickDeleteButton).toHaveBeenCalledWith({ name: 'Library Admin', role: 'library_admin', scope: 'Test Scope' });
expect(mockOnClickDeleteButton).toHaveBeenCalledWith({ name: 'Library Admin', role: 'library_admin', scope: 'lib:Org:TestScope' });
});

it('renders a disabled delete icon for admin roles when isUserAuthenticatedPage is true', () => {
Expand Down Expand Up @@ -586,7 +586,7 @@ describe('TableCells Components', () => {
original: {
role: 'django.superuser',
org: 'Test Org',
scope: 'Test Scope',
scope: 'lib:Org:TestScope',
permissionCount: 1,
},
};
Expand All @@ -610,7 +610,7 @@ describe('TableCells Components', () => {
});
const customRow = {
original: {
...baseRow,
...baseRow.original,
canManageScope: false,
},
};
Expand Down
19 changes: 10 additions & 9 deletions src/authz-module/components/TableCells.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { UserRoleWithPermissions, RoleToDelete } from '@src/types';
import { useNavigate } from 'react-router-dom';
import { useContext, useMemo, type ComponentProps } from 'react';
import {
ADMIN_ROLES, DJANGO_MANAGED_ROLES, MAP_ROLE_KEY_TO_LABEL,
ADMIN_ROLES, buildUserPath, DJANGO_MANAGED_ROLES, getScopeContextType,
MAP_ROLE_KEY_TO_LABEL, SUPERUSER_ROLE,
} from '@src/authz-module/constants';
import {
Icon, IconButton, OverlayTrigger, Tooltip, DataTableContext,
Expand Down Expand Up @@ -97,8 +98,8 @@ const NameCell = ({ row }: CellProps) => {
const ViewActionCell = ({ row, isCourseEnabled }: CellProps & Partial<ViewActionCellExtraProps>) => {
const { formatMessage } = useIntl();
const navigate = useNavigate();
const viewPath = `/authz/user/${row.original.username}`;
const isCourseScope = !row.original.role?.startsWith('lib') && !DJANGO_MANAGED_ROLES.includes(row.original.role);
const viewPath = buildUserPath(row.original.username ?? '');
const isCourseScope = getScopeContextType(row.original.scope) === 'course' && !DJANGO_MANAGED_ROLES.includes(row.original.role);
const isDisabled = isCourseEnabled !== undefined && isCourseScope && !isCourseEnabled(row.original.scope);

if (isDisabled) {
Expand Down Expand Up @@ -144,7 +145,7 @@ const ScopeCell = ({ row }: CellProps) => {
iconSrc: RESOURCE_ICONS.GLOBAL,
};
}
const scopeIcon = row.original.role?.startsWith('lib') ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE;
const scopeIcon = getScopeContextType(row.original.scope) === 'library' ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE;
return {
scopeText: row.original.scope,
iconSrc: scopeIcon,
Expand Down Expand Up @@ -174,10 +175,10 @@ const PermissionsCell = ({ row }: CellProps) => {
const isDjangoRole = DJANGO_MANAGED_ROLES.includes(role);
return (
<span>
{ isDjangoRole
{isDjangoRole
? formatMessage(
messages['authz.user.table.permissions.access.label'],
{ accessType: role === 'django.superuser' ? 'total' : 'partial' },
{ accessType: role === SUPERUSER_ROLE ? 'total' : 'partial' },
)
: formatMessage(messages['authz.user.table.permissions.available.count'], { count })}
</span>
Expand Down Expand Up @@ -239,7 +240,7 @@ const ActionsCell = ({
<Tooltip variant="light" id="tooltip-left">
{formatMessage(messages['authz.user.table.delete.action.djangorole.tooltip'])}
</Tooltip>
)}
)}
>
<Icon
className="mx-2 pl-1"
Expand All @@ -257,7 +258,7 @@ const ActionsCell = ({
<Tooltip variant="light" id="tooltip-left">
{formatMessage(messages['authz.user.table.delete.action.adminrole.tooltip'])}
</Tooltip>
)}
)}
>
<Icon
className="mx-2 pl-1 text-light-500"
Expand All @@ -267,7 +268,7 @@ const ActionsCell = ({
);
}

const isCourseScope = !role?.startsWith('lib');
const isCourseScope = getScopeContextType(row.original.scope) === 'course';
const isCourseAuthoringDisabled = isCourseEnabled !== undefined
&& isCourseScope && !isCourseEnabled(row.original.scope);

Expand Down
14 changes: 7 additions & 7 deletions src/authz-module/components/TableControlBar/ScopesFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LocationOn } from '@openedx/paragon/icons';
import { useViewTeamPermissions } from '@src/authz-module/hooks/useViewTeamPermissions';
import { useCourseAuthoringFlag } from '@src/authz-module/hooks/useCourseAuthoringFlag';
import { useScopes } from '@src/authz-module/data/hooks';
import { DEFAULT_FILTER_PAGE_SIZE } from '@src/authz-module/constants';
import { DEFAULT_FILTER_PAGE_SIZE, getScopeContextType } from '@src/authz-module/constants';
import { MultipleChoiceFilterProps } from './types';
import MultipleChoiceFilter from './MultipleChoiceFilter';
import { RESOURCE_ICONS } from '../constants';
Expand All @@ -29,13 +29,13 @@ const ScopesFilter = ({

const filterChoices = useMemo(() => (scopesData?.pages?.flatMap((p) => p.results) ?? [])
// Libraries are always available; courses only when the authoring flag is enabled for them.
.filter((scope) => scope.externalKey?.startsWith('lib') || isCourseEnabled(scope.externalKey))
.filter((scope) => getScopeContextType(scope.externalKey ?? '') === 'library' || isCourseEnabled(scope.externalKey))
.map((scope) => {
const scopeIcon = scope.externalKey?.startsWith('lib') ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE;
let groupName = formatMessage(messages['authz.team.members.table.group.courses']);
if (scope.externalKey?.startsWith('lib')) {
groupName = formatMessage(messages['authz.team.members.table.group.libraries']);
}
const isLibraryScope = getScopeContextType(scope.externalKey ?? '') === 'library';
const scopeIcon = isLibraryScope ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE;
const groupName = isLibraryScope
? formatMessage(messages['authz.team.members.table.group.libraries'])
: formatMessage(messages['authz.team.members.table.group.courses']);
return {
displayName: scope.displayName,
value: scope.externalKey,
Expand Down
3 changes: 2 additions & 1 deletion src/authz-module/components/UserPermissions.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { DJANGO_MANAGED_ROLES } from '@src/authz-module/constants';
import {
LIBRARY_ROLE_KEYS,
courseResourceTypes,
coursePermissions,
courseRolesWithPermissions,
Expand Down Expand Up @@ -36,7 +37,7 @@ const UserPermissions = ({ row }: UserPermissionsProps) => {

// Normalize role string to match keys in constants (e.g. "Course Admin" -> "course_admin")
roleKey = roleKey.trim().toLowerCase().replace(/[-\s]+/g, '_');
const isLibraryRole = roleKey.includes('library');
const isLibraryRole = LIBRARY_ROLE_KEYS.includes(roleKey);
const config = isLibraryRole
? {
resourceTypes: libraryResourceTypes,
Expand Down
Loading