Skip to content

Commit 4115799

Browse files
committed
feat: add VIEW permission for advanced settings access control
1 parent 66191a9 commit 4115799

1 file changed

Lines changed: 46 additions & 43 deletions

File tree

common/djangoapps/student/auth.py

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from django.core.exceptions import PermissionDenied
1212
from opaque_keys.edx.locator import LibraryLocator
1313
from openedx_authz import api as authz_api
14-
from openedx_authz.constants.permissions import COURSES_CREATE_COURSE, COURSES_MANAGE_ADVANCED_SETTINGS
14+
from openedx_authz.constants.permissions import COURSES_CREATE_COURSE, COURSES_MANAGE_ADVANCED_SETTINGS, COURSES_VIEW_ADVANCED_SETTINGS
1515

1616
from common.djangoapps.student.roles import (
1717
CourseBetaTesterRole,
@@ -187,40 +187,57 @@ def check_course_advanced_settings_access(user, course_key, access_type='read'):
187187
Uses openedx-authz when AUTHZ_COURSE_AUTHORING_FLAG is enabled,
188188
otherwise falls back to legacy permission checks.
189189
190-
If the DISABLE_ADVANCED_SETTINGS feature flag is on, then authz will not be used for the
191-
permission check.
190+
When DISABLE_ADVANCED_SETTINGS is enabled, only the 'feature_restricted' access type
191+
bypasses authz (staff/superuser only); 'read' and 'write' still go through authz normally.
192192
193193
Args:
194194
user: Django user object
195195
course_key: CourseKey for the course
196196
access_type: Type of access to check. Options:
197-
- 'read': Check studio read access (default)
198-
- 'write': Check studio write access
199-
- 'feature_restricted': Check access based on the DISABLE_ADVANCED_SETTINGS feature
200-
197+
- 'read': granted to users with MANAGE or VIEW permission (auditors get read-only);
198+
in legacy mode delegates to has_studio_read_access
199+
- 'write': requires MANAGE permission; in legacy mode delegates to has_studio_write_access
200+
- 'feature_restricted': requires MANAGE permission (or staff/superuser when
201+
DISABLE_ADVANCED_SETTINGS is set); in legacy mode delegates to
202+
has_studio_advanced_settings_access
201203
Returns:
202204
bool: True if user has permission, False otherwise
205+
206+
Raises
207+
ValueError: If access_type is not one of 'read', 'write', or 'feature_restricted'.
203208
"""
204-
if core_toggles.AUTHZ_COURSE_AUTHORING_FLAG.is_enabled(course_key):
205-
# For feature_restricted access type, check DISABLE_ADVANCED_SETTINGS feature
206-
if (
207-
access_type == 'feature_restricted'
208-
and settings.FEATURES.get('DISABLE_ADVANCED_SETTINGS', False)
209-
):
210-
# When feature is disabled, only staff/superuser can access (bypass authz)
211-
return user.is_staff or user.is_superuser
212-
# Otherwise check authz permission
213-
return authz_api.is_user_allowed(user.username, COURSES_MANAGE_ADVANCED_SETTINGS.identifier, str(course_key))
214-
215-
# Legacy permission checks
216-
if access_type == 'read':
217-
return has_studio_read_access(user, course_key)
218-
if access_type == 'feature_restricted':
219-
return has_studio_advanced_settings_access(user)
220-
if access_type == 'write':
209+
if access_type not in ('read', 'write', 'feature_restricted'):
210+
raise ValueError(f"Invalid access_type: {access_type!r}")
211+
212+
if not core_toggles.AUTHZ_COURSE_AUTHORING_FLAG.is_enabled(course_key):
213+
if access_type == 'read':
214+
return has_studio_read_access(user, course_key)
215+
if access_type == 'feature_restricted':
216+
return has_studio_advanced_settings_access(user)
221217
return has_studio_write_access(user, course_key)
222218

223-
raise ValueError(f"Invalid access_type: {access_type}")
219+
# Feature flag override: when DISABLE_ADVANCED_SETTINGS is enabled,
220+
# only staff/superuser can access regardless of authz permissions
221+
if access_type == 'feature_restricted' and settings.FEATURES.get('DISABLE_ADVANCED_SETTINGS', False):
222+
return user.is_staff or user.is_superuser
223+
224+
# MANAGE satisfies all access types. Check it first for all three cases.
225+
if authz_api.is_user_allowed(
226+
user.username,
227+
COURSES_MANAGE_ADVANCED_SETTINGS.identifier,
228+
str(course_key),
229+
):
230+
return True
231+
232+
# Only 'read' falls back to VIEW (auditor access); 'write' and 'feature_restricted' require MANAGE.
233+
if access_type == 'read':
234+
return authz_api.is_user_allowed(
235+
user.username,
236+
COURSES_VIEW_ADVANCED_SETTINGS.identifier,
237+
str(course_key),
238+
)
239+
240+
return False
224241

225242

226243
def is_content_creator(user, org):
@@ -231,19 +248,9 @@ def is_content_creator(user, org):
231248
state of the AuthZ feature flag, it delegates the evaluation to either the AuthZ-based
232249
RBAC system or the legacy role-based permission system.
233250
234-
Args:
235-
user (User): The user whose permissions are being evaluated.
236-
org (str): The organization identifier used as the permission scope.
237-
238-
Returns:
239-
bool: True if the user has permission to create course content in the given
240-
organization, False otherwise.
241-
242-
Notes:
243-
- When AuthZ is enabled, this checks permissions via RBAC policies.
244-
- When AuthZ is disabled, this falls back to legacy Django role checks.
245-
- Course creation may still be blocked by global feature flags (e.g.,
246-
DISABLE_COURSE_CREATION), which are enforced downstream.
251+
:param user: The user whose permissions are being evaluated.
252+
:param org: The organization identifier used as the permission scope.
253+
:returns: True if the user has permission to create course content in the given org.
247254
"""
248255
if core_toggles.AUTHZ_COURSE_AUTHORING_FLAG.is_enabled():
249256
return _has_content_creator_access(user, org)
@@ -268,11 +275,7 @@ def _has_content_creator_access(user, org):
268275

269276
def _has_legacy_content_creator_access(user, org):
270277
"""
271-
Check if the user has the role to create content.
272-
273-
This function checks if the User has role to create content
274-
or if the org is supplied, it checks for Org level course content
275-
creator.
278+
Check legacy role-based content creator access for the given user and org.
276279
"""
277280
return (user_has_role(user, CourseCreatorRole()) or
278281
user_has_role(user, OrgContentCreatorRole(org=org)))

0 commit comments

Comments
 (0)