1111from django .core .exceptions import PermissionDenied
1212from opaque_keys .edx .locator import LibraryLocator
1313from 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
1616from 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
226243def 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
269276def _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