Skip to content
Merged
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
4 changes: 4 additions & 0 deletions src/authz/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ export const COURSE_PERMISSIONS = {

VIEW_PAGES_AND_RESOURCES: 'courses.view_pages_and_resources',
MANAGE_PAGES_AND_RESOURCES: 'courses.manage_pages_and_resources',
VIEW_FILES: 'courses.view_files',
CREATE_FILES: 'courses.create_files',
DELETE_FILES: 'courses.delete_files',
EDIT_FILES: 'courses.edit_files',
};
2 changes: 1 addition & 1 deletion src/authz/hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('useCourseUserPermissions', () => {
expect(result.current.canEdit).toBe(false);
});

it('returns isLoading=true and no permission keys while authz permissions are loading', () => {
it('returns isLoading=true and permissions as false while authz permissions are loading', () => {
mockWaffleFlags({ enableAuthzCourseAuthoring: true });
jest.mocked(useUserPermissions).mockReturnValue({
isLoading: true,
Expand Down
135 changes: 0 additions & 135 deletions src/authz/hooks.test.tsx

This file was deleted.

130 changes: 100 additions & 30 deletions src/authz/permissionHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import {
getPagesAndResourcesPermissions,
getAdvancedSettingsPermissions,
getCourseUpdatesPermissions,
getFilesPermissions,
} from './permissionHelpers';
import { COURSE_PERMISSIONS } from './constants';

const courseId = 'course-v1:org+course+run';

describe('permissionHelpers', () => {
const mockCourseId = 'course-v1:edX+DemoX+Demo_Course';

describe('getCourseUpdatesPermissions', () => {
it('should return correct permission structure for course updates operations', () => {
const result = getCourseUpdatesPermissions(courseId);
Expand All @@ -25,51 +28,118 @@ describe('permissionHelpers', () => {
});
});

it('should use the provided courseId as scope for all permissions', () => {
const customCourseId = 'course-v1:TestOrg+TestCourse+2024';
const result = getCourseUpdatesPermissions(customCourseId);
describe('getFilesPermissions', () => {
it('should return correct permission structure for file operations', () => {
const result = getFilesPermissions(mockCourseId);

Object.values(result).forEach(permission => {
expect(permission.scope).toBe(customCourseId);
expect(result).toEqual({
canViewFiles: {
action: COURSE_PERMISSIONS.VIEW_FILES,
scope: mockCourseId,
},
canCreateFiles: {
action: COURSE_PERMISSIONS.CREATE_FILES,
scope: mockCourseId,
},
canDeleteFiles: {
action: COURSE_PERMISSIONS.DELETE_FILES,
scope: mockCourseId,
},
canEditFiles: {
action: COURSE_PERMISSIONS.EDIT_FILES,
scope: mockCourseId,
},
});
});

it('should use the provided courseId as scope for all permissions', () => {
const customCourseId = 'course-v1:TestOrg+TestCourse+2024';
const result = getFilesPermissions(customCourseId);

Object.values(result).forEach(permission => {
expect(permission.scope).toBe(customCourseId);
});
});
});
});

describe('getGradingPermissions', () => {
it('returns VIEW and EDIT permissions with the correct actions and scope', () => {
const result = getGradingPermissions(courseId);
describe('getGradingPermissions', () => {
it('returns VIEW and EDIT permissions with the correct actions and scope', () => {
const result = getGradingPermissions(courseId);

expect(result.canViewGradingSettings.action).toBe(COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS);
expect(result.canViewGradingSettings.scope).toBe(courseId);
expect(result.canEditGradingSettings.action).toBe(COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS);
expect(result.canEditGradingSettings.scope).toBe(courseId);
expect(result.canViewGradingSettings.action).toBe(COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS);
expect(result.canViewGradingSettings.scope).toBe(courseId);
expect(result.canEditGradingSettings.action).toBe(COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS);
expect(result.canEditGradingSettings.scope).toBe(courseId);
});
});

describe('getPagesAndResourcesPermissions', () => {
it('returns VIEW and MANAGE permissions with the correct actions and scope', () => {
const result = getPagesAndResourcesPermissions(courseId);

expect(result.canViewPagesAndResources.action).toBe(COURSE_PERMISSIONS.VIEW_PAGES_AND_RESOURCES);
expect(result.canViewPagesAndResources.scope).toBe(courseId);
expect(result.canManagePagesAndResources.action).toBe(COURSE_PERMISSIONS.MANAGE_PAGES_AND_RESOURCES);
expect(result.canManagePagesAndResources.scope).toBe(courseId);
});
});

describe('getAdvancedSettingsPermissions', () => {
it('returns MANAGE permission with the correct action and scope', () => {
const result = getAdvancedSettingsPermissions(courseId);

expect(result.canManageAdvancedSettings.action).toBe(COURSE_PERMISSIONS.MANAGE_ADVANCED_SETTINGS);
expect(result.canManageAdvancedSettings.scope).toBe(courseId);
});

it('uses the provided courseId as scope', () => {
const otherId = 'course-v1:another+test+run';
const result = getAdvancedSettingsPermissions(otherId);

expect(result.canManageAdvancedSettings.scope).toBe(otherId);
});
});
});

describe('getPagesAndResourcesPermissions', () => {
it('returns VIEW and MANAGE permissions with the correct actions and scope', () => {
const result = getPagesAndResourcesPermissions(courseId);
it('should use correct COURSE_PERMISSIONS constants for each action', () => {
const result = getFilesPermissions(mockCourseId);

expect(result.canViewPagesAndResources.action).toBe(COURSE_PERMISSIONS.VIEW_PAGES_AND_RESOURCES);
expect(result.canViewPagesAndResources.scope).toBe(courseId);
expect(result.canManagePagesAndResources.action).toBe(COURSE_PERMISSIONS.MANAGE_PAGES_AND_RESOURCES);
expect(result.canManagePagesAndResources.scope).toBe(courseId);
expect(result.canViewFiles.action).toBe(COURSE_PERMISSIONS.VIEW_FILES);
expect(result.canCreateFiles.action).toBe(COURSE_PERMISSIONS.CREATE_FILES);
expect(result.canDeleteFiles.action).toBe(COURSE_PERMISSIONS.DELETE_FILES);
expect(result.canEditFiles.action).toBe(COURSE_PERMISSIONS.EDIT_FILES);
});
});

describe('getAdvancedSettingsPermissions', () => {
it('returns MANAGE permission with the correct action and scope', () => {
const result = getAdvancedSettingsPermissions(courseId);
describe('getGradingPermissions', () => {
it('should return correct permission structure for grading operations', () => {
const result = getGradingPermissions(mockCourseId);

expect(result).toEqual({
canViewGradingSettings: {
action: COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS,
scope: mockCourseId,
},
canEditGradingSettings: {
action: COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS,
scope: mockCourseId,
},
});
});

it('should use the provided courseId as scope for all permissions', () => {
const customCourseId = 'course-v1:TestOrg+TestCourse+2024';
const result = getGradingPermissions(customCourseId);

expect(result.canManageAdvancedSettings.action).toBe(COURSE_PERMISSIONS.MANAGE_ADVANCED_SETTINGS);
expect(result.canManageAdvancedSettings.scope).toBe(courseId);
Object.values(result).forEach(permission => {
expect(permission.scope).toBe(customCourseId);
});
});

it('uses the provided courseId as scope', () => {
const otherId = 'course-v1:another+test+run';
const result = getAdvancedSettingsPermissions(otherId);
it('should use correct COURSE_PERMISSIONS constants for each action', () => {
const result = getGradingPermissions(mockCourseId);

expect(result.canManageAdvancedSettings.scope).toBe(otherId);
expect(result.canViewGradingSettings.action).toBe(COURSE_PERMISSIONS.VIEW_GRADING_SETTINGS);
expect(result.canEditGradingSettings.action).toBe(COURSE_PERMISSIONS.EDIT_GRADING_SETTINGS);
});
});
});
18 changes: 18 additions & 0 deletions src/authz/permissionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,21 @@ export const getAdvancedSettingsPermissions = (courseId: string) => ({
scope: courseId,
},
});
export const getFilesPermissions = (courseId: string) => ({
canViewFiles: {
action: COURSE_PERMISSIONS.VIEW_FILES,
scope: courseId,
},
canCreateFiles: {
action: COURSE_PERMISSIONS.CREATE_FILES,
scope: courseId,
},
canDeleteFiles: {
action: COURSE_PERMISSIONS.DELETE_FILES,
scope: courseId,
},
canEditFiles: {
action: COURSE_PERMISSIONS.EDIT_FILES,
scope: courseId,
},
});
14 changes: 14 additions & 0 deletions src/files-and-videos/files-page/CourseFilesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { getFileSizeToClosestByte } from '@src/utils';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { useCourseUserPermissions } from '@src/authz/hooks';
import { getFilesPermissions } from '@src/authz/permissionHelpers';

export const CourseFilesTable = () => {
const intl = useIntl();
Expand All @@ -40,6 +42,12 @@ export const CourseFilesTable = () => {
errors: errorMessages,
} = useSelector((state: DeprecatedReduxState) => state.assets);

const {
canCreateFiles,
canDeleteFiles,
canEditFiles,
} = useCourseUserPermissions(courseId, getFilesPermissions(courseId));

const handleErrorReset = (error) => dispatch(resetErrors(error));
const handleDeleteFile = (id) => dispatch(deleteAssetFile(courseId, id));
const handleDownloadFile = (selectedRows) =>
Expand Down Expand Up @@ -69,6 +77,7 @@ export const CourseFilesTable = () => {
FileInfoModalSidebar({
asset,
handleLockedAsset: handleLockFile,
canLockFile: canEditFiles,
});

const assets = useModels('assets', assetIds);
Expand Down Expand Up @@ -180,6 +189,11 @@ export const CourseFilesTable = () => {
thumbnailPreview,
infoModalSidebar,
files: assets,
permissions: {
canCreateFiles,
canDeleteFiles,
canEditFiles,
},
}}
/>
<FileValidationModal {...{ handleFileOverwrite }} />
Expand Down
Loading