Skip to content

Commit ba9db08

Browse files
docs: add authorization schema reference
1 parent e84691b commit ba9db08

4 files changed

Lines changed: 353 additions & 1 deletion

File tree

docs/decisions/0017-static-authorization-schema.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Decision
2121

2222
The authz schema is a versioned YAML format for static permissions, permission categories, roles, and changes to existing roles. Every file declares ``schema_version`` and ``priority``. Open edX publishes a YAML Schema for this format so that editors, CI, and the compiler all apply the same field and validation rules.
2323

24+
The :ref:`Authorization Schema Reference` describes every field, the naming conventions, and complete configuration examples.
25+
2426
#. Permissions and categories
2527
=============================
2628

docs/decisions/0023-extend-static-roles.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Decision
1919
#. Role extension fields
2020
========================
2121

22-
A ``role_extensions`` entry identifies an existing static role with ``role`` and changes only the fields included in the entry. It may use:
22+
A ``role_extensions`` entry identifies an existing static role with ``role`` and changes only the fields included in the entry. The :ref:`Authorization Schema Reference` describes these fields and includes complete examples for applications and Tutor configuration. An entry may use:
2323

2424
* ``add_permissions`` to add complete permission IDs;
2525
* ``remove_permissions`` to remove complete permission IDs;
@@ -129,6 +129,7 @@ References
129129
* `ADR 0017`_
130130
* `ADR 0018`_
131131
* `ADR 0019`_
132+
* :ref:`Authorization Schema Reference`
132133
* `Tutor plugin development`_
133134

