From cca7c2feb703cfe4a162b1ee5f22147af805830f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20M=C3=A9ndez?= Date: Thu, 10 Sep 2026 12:13:09 -0600 Subject: [PATCH 1/3] feat: add authz schema definition files Adds the course and library role/permission schema YAML files under openedx_authz/authz/. Part 2 of the schema loading pipeline stack. --- openedx_authz/authz/__init__.py | 35 ++ .../authz/course_permissions.authz.yaml | 331 ++++++++++++++++++ openedx_authz/authz/course_roles.authz.yaml | 158 +++++++++ .../authz/library_permissions.authz.yaml | 104 ++++++ openedx_authz/authz/library_roles.authz.yaml | 70 ++++ openedx_authz/tests/authz/__init__.py | 0 openedx_authz/tests/authz/test_resources.py | 41 +++ 7 files changed, 739 insertions(+) create mode 100644 openedx_authz/authz/__init__.py create mode 100644 openedx_authz/authz/course_permissions.authz.yaml create mode 100644 openedx_authz/authz/course_roles.authz.yaml create mode 100644 openedx_authz/authz/library_permissions.authz.yaml create mode 100644 openedx_authz/authz/library_roles.authz.yaml create mode 100644 openedx_authz/tests/authz/__init__.py create mode 100644 openedx_authz/tests/authz/test_resources.py diff --git a/openedx_authz/authz/__init__.py b/openedx_authz/authz/__init__.py new file mode 100644 index 00000000..06fb286f --- /dev/null +++ b/openedx_authz/authz/__init__.py @@ -0,0 +1,35 @@ +"""openedx-authz's own static authorization schema resources. + +This package ships the platform-default ``.authz.yaml`` files and exposes them +through the ``authz.schema`` entry point (ADR 0019). openedx-authz is a schema +provider like any other distribution; its files are discovered the same way a +third-party application's would be. + +Register in setup.py / pyproject.toml:: + + entry_points = { + "authz.schema": [ + "openedx_authz = openedx_authz.authz:get_schema_resources", + ], + } +""" + +from __future__ import annotations + +# Resource paths are relative to this module (``openedx_authz.authz``), which +# keeps discovery independent of virtualenv/container layout (ADR 0019). +SCHEMA_RESOURCES: tuple[str, ...] = ( + "library_permissions.authz.yaml", + "library_roles.authz.yaml", + "course_permissions.authz.yaml", + "course_roles.authz.yaml", +) + + +def get_schema_resources() -> list[str]: + """Return this package's schema resource paths (relative to this module). + + The ``authz.schema`` entry point points at this callable; the discovery + step resolves the returned paths via ``importlib.resources``. + """ + return list(SCHEMA_RESOURCES) diff --git a/openedx_authz/authz/course_permissions.authz.yaml b/openedx_authz/authz/course_permissions.authz.yaml new file mode 100644 index 00000000..3ded2457 --- /dev/null +++ b/openedx_authz/authz/course_permissions.authz.yaml @@ -0,0 +1,331 @@ +schema_version: "1.0" +priority: 100 + +# Course permission and category definitions for the +# openedx-authz authorization schema. +# +# Icons are Paragon icon names (@openedx/paragon/icons). + +permission_categories: + - id: course_access_content + display_name: Course access & content + description: Permissions related to accessing the course and managing core course content, including creating, editing, and publishing materials. + icon: BookOpen + - id: course_library_updates + display_name: Library updates + description: Permissions for reviewing and managing updates made to content libraries connected to the course. + icon: LibraryBooks + - id: course_updates_handouts + display_name: Course updates & handouts + description: Permissions for viewing and managing course updates and handouts that are visible to learners. + icon: Sync + - id: course_pages_resources + display_name: Pages & resources + description: Permissions for viewing and managing course pages and additional learning resources. + icon: Article + - id: course_files + display_name: Files + description: Permissions for viewing and managing files and assets uploaded to the course. + icon: Folder + - id: course_schedule_details + display_name: Schedule & details + description: Permissions for viewing and editing the course schedule and course information. + icon: Calendar + - id: course_grading + display_name: Grading + description: Permissions related to viewing and managing grading configuration and grading policies. + icon: Award + - id: course_team_group + display_name: Course team & groups + description: Permissions for viewing and managing the course team, learner groups, and group configurations. + icon: Group + - id: course_tags_taxonomies + display_name: Tags + description: Permissions for managing tags used to organize course content. + icon: LocalOffer + - id: course_advanced_certificates + display_name: Advanced & certificates + description: Permissions for managing advanced course settings and course certificates. + icon: CheckCircle + - id: course_import_export + display_name: Import / export + description: Permissions for importing and exporting course content and related data. + icon: Download + - id: course_other + display_name: Other + description: Additional permissions not included in other categories, such as viewing checklists. + icon: DrawShapes + # Backend-only category for legacy compatibility actions that have no frontend + # display metadata. These are referenced by legacy roles in authz.policy. + - id: course_legacy + display_name: Legacy compatibility + description: Internal compatibility permissions that map legacy course roles to the authorization system. Not shown as individually assignable permissions. + icon: DrawShapes + +permissions: + - namespace: courses + name: view_course + display_name: View course + description: See the course in the Studio home and access the course outline in read-only mode. Includes the "View Live" option to preview the course as a learner in the LMS. + category: course_access_content + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: edit_course_content + display_name: Edit course content + description: Edit the course outline, units, and components. + category: course_access_content + scopes: [course-v1] + icon: EditOutline + - namespace: courses + name: publish_course_content + display_name: Publish course content + description: Make course content visible to learners. + category: course_access_content + scopes: [course-v1] + icon: DownloadDone + - namespace: courses + name: view_library_updates + display_name: View library updates + description: View pending updates from content libraries linked to this course. + category: course_library_updates + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_library_updates + display_name: Manage library updates + description: Accept or reject pending updates from content libraries linked to this course. + category: course_library_updates + scopes: [course-v1] + icon: Checklist + - namespace: courses + name: view_course_updates + display_name: View course updates + description: See course announcements and handouts visible to learners. + category: course_updates_handouts + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_course_updates + display_name: Manage course updates + description: Create, edit, and delete course announcements and handouts. + category: course_updates_handouts + scopes: [course-v1] + icon: Settings + - namespace: courses + name: view_pages_and_resources + display_name: View pages & resources + description: See the Pages & Resources section in Studio. + category: course_pages_resources + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_pages_and_resources + display_name: Manage pages & resources + description: Enable or disable course features such as Discussions, the Wiki, Notes, Calculator, and Live. Create and edit Textbooks and Custom pages, and manage their configurations. + category: course_pages_resources + scopes: [course-v1] + icon: Settings + - namespace: courses + name: view_files + display_name: View files + description: See the list of files and assets uploaded to the course. + category: course_files + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: create_files + display_name: Create files + description: Upload new files and assets to the course. + category: course_files + scopes: [course-v1] + icon: Plus + - namespace: courses + name: edit_files + display_name: Edit files + description: Perform non-destructive actions on files, such as locking or unlocking them. + category: course_files + scopes: [course-v1] + icon: EditOutline + - namespace: courses + name: delete_files + display_name: Delete files + description: Permanently remove files and assets from the course. + category: course_files + scopes: [course-v1] + icon: Delete + - namespace: courses + name: view_schedule_and_details + display_name: View schedule & details + description: See the course schedule (start and end dates, enrollment dates, and pacing settings) and course details (summary, pacing, and prerequisites). + category: course_schedule_details + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: edit_schedule + display_name: Edit schedule + description: Update course start and end dates, enrollment dates, and pacing settings. + category: course_schedule_details + scopes: [course-v1] + icon: EditOutline + - namespace: courses + name: edit_details + display_name: Edit course details + description: Update course information including the course summary, pacing, and prerequisites. + category: course_schedule_details + scopes: [course-v1] + icon: EditOutline + - namespace: courses + name: view_grading_settings + display_name: View grading settings + description: See the grading configuration for the course, including assignment types and grading scale. + category: course_grading + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: edit_grading_settings + display_name: Edit grading settings + description: Update the grading configuration for the course, including assignment types and grading scale. + category: course_grading + scopes: [course-v1] + icon: EditOutline + - namespace: courses + name: view_course_team + display_name: View course team + description: See the list of users with a role assigned to this course. + category: course_team_group + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_course_team + display_name: Manage course team + description: Add, change, or remove role assignments for this course from the Roles and Permissions console. + category: course_team_group + scopes: [course-v1] + icon: Settings + - namespace: courses + name: view_group_configurations + display_name: View group configurations + description: See the list of content groups and their configurations for this course. + category: course_team_group + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_group_configurations + display_name: Manage group configurations + description: Create and manage content groups used to target course content to specific learners. + category: course_team_group + scopes: [course-v1] + icon: Settings + - namespace: courses + name: manage_tags + display_name: Manage tags + description: Create, edit, and delete tags on this course. + category: course_tags_taxonomies + scopes: [course-v1] + icon: Settings + - namespace: courses + name: view_advanced_settings + display_name: View advanced settings + description: Access the Advanced Settings page in Studio. This covers a wide range of technical course configurations, including proctoring, timed exams, LTI tools, enrollment limits, and custom display options. + category: course_advanced_certificates + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_advanced_settings + display_name: Manage advanced settings + description: Edit technical course configurations in the Advanced Settings page in Studio. + category: course_advanced_certificates + scopes: [course-v1] + icon: Settings + - namespace: courses + name: view_certificates + display_name: View certificates + description: See the course certificate settings. + category: course_advanced_certificates + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_certificates + display_name: Manage certificates + description: Create and edit course certificates, including certificate design and eligibility settings. + category: course_advanced_certificates + scopes: [course-v1] + icon: Settings + - namespace: courses + name: import_course + display_name: Import course + description: Import course content from a file. This is a high-privilege action that can overwrite most course content and settings. + category: course_import_export + scopes: [course-v1] + icon: Download + - namespace: courses + name: export_course + display_name: Export course + description: Download the course content as a file for backup or reuse in another platform. + category: course_import_export + scopes: [course-v1] + icon: Upload + - namespace: courses + name: export_tags + display_name: Export tags + description: Download the tag data associated with this course. + category: course_import_export + scopes: [course-v1] + icon: Upload + - namespace: courses + name: view_checklists + display_name: View checklists + description: See the course launch checklist in Studio. + category: course_other + scopes: [course-v1] + icon: RemoveRedEye + - namespace: courses + name: manage_taxonomies + display_name: Manage taxonomies + description: Manage taxonomies associated with this course. (Granted to course_admin; not currently surfaced in the admin console.) + category: course_tags_taxonomies + scopes: [course-v1] + icon: LocalOffer + + # ------------------------------------------------------------ + # Legacy compatibility course permissions + # Used for code paths that have not been upgraded to the + # new openedx-authz permissions. + # ------------------------------------------------------------ + + - namespace: courses + name: legacy_instructor_role_permissions + display_name: Legacy instructor permissions + description: Compatibility action mapping the legacy course instructor role into the authorization system. + category: course_legacy + scopes: [course-v1] + icon: DrawShapes + - namespace: courses + name: legacy_staff_role_permissions + display_name: Legacy staff permissions + description: Compatibility action mapping the legacy course staff role into the authorization system. + category: course_legacy + scopes: [course-v1] + icon: DrawShapes + - namespace: courses + name: legacy_limited_staff_role_permissions + display_name: Legacy limited staff permissions + description: Compatibility action mapping the legacy course limited staff role into the authorization system. + category: course_legacy + scopes: [course-v1] + icon: DrawShapes + - namespace: courses + name: legacy_data_researcher_permissions + display_name: Legacy data researcher permissions + description: Compatibility action mapping the legacy course data researcher role into the authorization system. + category: course_legacy + scopes: [course-v1] + icon: DrawShapes + - namespace: courses + name: legacy_beta_tester_permissions + display_name: Legacy beta tester permissions + description: Compatibility action mapping the legacy course beta tester role into the authorization system. + category: course_legacy + scopes: [course-v1] + icon: DrawShapes diff --git a/openedx_authz/authz/course_roles.authz.yaml b/openedx_authz/authz/course_roles.authz.yaml new file mode 100644 index 00000000..c056a6bb --- /dev/null +++ b/openedx_authz/authz/course_roles.authz.yaml @@ -0,0 +1,158 @@ +schema_version: "1.0" +priority: 100 + +# Course role definitions for the openedx-authz +# authorization schema. + +roles: + - id: course_admin + display_name: Course Admin + description: Can manage the course team and all course settings. + scopes: [course-v1] + permissions: + - courses.legacy_instructor_role_permissions + - courses.view_course + - courses.view_course_updates + - courses.view_pages_and_resources + - courses.view_files + - courses.view_grading_settings + - courses.view_checklists + - courses.view_course_team + - courses.view_schedule_and_details + - courses.view_advanced_settings + - courses.view_certificates + - courses.view_group_configurations + - courses.view_library_updates + - courses.edit_course_content + - courses.manage_library_updates + - courses.manage_course_updates + - courses.manage_pages_and_resources + - courses.create_files + - courses.edit_files + - courses.edit_grading_settings + - courses.manage_group_configurations + - courses.edit_details + - courses.manage_tags + - courses.publish_course_content + - courses.delete_files + - courses.edit_schedule + - courses.manage_advanced_settings + - courses.manage_certificates + - courses.import_course + - courses.export_course + - courses.export_tags + - courses.manage_course_team + - courses.manage_taxonomies + + - id: course_staff + display_name: Course Staff + description: Can publish content and manage the course lifecycle in Studio. + scopes: [course-v1] + permissions: + - courses.legacy_staff_role_permissions + - courses.view_course + - courses.view_course_updates + - courses.view_pages_and_resources + - courses.view_files + - courses.view_grading_settings + - courses.view_checklists + - courses.view_course_team + - courses.view_schedule_and_details + - courses.view_advanced_settings + - courses.view_certificates + - courses.view_group_configurations + - courses.view_library_updates + - courses.edit_course_content + - courses.manage_library_updates + - courses.manage_course_updates + - courses.manage_pages_and_resources + - courses.create_files + - courses.edit_files + - courses.edit_grading_settings + - courses.manage_group_configurations + - courses.edit_details + - courses.manage_tags + - courses.publish_course_content + - courses.delete_files + - courses.edit_schedule + - courses.manage_advanced_settings + - courses.manage_certificates + - courses.import_course + - courses.export_course + - courses.export_tags + + - id: course_editor + display_name: Course Editor + description: Can create and edit course content, but cannot publish or change critical course settings. + scopes: [course-v1] + hidden: true + permissions: + - courses.view_course + - courses.view_course_updates + - courses.view_pages_and_resources + - courses.view_files + - courses.view_grading_settings + - courses.view_checklists + - courses.view_course_team + - courses.view_schedule_and_details + - courses.view_advanced_settings + - courses.view_certificates + - courses.view_group_configurations + - courses.view_library_updates + - courses.edit_course_content + - courses.manage_library_updates + - courses.manage_course_updates + - courses.manage_pages_and_resources + - courses.create_files + - courses.edit_files + - courses.edit_grading_settings + - courses.manage_group_configurations + - courses.edit_details + - courses.manage_tags + + - id: course_auditor + display_name: Course Auditor + description: Can view course content and settings, but cannot make changes. + scopes: [course-v1] + hidden: true + permissions: + - courses.view_course + - courses.view_course_updates + - courses.view_pages_and_resources + - courses.view_files + - courses.view_grading_settings + - courses.view_checklists + - courses.view_course_team + - courses.view_schedule_and_details + - courses.view_advanced_settings + - courses.view_certificates + - courses.view_group_configurations + - courses.view_library_updates + + # ------------------------------------------------------------ + # Legacy course roles. + # Each grants a single legacy compatibility permission. + # ------------------------------------------------------------ + - id: course_limited_staff + display_name: Course Limited Staff (legacy) + description: Legacy course role retained for backward compatibility. Grants the legacy limited staff compatibility permission. + scopes: [course-v1] + hidden: true + permissions: + - courses.legacy_limited_staff_role_permissions + + - id: course_data_researcher + display_name: Course Data Researcher (legacy) + description: Legacy course role retained for backward compatibility. Grants the legacy data researcher compatibility permission. + scopes: [course-v1] + hidden: true + permissions: + - courses.legacy_data_researcher_permissions + + - id: course_beta_tester + display_name: Course Beta Tester (legacy) + description: Legacy course role retained for backward compatibility. Grants the legacy beta tester compatibility permission. + scopes: [course-v1] + hidden: true + permissions: + - courses.legacy_beta_tester_permissions diff --git a/openedx_authz/authz/library_permissions.authz.yaml b/openedx_authz/authz/library_permissions.authz.yaml new file mode 100644 index 00000000..f6a8fea1 --- /dev/null +++ b/openedx_authz/authz/library_permissions.authz.yaml @@ -0,0 +1,104 @@ +schema_version: "1.0" +priority: 100 + +# Content library permission and category definitions for the +# openedx-authz authorization schema. +# +# Icons are Paragon icon names (@openedx/paragon/icons). + +permission_categories: + - id: library + display_name: Library + description: Permissions related to viewing, managing, and publishing the library structure and metadata. + icon: CollectionsBookmark + - id: library_content + display_name: Content + description: Permissions for editing, publishing, and reusing content within the library. + icon: Notes + - id: library_team + display_name: Team + description: Permissions for viewing and managing users who have access to the library. + icon: Group + - id: library_collection + display_name: Collection + description: Permissions for creating and managing content collections within the library. + icon: AutoAwesomeMosaic + +permissions: + - namespace: content_libraries + name: view_library + display_name: View + description: See the library in Studio and access its content in read-only mode. + category: library + scopes: [lib] + icon: RemoveRedEye + - namespace: content_libraries + name: manage_library_tags + display_name: Manage tags + description: Create, edit, and delete tags on this library. + category: library + scopes: [lib] + icon: Settings + - namespace: content_libraries + name: delete_library + display_name: Delete + description: Allows users to delete the entire content library. + category: library + scopes: [lib] + icon: Delete + - namespace: content_libraries + name: edit_library_content + display_name: Edit + description: Create, edit, and delete content items in the library. + category: library_content + scopes: [lib] + icon: EditOutline + - namespace: content_libraries + name: publish_library_content + display_name: Publish + description: Publish individual content items to make them available for reuse in courses. + category: library_content + scopes: [lib] + icon: DownloadDone + - namespace: content_libraries + name: reuse_library_content + display_name: Reuse + description: Add published content from this library to a course. + category: library_content + scopes: [lib] + icon: SpinnerIcon + - namespace: content_libraries + name: view_library_team + display_name: View + description: See the list of users with a role assigned to this library. + category: library_team + scopes: [lib] + icon: RemoveRedEye + - namespace: content_libraries + name: manage_library_team + display_name: Manage + description: Add, change, or remove role assignments for this library from the Roles and Permissions console. + category: library_team + scopes: [lib] + icon: Settings + - namespace: content_libraries + name: create_library_collection + display_name: Create + description: Create new collections to organize content within the library. + category: library_collection + scopes: [lib] + icon: Plus + - namespace: content_libraries + name: edit_library_collection + display_name: Edit + description: Update the name and contents of existing collections. + category: library_collection + scopes: [lib] + icon: EditOutline + - namespace: content_libraries + name: delete_library_collection + display_name: Delete + description: Permanently remove collections from the library. + category: library_collection + scopes: [lib] + icon: Delete diff --git a/openedx_authz/authz/library_roles.authz.yaml b/openedx_authz/authz/library_roles.authz.yaml new file mode 100644 index 00000000..2c90ba8e --- /dev/null +++ b/openedx_authz/authz/library_roles.authz.yaml @@ -0,0 +1,70 @@ +schema_version: "1.0" +priority: 100 + +# Content library role definitions for the openedx-authz +# authorization schema. + +roles: + - id: library_admin + display_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. + scopes: [lib] + permissions: + - content_libraries.view_library + - content_libraries.manage_library_tags + - content_libraries.delete_library + - content_libraries.edit_library_content + - content_libraries.publish_library_content + - content_libraries.reuse_library_content + - content_libraries.view_library_team + - content_libraries.manage_library_team + - content_libraries.create_library_collection + - content_libraries.edit_library_collection + - content_libraries.delete_library_collection + + - id: library_author + display_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. + scopes: [lib] + permissions: + - content_libraries.view_library + - content_libraries.manage_library_tags + - content_libraries.edit_library_content + - content_libraries.publish_library_content + - content_libraries.reuse_library_content + - content_libraries.view_library_team + - content_libraries.create_library_collection + - content_libraries.edit_library_collection + - content_libraries.delete_library_collection + + - id: library_contributor + display_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. + scopes: [lib] + permissions: + - content_libraries.view_library + - content_libraries.manage_library_tags + - content_libraries.edit_library_content + - content_libraries.reuse_library_content + - content_libraries.view_library_team + - content_libraries.create_library_collection + - content_libraries.edit_library_collection + - content_libraries.delete_library_collection + + - id: library_user + display_name: Library User + description: The Library User can view and reuse content but cannot edit or delete any resource. + scopes: [lib] + permissions: + - content_libraries.view_library + - content_libraries.reuse_library_content + - content_libraries.view_library_team diff --git a/openedx_authz/tests/authz/__init__.py b/openedx_authz/tests/authz/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/openedx_authz/tests/authz/test_resources.py b/openedx_authz/tests/authz/test_resources.py new file mode 100644 index 00000000..2d420ec2 --- /dev/null +++ b/openedx_authz/tests/authz/test_resources.py @@ -0,0 +1,41 @@ +"""Tests for the ``openedx_authz.authz`` schema-provider entry point. + +These tests pin the contract of the ``authz.schema`` entry point (ADR 0019): +``get_schema_resources`` must return the declared schema paths, and each of +those paths must resolve to a real packaged resource so that discovery via +``importlib.resources`` cannot fail silently at runtime. +""" + +from importlib.resources import files +from unittest import TestCase + +from openedx_authz import authz + + +class TestSchemaProvider(TestCase): + """Test the ``openedx_authz`` schema-provider entry point.""" + + def test_get_schema_resources_returns_declared_paths(self): + """The entry point returns the declared resources as a fresh list.""" + result = authz.get_schema_resources() + + self.assertEqual(result, list(authz.SCHEMA_RESOURCES)) + # Must be a list (a copy), not the underlying tuple, so callers can + # mutate the result without affecting module state. + self.assertIsInstance(result, list) + self.assertIsNot(result, authz.SCHEMA_RESOURCES) + + def test_declared_schema_resources_exist_on_disk(self): + """Every declared resource resolves to a packaged file. + + Guards against a renamed or removed ``.authz.yaml`` file turning into a + silent schema-discovery failure at runtime. + """ + package_root = files("openedx_authz.authz") + + for resource in authz.get_schema_resources(): + with self.subTest(resource=resource): + self.assertTrue( + (package_root / resource).is_file(), + f"missing schema resource: {resource}", + ) From 38edd099e11d88e0719d1d90eb4f8fdf4a39ebb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20M=C3=A9ndez?= Date: Mon, 14 Sep 2026 12:23:46 -0600 Subject: [PATCH 2/3] style: unify formatting on authz schema yaml files --- .../authz/course_permissions.authz.yaml | 111 ++++++++++++------ openedx_authz/authz/course_roles.authz.yaml | 21 ++-- .../authz/library_permissions.authz.yaml | 33 ++++-- openedx_authz/authz/library_roles.authz.yaml | 12 +- 4 files changed, 118 insertions(+), 59 deletions(-) diff --git a/openedx_authz/authz/course_permissions.authz.yaml b/openedx_authz/authz/course_permissions.authz.yaml index 3ded2457..01fd254e 100644 --- a/openedx_authz/authz/course_permissions.authz.yaml +++ b/openedx_authz/authz/course_permissions.authz.yaml @@ -68,224 +68,256 @@ permissions: display_name: View course description: See the course in the Studio home and access the course outline in read-only mode. Includes the "View Live" option to preview the course as a learner in the LMS. category: course_access_content - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: edit_course_content display_name: Edit course content description: Edit the course outline, units, and components. category: course_access_content - scopes: [course-v1] + scopes: + - course-v1 icon: EditOutline - namespace: courses name: publish_course_content display_name: Publish course content description: Make course content visible to learners. category: course_access_content - scopes: [course-v1] + scopes: + - course-v1 icon: DownloadDone - namespace: courses name: view_library_updates display_name: View library updates description: View pending updates from content libraries linked to this course. category: course_library_updates - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_library_updates display_name: Manage library updates description: Accept or reject pending updates from content libraries linked to this course. category: course_library_updates - scopes: [course-v1] + scopes: + - course-v1 icon: Checklist - namespace: courses name: view_course_updates display_name: View course updates description: See course announcements and handouts visible to learners. category: course_updates_handouts - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_course_updates display_name: Manage course updates description: Create, edit, and delete course announcements and handouts. category: course_updates_handouts - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: view_pages_and_resources display_name: View pages & resources description: See the Pages & Resources section in Studio. category: course_pages_resources - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_pages_and_resources display_name: Manage pages & resources description: Enable or disable course features such as Discussions, the Wiki, Notes, Calculator, and Live. Create and edit Textbooks and Custom pages, and manage their configurations. category: course_pages_resources - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: view_files display_name: View files description: See the list of files and assets uploaded to the course. category: course_files - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: create_files display_name: Create files description: Upload new files and assets to the course. category: course_files - scopes: [course-v1] + scopes: + - course-v1 icon: Plus - namespace: courses name: edit_files display_name: Edit files description: Perform non-destructive actions on files, such as locking or unlocking them. category: course_files - scopes: [course-v1] + scopes: + - course-v1 icon: EditOutline - namespace: courses name: delete_files display_name: Delete files description: Permanently remove files and assets from the course. category: course_files - scopes: [course-v1] + scopes: + - course-v1 icon: Delete - namespace: courses name: view_schedule_and_details display_name: View schedule & details description: See the course schedule (start and end dates, enrollment dates, and pacing settings) and course details (summary, pacing, and prerequisites). category: course_schedule_details - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: edit_schedule display_name: Edit schedule description: Update course start and end dates, enrollment dates, and pacing settings. category: course_schedule_details - scopes: [course-v1] + scopes: + - course-v1 icon: EditOutline - namespace: courses name: edit_details display_name: Edit course details description: Update course information including the course summary, pacing, and prerequisites. category: course_schedule_details - scopes: [course-v1] + scopes: + - course-v1 icon: EditOutline - namespace: courses name: view_grading_settings display_name: View grading settings description: See the grading configuration for the course, including assignment types and grading scale. category: course_grading - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: edit_grading_settings display_name: Edit grading settings description: Update the grading configuration for the course, including assignment types and grading scale. category: course_grading - scopes: [course-v1] + scopes: + - course-v1 icon: EditOutline - namespace: courses name: view_course_team display_name: View course team description: See the list of users with a role assigned to this course. category: course_team_group - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_course_team display_name: Manage course team description: Add, change, or remove role assignments for this course from the Roles and Permissions console. category: course_team_group - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: view_group_configurations display_name: View group configurations description: See the list of content groups and their configurations for this course. category: course_team_group - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_group_configurations display_name: Manage group configurations description: Create and manage content groups used to target course content to specific learners. category: course_team_group - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: manage_tags display_name: Manage tags description: Create, edit, and delete tags on this course. category: course_tags_taxonomies - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: view_advanced_settings display_name: View advanced settings description: Access the Advanced Settings page in Studio. This covers a wide range of technical course configurations, including proctoring, timed exams, LTI tools, enrollment limits, and custom display options. category: course_advanced_certificates - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_advanced_settings display_name: Manage advanced settings description: Edit technical course configurations in the Advanced Settings page in Studio. category: course_advanced_certificates - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: view_certificates display_name: View certificates description: See the course certificate settings. category: course_advanced_certificates - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_certificates display_name: Manage certificates description: Create and edit course certificates, including certificate design and eligibility settings. category: course_advanced_certificates - scopes: [course-v1] + scopes: + - course-v1 icon: Settings - namespace: courses name: import_course display_name: Import course description: Import course content from a file. This is a high-privilege action that can overwrite most course content and settings. category: course_import_export - scopes: [course-v1] + scopes: + - course-v1 icon: Download - namespace: courses name: export_course display_name: Export course description: Download the course content as a file for backup or reuse in another platform. category: course_import_export - scopes: [course-v1] + scopes: + - course-v1 icon: Upload - namespace: courses name: export_tags display_name: Export tags description: Download the tag data associated with this course. category: course_import_export - scopes: [course-v1] + scopes: + - course-v1 icon: Upload - namespace: courses name: view_checklists display_name: View checklists description: See the course launch checklist in Studio. category: course_other - scopes: [course-v1] + scopes: + - course-v1 icon: RemoveRedEye - namespace: courses name: manage_taxonomies display_name: Manage taxonomies description: Manage taxonomies associated with this course. (Granted to course_admin; not currently surfaced in the admin console.) category: course_tags_taxonomies - scopes: [course-v1] + scopes: + - course-v1 icon: LocalOffer # ------------------------------------------------------------ @@ -299,33 +331,38 @@ permissions: display_name: Legacy instructor permissions description: Compatibility action mapping the legacy course instructor role into the authorization system. category: course_legacy - scopes: [course-v1] + scopes: + - course-v1 icon: DrawShapes - namespace: courses name: legacy_staff_role_permissions display_name: Legacy staff permissions description: Compatibility action mapping the legacy course staff role into the authorization system. category: course_legacy - scopes: [course-v1] + scopes: + - course-v1 icon: DrawShapes - namespace: courses name: legacy_limited_staff_role_permissions display_name: Legacy limited staff permissions description: Compatibility action mapping the legacy course limited staff role into the authorization system. category: course_legacy - scopes: [course-v1] + scopes: + - course-v1 icon: DrawShapes - namespace: courses name: legacy_data_researcher_permissions display_name: Legacy data researcher permissions description: Compatibility action mapping the legacy course data researcher role into the authorization system. category: course_legacy - scopes: [course-v1] + scopes: + - course-v1 icon: DrawShapes - namespace: courses name: legacy_beta_tester_permissions display_name: Legacy beta tester permissions description: Compatibility action mapping the legacy course beta tester role into the authorization system. category: course_legacy - scopes: [course-v1] + scopes: + - course-v1 icon: DrawShapes diff --git a/openedx_authz/authz/course_roles.authz.yaml b/openedx_authz/authz/course_roles.authz.yaml index c056a6bb..f070550a 100644 --- a/openedx_authz/authz/course_roles.authz.yaml +++ b/openedx_authz/authz/course_roles.authz.yaml @@ -8,7 +8,8 @@ roles: - id: course_admin display_name: Course Admin description: Can manage the course team and all course settings. - scopes: [course-v1] + scopes: + - course-v1 permissions: - courses.legacy_instructor_role_permissions - courses.view_course @@ -47,7 +48,8 @@ roles: - id: course_staff display_name: Course Staff description: Can publish content and manage the course lifecycle in Studio. - scopes: [course-v1] + scopes: + - course-v1 permissions: - courses.legacy_staff_role_permissions - courses.view_course @@ -84,7 +86,8 @@ roles: - id: course_editor display_name: Course Editor description: Can create and edit course content, but cannot publish or change critical course settings. - scopes: [course-v1] + scopes: + - course-v1 hidden: true permissions: - courses.view_course @@ -113,7 +116,8 @@ roles: - id: course_auditor display_name: Course Auditor description: Can view course content and settings, but cannot make changes. - scopes: [course-v1] + scopes: + - course-v1 hidden: true permissions: - courses.view_course @@ -136,7 +140,8 @@ roles: - id: course_limited_staff display_name: Course Limited Staff (legacy) description: Legacy course role retained for backward compatibility. Grants the legacy limited staff compatibility permission. - scopes: [course-v1] + scopes: + - course-v1 hidden: true permissions: - courses.legacy_limited_staff_role_permissions @@ -144,7 +149,8 @@ roles: - id: course_data_researcher display_name: Course Data Researcher (legacy) description: Legacy course role retained for backward compatibility. Grants the legacy data researcher compatibility permission. - scopes: [course-v1] + scopes: + - course-v1 hidden: true permissions: - courses.legacy_data_researcher_permissions @@ -152,7 +158,8 @@ roles: - id: course_beta_tester display_name: Course Beta Tester (legacy) description: Legacy course role retained for backward compatibility. Grants the legacy beta tester compatibility permission. - scopes: [course-v1] + scopes: + - course-v1 hidden: true permissions: - courses.legacy_beta_tester_permissions diff --git a/openedx_authz/authz/library_permissions.authz.yaml b/openedx_authz/authz/library_permissions.authz.yaml index f6a8fea1..b1e25be4 100644 --- a/openedx_authz/authz/library_permissions.authz.yaml +++ b/openedx_authz/authz/library_permissions.authz.yaml @@ -30,75 +30,86 @@ permissions: display_name: View description: See the library in Studio and access its content in read-only mode. category: library - scopes: [lib] + scopes: + - lib icon: RemoveRedEye - namespace: content_libraries name: manage_library_tags display_name: Manage tags description: Create, edit, and delete tags on this library. category: library - scopes: [lib] + scopes: + - lib icon: Settings - namespace: content_libraries name: delete_library display_name: Delete description: Allows users to delete the entire content library. category: library - scopes: [lib] + scopes: + - lib icon: Delete - namespace: content_libraries name: edit_library_content display_name: Edit description: Create, edit, and delete content items in the library. category: library_content - scopes: [lib] + scopes: + - lib icon: EditOutline - namespace: content_libraries name: publish_library_content display_name: Publish description: Publish individual content items to make them available for reuse in courses. category: library_content - scopes: [lib] + scopes: + - lib icon: DownloadDone - namespace: content_libraries name: reuse_library_content display_name: Reuse description: Add published content from this library to a course. category: library_content - scopes: [lib] + scopes: + - lib icon: SpinnerIcon - namespace: content_libraries name: view_library_team display_name: View description: See the list of users with a role assigned to this library. category: library_team - scopes: [lib] + scopes: + - lib icon: RemoveRedEye - namespace: content_libraries name: manage_library_team display_name: Manage description: Add, change, or remove role assignments for this library from the Roles and Permissions console. category: library_team - scopes: [lib] + scopes: + - lib icon: Settings - namespace: content_libraries name: create_library_collection display_name: Create description: Create new collections to organize content within the library. category: library_collection - scopes: [lib] + scopes: + - lib icon: Plus - namespace: content_libraries name: edit_library_collection display_name: Edit description: Update the name and contents of existing collections. category: library_collection - scopes: [lib] + scopes: + - lib icon: EditOutline - namespace: content_libraries name: delete_library_collection display_name: Delete description: Permanently remove collections from the library. category: library_collection - scopes: [lib] + scopes: + - lib icon: Delete diff --git a/openedx_authz/authz/library_roles.authz.yaml b/openedx_authz/authz/library_roles.authz.yaml index 2c90ba8e..ddecb9ce 100644 --- a/openedx_authz/authz/library_roles.authz.yaml +++ b/openedx_authz/authz/library_roles.authz.yaml @@ -11,7 +11,8 @@ roles: 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. - scopes: [lib] + scopes: + - lib permissions: - content_libraries.view_library - content_libraries.manage_library_tags @@ -31,7 +32,8 @@ roles: 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. - scopes: [lib] + scopes: + - lib permissions: - content_libraries.view_library - content_libraries.manage_library_tags @@ -49,7 +51,8 @@ roles: 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. - scopes: [lib] + scopes: + - lib permissions: - content_libraries.view_library - content_libraries.manage_library_tags @@ -63,7 +66,8 @@ roles: - id: library_user display_name: Library User description: The Library User can view and reuse content but cannot edit or delete any resource. - scopes: [lib] + scopes: + - lib permissions: - content_libraries.view_library - content_libraries.reuse_library_content From 7a477804917ff10080ebd85e8a850faca66943c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20M=C3=A9ndez?= Date: Mon, 14 Sep 2026 12:31:03 -0600 Subject: [PATCH 3/3] refactor: move authz schema files to authz/schema and drop .authz infix Relocate the four platform-default schema files into openedx_authz/authz/schema and drop the .authz filename infix. Update the schema-provider entry point resource paths and the test that pins them. No loader/discovery implementation is included; get_schema_resources still returns individual resource paths. --- openedx_authz/authz/__init__.py | 28 ++++++------ .../course_permissions.yaml} | 0 .../course_roles.yaml} | 0 .../library_permissions.yaml} | 0 .../library_roles.yaml} | 0 openedx_authz/tests/authz/test_resources.py | 45 +++++++++++-------- 6 files changed, 39 insertions(+), 34 deletions(-) rename openedx_authz/authz/{course_permissions.authz.yaml => schema/course_permissions.yaml} (100%) rename openedx_authz/authz/{course_roles.authz.yaml => schema/course_roles.yaml} (100%) rename openedx_authz/authz/{library_permissions.authz.yaml => schema/library_permissions.yaml} (100%) rename openedx_authz/authz/{library_roles.authz.yaml => schema/library_roles.yaml} (100%) diff --git a/openedx_authz/authz/__init__.py b/openedx_authz/authz/__init__.py index 06fb286f..02944798 100644 --- a/openedx_authz/authz/__init__.py +++ b/openedx_authz/authz/__init__.py @@ -1,9 +1,10 @@ """openedx-authz's own static authorization schema resources. -This package ships the platform-default ``.authz.yaml`` files and exposes them -through the ``authz.schema`` entry point (ADR 0019). openedx-authz is a schema -provider like any other distribution; its files are discovered the same way a -third-party application's would be. +This package ships the platform-default schema files under ``authz/schema`` and +exposes that directory through the ``authz.schema`` entry point (ADR 0019). +openedx-authz is a schema provider like any other distribution; its directory is +discovered the same way a third-party application's would be, and the loader +reads every ``.yaml`` file inside it. Register in setup.py / pyproject.toml:: @@ -16,20 +17,17 @@ from __future__ import annotations -# Resource paths are relative to this module (``openedx_authz.authz``), which -# keeps discovery independent of virtualenv/container layout (ADR 0019). -SCHEMA_RESOURCES: tuple[str, ...] = ( - "library_permissions.authz.yaml", - "library_roles.authz.yaml", - "course_permissions.authz.yaml", - "course_roles.authz.yaml", -) +# Directory paths (relative to an importable top-level package) that contain +# this distribution's ``.yaml`` schema files. Per ADR 0019, providers return +# directories, not individual files. +SCHEMA_DIRECTORIES: tuple[str, ...] = ("openedx_authz/authz/schema",) def get_schema_resources() -> list[str]: - """Return this package's schema resource paths (relative to this module). + """Return this package's schema directory paths. The ``authz.schema`` entry point points at this callable; the discovery - step resolves the returned paths via ``importlib.resources``. + step resolves each returned directory via ``importlib.resources`` and loads + every ``.yaml`` file it contains. """ - return list(SCHEMA_RESOURCES) + return list(SCHEMA_DIRECTORIES) diff --git a/openedx_authz/authz/course_permissions.authz.yaml b/openedx_authz/authz/schema/course_permissions.yaml similarity index 100% rename from openedx_authz/authz/course_permissions.authz.yaml rename to openedx_authz/authz/schema/course_permissions.yaml diff --git a/openedx_authz/authz/course_roles.authz.yaml b/openedx_authz/authz/schema/course_roles.yaml similarity index 100% rename from openedx_authz/authz/course_roles.authz.yaml rename to openedx_authz/authz/schema/course_roles.yaml diff --git a/openedx_authz/authz/library_permissions.authz.yaml b/openedx_authz/authz/schema/library_permissions.yaml similarity index 100% rename from openedx_authz/authz/library_permissions.authz.yaml rename to openedx_authz/authz/schema/library_permissions.yaml diff --git a/openedx_authz/authz/library_roles.authz.yaml b/openedx_authz/authz/schema/library_roles.yaml similarity index 100% rename from openedx_authz/authz/library_roles.authz.yaml rename to openedx_authz/authz/schema/library_roles.yaml diff --git a/openedx_authz/tests/authz/test_resources.py b/openedx_authz/tests/authz/test_resources.py index 2d420ec2..59391b75 100644 --- a/openedx_authz/tests/authz/test_resources.py +++ b/openedx_authz/tests/authz/test_resources.py @@ -1,9 +1,10 @@ """Tests for the ``openedx_authz.authz`` schema-provider entry point. These tests pin the contract of the ``authz.schema`` entry point (ADR 0019): -``get_schema_resources`` must return the declared schema paths, and each of -those paths must resolve to a real packaged resource so that discovery via -``importlib.resources`` cannot fail silently at runtime. +``get_schema_resources`` must return the declared schema *directories*, and each +of those directories must resolve to a real packaged directory containing at +least one ``.yaml`` file, so that discovery via ``importlib.resources`` cannot +fail silently at runtime. """ from importlib.resources import files @@ -15,27 +16,33 @@ class TestSchemaProvider(TestCase): """Test the ``openedx_authz`` schema-provider entry point.""" - def test_get_schema_resources_returns_declared_paths(self): - """The entry point returns the declared resources as a fresh list.""" + def test_get_schema_resources_returns_declared_directories(self): + """The entry point returns the declared directories as a fresh list.""" result = authz.get_schema_resources() - self.assertEqual(result, list(authz.SCHEMA_RESOURCES)) + self.assertEqual(result, list(authz.SCHEMA_DIRECTORIES)) # Must be a list (a copy), not the underlying tuple, so callers can # mutate the result without affecting module state. self.assertIsInstance(result, list) - self.assertIsNot(result, authz.SCHEMA_RESOURCES) + self.assertIsNot(result, authz.SCHEMA_DIRECTORIES) - def test_declared_schema_resources_exist_on_disk(self): - """Every declared resource resolves to a packaged file. + def test_declared_directories_exist_and_contain_yaml(self): + """Every declared directory resolves to a packaged dir with schema files. - Guards against a renamed or removed ``.authz.yaml`` file turning into a - silent schema-discovery failure at runtime. + Guards against a renamed or removed ``authz/schema`` directory turning + into a silent schema-discovery failure at runtime. """ - package_root = files("openedx_authz.authz") - - for resource in authz.get_schema_resources(): - with self.subTest(resource=resource): - self.assertTrue( - (package_root / resource).is_file(), - f"missing schema resource: {resource}", - ) + for directory in authz.get_schema_resources(): + with self.subTest(directory=directory): + parts = [segment for segment in directory.strip("/").split("/") if segment] + anchor, subpath = parts[0], "/".join(parts[1:]) + target = files(anchor) + if subpath: + target = target.joinpath(subpath) + self.assertTrue(target.is_dir(), f"missing schema directory: {directory}") + yaml_files = [ + entry.name + for entry in target.iterdir() + if entry.is_file() and entry.name.endswith((".yaml", ".yml")) + ] + self.assertTrue(yaml_files, f"no .yaml files in schema directory: {directory}")