diff --git a/openedx_authz/authz/__init__.py b/openedx_authz/authz/__init__.py new file mode 100644 index 00000000..02944798 --- /dev/null +++ b/openedx_authz/authz/__init__.py @@ -0,0 +1,33 @@ +"""openedx-authz's own static authorization schema resources. + +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:: + + entry_points = { + "authz.schema": [ + "openedx_authz = openedx_authz.authz:get_schema_resources", + ], + } +""" + +from __future__ import annotations + +# 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 directory paths. + + The ``authz.schema`` entry point points at this callable; the discovery + step resolves each returned directory via ``importlib.resources`` and loads + every ``.yaml`` file it contains. + """ + return list(SCHEMA_DIRECTORIES) diff --git a/openedx_authz/authz/schema/course_permissions.yaml b/openedx_authz/authz/schema/course_permissions.yaml new file mode 100644 index 00000000..01fd254e --- /dev/null +++ b/openedx_authz/authz/schema/course_permissions.yaml @@ -0,0 +1,368 @@ +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/schema/course_roles.yaml b/openedx_authz/authz/schema/course_roles.yaml new file mode 100644 index 00000000..f070550a --- /dev/null +++ b/openedx_authz/authz/schema/course_roles.yaml @@ -0,0 +1,165 @@ +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/schema/library_permissions.yaml b/openedx_authz/authz/schema/library_permissions.yaml new file mode 100644 index 00000000..b1e25be4 --- /dev/null +++ b/openedx_authz/authz/schema/library_permissions.yaml @@ -0,0 +1,115 @@ +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/schema/library_roles.yaml b/openedx_authz/authz/schema/library_roles.yaml new file mode 100644 index 00000000..ddecb9ce --- /dev/null +++ b/openedx_authz/authz/schema/library_roles.yaml @@ -0,0 +1,74 @@ +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..59391b75 --- /dev/null +++ b/openedx_authz/tests/authz/test_resources.py @@ -0,0 +1,48 @@ +"""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 *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 +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_directories(self): + """The entry point returns the declared directories as a fresh list.""" + result = authz.get_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_DIRECTORIES) + + 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/schema`` directory turning + into a silent schema-discovery failure at runtime. + """ + 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}")