134135
.. _ADR 0017: 0017-static-authorization-schema.rst
Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
.. _Authorization Schema Reference:
2+
3+
Authorization Schema Reference
4+
##############################
5+
6+
The Open edX Authorization schema, or authz schema, is a YAML configuration format for static permissions, permission categories, roles, and changes to existing roles. Applications ship schema files with their code, while site operators can contribute the same format through their deployment configuration. Deployment validates and compiles all contributions into the policy used by ``openedx-authz``.
7+
8+
Use this reference when creating or reviewing an authz schema file. The examples omit fields only when the surrounding section does not need them.
9+
10+
.. contents:: Contents
11+
:depth: 2
12+
:local:
13+
14+
Complete example
15+
****************
16+
17+
The following file defines one category, two permissions, one role, and an extension to a role defined elsewhere:
18+
19+
.. code-block:: yaml
20+
21+
schema_version: "1.0"
22+
priority: 100
23+
24+
permission_categories:
25+
- id: course_content
26+
display_name: Course content
27+
description: Permissions for viewing and editing course content.
28+
icon: Article
29+
30+
permissions:
31+
- namespace: courses
32+
name: view_course
33+
display_name: View course
34+
description: View course configuration and content.
35+
category: course_content
36+
scopes:
37+
- course-v1
38+
icon: Visibility
39+
40+
- namespace: courses
41+
name: view_course_updates
42+
display_name: View course updates
43+
description: View course update posts.
44+
category: course_content
45+
scopes:
46+
- course-v1
47+
icon: Visibility
48+
49+
roles:
50+
- id: course_observer
51+
display_name: Course observer
52+
description: Reviews a course without changing it.
53+
scopes:
54+
- course-v1
55+
permissions:
56+
- courses.view_course
57+
- courses.view_course_updates
58+
59+
role_extensions:
60+
- role: course_editor
61+
add_permissions:
62+
- courses.export_course
63+
64+
Top-level fields
65+
****************
66+
67+
``schema_version``
68+
==================
69+
70+
The version of the YAML format used by the file. Write it as a quoted ``major.minor`` value, such as ``"1.0"``. A deployment stops before changing the database when it encounters a version it cannot read.
71+
72+
``priority``
73+
============
74+
75+
An integer used when several files change the same definition or role field. A higher number takes precedence. Contributions with the same priority may be combined when they agree or affect different fields, but conflicting values at the same priority fail validation.
76+
77+
Priority does not control the order shown in a user interface. Clients may sort roles, permissions, and categories for their own presentation.
78+
79+
``permission_categories``
80+
=========================
81+
82+
A list of category definitions used to group permissions for display and discovery. Categories do not grant access.
83+
84+
``permissions``
85+
===============
86+
87+
A list of permission definitions. Application checks use the stable permission ID formed from each permission's ``namespace`` and ``name``.
88+
89+
``roles``
90+
=========
91+
92+
A list of static role definitions. A role lists every permission assigned to it.
93+
94+
``role_extensions``
95+
===================
96+
97+
A list of changes to static roles defined in this file or another schema contribution. An extension changes only the fields it includes and does not copy or replace the complete role.
98+
99+
Permission categories
100+
*********************
101+
102+
A category contains these fields:
103+
104+
``id``
105+
The stable category identifier. It is required and uses lowercase snake case, such as ``course_content`` or ``library_management``. Category IDs are global and do not include a permission namespace. Applications that use the same ID contribute permissions to the same category.
106+
107+
``display_name``
108+
The source-language name shown to users. It uses sentence case and is translated through the authz schema translation process.
109+
110+
``description``
111+
A complete source-language sentence describing the group of permissions.
112+
113+
``icon``
114+
An optional icon name exported by ``@openedx/paragon/icons``. The value is case-sensitive, such as ``Article``.
115+
116+
For example:
117+
118+
.. code-block:: yaml
119+
120+
permission_categories:
121+
- id: library_management
122+
display_name: Library management
123+
description: Permissions for managing content libraries.
124+
icon: Article
125+
126+
Permissions
127+
***********
128+
129+
A permission contains these fields:
130+
131+
``namespace``
132+
The stable product domain that owns the permission. It uses lowercase snake case, such as ``courses`` or ``content_libraries``. The namespace does not need to match the Python package, Django app, IDA, or Tutor plugin that contributes the file. Code may move between applications without changing the permission ID.
133+
134+
``name``
135+
The operation within the product domain. It uses lowercase snake case and normally begins with a verb, such as ``view_course``, ``export_course``, or ``manage_library_tags``.
136+
137+
``display_name``
138+
The source-language name shown to users. Changing it does not change the permission ID used by application checks.
139+
140+
``description``
141+
A complete source-language sentence describing the access controlled by the permission.
142+
143+
``category``
144+
The complete ID of a category defined in the combined schema.
145+
146+
``scopes``
147+
The scope namespaces where the permission can apply. These values come from registered ``ScopeData`` types, such as ``course-v1``, ``ccx-v1``, or ``lib``.
148+
149+
``icon``
150+
An optional, case-sensitive icon name exported by ``@openedx/paragon/icons``.
151+
152+
The complete permission ID joins ``namespace`` and ``name`` with a period. For example:
153+
154+
.. code-block:: yaml
155+
156+
permissions:
157+
- namespace: content_libraries
158+
name: manage_library_tags
159+
display_name: Manage library tags
160+
description: Add, edit, and remove tags in a content library.
161+
category: library_management
162+
scopes:
163+
- lib
164+
165+
The complete ID is ``content_libraries.manage_library_tags``. Role definitions, role extensions, application checks, and API responses use this value.
166+
167+
The Casbin form ``act^content_libraries.manage_library_tags`` is an internal value and is not valid in a schema file.
168+
169+
Roles
170+
*****
171+
172+
A role contains these fields:
173+
174+
``id``
175+
The stable role identifier. It uses lowercase snake case, such as ``course_admin``, ``course_editor``, or ``library_author``. Role IDs do not include a product namespace because authorization uses the role within its supported scopes.
176+
177+
``display_name``
178+
The source-language name shown to users.
179+
180+
``description``
181+
A complete source-language sentence describing what the role can do.
182+
183+
``scopes``
184+
The scope namespaces where the role can be assigned. Every permission listed by the role must support those scopes.
185+
186+
``permissions``
187+
A list of complete permission IDs. The compiler does not infer one permission from another, so the role lists every permission it needs.
188+
189+
``icon``
190+
An optional, case-sensitive icon name exported by ``@openedx/paragon/icons``.
191+
192+
``hidden``
193+
An optional boolean that defaults to ``false``. A hidden role does not appear in normal role discovery and selection interfaces. Hiding does not delete the role, remove existing assignments, or change permission checks.
194+
195+
For example:
196+
197+
.. code-block:: yaml
198+
199+
roles:
200+
- id: library_reviewer
201+
display_name: Library reviewer
202+
description: Reviews library content without publishing it.
203+
scopes:
204+
- lib
205+
permissions:
206+
- content_libraries.view_library
207+
- content_libraries.view_library_team
208+
icon: Visibility
209+
210+
The Casbin form ``role^library_reviewer`` is an internal value and is not valid as ``roles.id`` or in a ``role_extensions.role`` reference.
211+
212+
Role extensions
213+
***************
214+
215+
A role extension contains ``role`` and at least one field to change:
216+
217+
``role``
218+
The complete ID of an existing static role.
219+
220+
``add_permissions``
221+
Complete permission IDs to add to the role.
222+
223+
``remove_permissions``
224+
Complete permission IDs to remove from the role.
225+
226+
``display_name``, ``description``, and ``icon``
227+
Display metadata to replace. Metadata fields left out of the extension keep their current values.
228+
229+
``hidden``
230+
Whether the role appears in normal role discovery and selection interfaces.
231+
232+
For example, a deployment can allow course editors to export courses, remove their access to tag management, change the displayed role name, and hide the course auditor role:
233+
234+
.. code-block:: yaml
235+
236+
schema_version: "1.0"
237+
priority: 200
238+
239+
role_extensions:
240+
- role: course_editor
241+
add_permissions:
242+
- courses.export_course
243+
remove_permissions:
244+
- courses.manage_tags
245+
display_name: Course author
246+
description: Creates and exports course content.
247+
248+
- role: course_auditor
249+
hidden: true
250+
251+
An extension fails validation when its target role or a referenced permission does not exist. Adding a permission already assigned to the role or removing one the role does not have produces a warning and leaves the result unchanged.
252+
253+
Identifier rules
254+
****************
255+
256+
Permission namespaces, permission names, category IDs, and role IDs use lowercase letters, numbers, and underscores, begin with a letter, and match ``[a-z][a-z0-9_]*``. The period in a complete permission ID separates its namespace from its name and does not appear inside either part.
257+
258+
Valid identifiers include:
259+
260+
.. code-block:: text
261+
262+
courses
263+
view_course
264+
courses.view_course
265+
course_content
266+
course_editor
267+
268+
The following values are invalid:
269+
270+
.. code-block:: text
271+
272+
Courses.view_course # uppercase letter
273+
courses:view_course # wrong separator
274+
act^courses.view_course # internal Casbin namespace
275+
course content # space
276+
role^course_editor # internal Casbin namespace
277+
278+
Scope namespaces follow the spelling registered by their ``ScopeData`` type and may contain a hyphen. Do not apply the snake-case identifier rule to values such as ``course-v1`` or ``ccx-v1``.
279+
280+
Schema files in applications
281+
****************************
282+
283+
Applications keep schema resources under an ``authz`` package directory and use the ``.authz.yaml`` suffix. The filename describes the definitions in the file using lowercase snake case:
284+
285+
.. code-block:: text
286+
287+
course_authoring/
288+
└── authz/
289+
├── course_permissions.authz.yaml
290+
└── course_roles.authz.yaml
291+
292+
The application exposes these package resources through the ``openedx-authz`` schema entry point. Resource paths are relative to the Python module, which keeps discovery independent of virtual-environment and container paths.
293+
294+
Tutor configuration for site operators
295+
**************************************
296+
297+
A site operator can provide an authz schema through the ``openedx-authz-schema`` patch. Run ``tutor plugins printroot`` to find the local plugin directory, then create ``openedx-authz-overrides.yml`` there:
298+
299+
.. code-block:: yaml
300+
301+
name: openedx-authz-overrides
302+
version: 0.1.0
303+
304+
patches:
305+
openedx-authz-schema: |
306+
schema_version: "1.0"
307+
priority: 200
308+
309+
role_extensions:
310+
- role: course_editor
311+
add_permissions:
312+
- courses.export_course
313+
remove_permissions:
314+
- courses.manage_tags
315+
display_name: Course author
316+
description: Creates and exports course content.
317+
318+
- role: course_auditor
319+
hidden: true
320+
321+
Enable the plugin and save the rendered Tutor configuration:
322+
323+
.. code-block:: console
324+
325+
tutor plugins enable openedx-authz-overrides
326+
tutor config save
327+
328+
The next deployment validates and compiles the patch with the schema files provided by applications.
329+
330+
Checking the resulting permissions
331+
**********************************
332+
333+
After deployment, use the existing ``enforcement`` management command to check the policy stored in the database:
334+
335+
.. code-block:: console
336+
337+
tutor local run lms ./manage.py lms enforcement
338+
339+
The command expects a subject, complete permission ID, and scope. Assuming ``alice`` has ``course_editor`` in ``course-v1:OpenedX+DemoX+DemoCourse``, the extension above produces these results:
340+
341+
.. code-block:: text
342+
343+
alice courses.export_course course-v1:OpenedX+DemoX+DemoCourse
344+
✓ ALLOWED: alice courses.export_course course-v1:OpenedX+DemoX+DemoCourse
345+
346+
alice courses.manage_tags course-v1:OpenedX+DemoX+DemoCourse
347+
✗ DENIED: alice courses.manage_tags course-v1:OpenedX+DemoX+DemoCourse

docs/references/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ References
33

44
.. toctree::
55
:maxdepth: 1
6+
7+
authorization-schema

0 commit comments

Comments
 (0)