Skip to content

Commit 41fd708

Browse files
docs: ADR proposing authorization foundation for openedx ecosystem
Make decisions about: 1. Permission model to use to adopt an universal language 2. Make scope first class citizens 3. Paradigm to use 4. Policy usage 5. Centralize enforcement 6. Integrations 7. Extensibility 8. Security The goal is to build a system that follows these decisions to successfully address our current restrictions.
1 parent b4537a0 commit 41fd708

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
0002: Authorization (AuthZ) Model Foundations
2+
#############################################
3+
4+
Status
5+
******
6+
**Draft**
7+
8+
Context
9+
*******
10+
Open edX needs a single way to decide: who can do what, on which resource, and under which conditions. Today, permissions are checked in many different ways. Some systems are feature-specific (``student_courseaccessrole``, ``django_comment_client_role``, ``contentlibrarypermission``). Others use global roles passed in JWTs. Many checks are written directly in code (``if user.is_superuser``). This makes the system hard to extend, hard to change, and not easy to audit.
11+
12+
We want an authorization model that is:
13+
14+
* Clear and consistent vocabulary everywhere.
15+
* Explicitly supports industry standards and is built on battle-tested technologies.
16+
* Flexible but still simple to maintain.
17+
* Able to explain every decision (the system should be transparent on why access was granted or not).
18+
* Unified and centralized enforcement rather than ad-hoc implementations for immediate needs.
19+
* Able to support query-based access patterns out of the box.
20+
* Focused on connecting stakeholders and making policies clear and accessible to everyone involved.
21+
22+
.. note::
23+
24+
Authorization is considered independent from authentication. There will be an interface between them so we can combine correctness and consistency. A separate ADR will cover the details of this interface (e.g., how roles in JWTs are handled and how checks are made).
25+
26+
Decision
27+
********
28+
29+
I. Canonical Permission Model
30+
=============================
31+
32+
Normalize all checks to Subject-Action-Object-Context (S-A-O-C)
33+
----------------------------------------------------------------
34+
* We express authorization as: is **Subject** allowed to do **Action** on **Object** under **Context**?
35+
* This normalization is used in policies, code, queries, and audits.
36+
* Examples:
37+
38+
- Can Alice (subject) edit (action) Course 123 (object) as part of Org A (context)?
39+
- Can a service account (subject) read (action) Library X (object) during maintenance mode (context)?
40+
41+
II. Resources and Scopes
42+
========================
43+
44+
Scopes as first-class citizens in permission-granting.
45+
------------------------------------------------------
46+
* A **scope** defines the boundary within which a role or policy applies (for example: platform-wide, organization-wide, a single course, or a specific library).
47+
* Treating scopes as **first-class citizens** means they are explicitly modeled in the system, not hidden inside ad-hoc resource definitions. They must be available to policies, queries, and audits in a consistent way.
48+
* Scopes can be **parameterized** (e.g., ``organization:ORG-A``, ``course:CS101``, ``site:sandbox.openedx.org``, ``instance``) to support granular checks.
49+
* **Inheritance across scopes** must be supported (e.g., permissions granted at the organization level can cascade to courses in that organization when intended).
50+
* By making scopes explicit and consistent, we avoid the fragmentation seen in legacy systems (different services using different implicit notions of "site", "org", "course").
51+
52+
III. Authorization Paradigm
53+
===========================
54+
55+
Adopt ABAC as the goal; Scoped RBAC as a first step
56+
---------------------------------------------------
57+
* We recommend **ABAC** as the main model for Open edX authorization.
58+
* **Scoped RBAC** may be used pragmatically as a first step, with the ambition of moving into a more granular system with ABAC.
59+
* **RBAC** handles role-based permissions well (e.g., "admins can edit any record").
60+
* **ABAC** adds finer control by using attributes of subjects, resources, and context (e.g., "editors can edit only in their assigned organizations or locations").
61+
* **ReBAC** is not chosen because it adds complexity and we do not have strong use cases today.
62+
63+
- Although ReBAC solves interesting problems out of the box (inheritance, recursive relationships), it introduces a mental shift in how to think about authorization.
64+
- Some technologies are ReBAC-first but can also implement RBAC and ABAC effectively. These are not excluded, but they shouldn't go against our **simplicity principle**.
65+
66+
* **Simplicity principle**: avoid adding features like deep role inheritance or complex hierarchies until there are clear use cases that require them.
67+
68+
IV. Policy Definition
69+
=====================
70+
71+
Externalize policies
72+
--------------------
73+
* Policies must be defined outside code, not hardcoded with conditionals.
74+
75+
- Prefer declarative policy definitions (e.g., JSON, YAML, policy language) over in-code checks like ``if user.is_superuser``.
76+
- Prefer explicit permission checks over implicit role lookups in business logic.
77+
78+
* Policies must explicitly show whether access comes from:
79+
80+
- **Default roles** (out-of-the-box), or
81+
- **Extensions** (plugin-based).
82+
83+
* Policies must be versioned, reviewable, and easy to share.
84+
* If policies are not easy to read, provide an abstracted or friendly view.
85+
* Show the **effect** of policies when available (allow/deny).
86+
87+
V. Enforcement
88+
==============
89+
90+
Use centralized enforcement
91+
---------------------------
92+
* Authorization checks must go through a single path, not spread across ad-hoc implementations.
93+
* Centralized enforcement can take two possible forms:
94+
95+
- A **central service** that acts as the decision point for all checks.
96+
- A **shared adapter/library** that is the only way services can ask for permissions.
97+
98+
* In both cases, services must not embed authorization logic directly.
99+
100+
VI. Engines and Integration
101+
============================
102+
103+
Use proven frameworks with ABAC support and an adapter
104+
------------------------------------------------------
105+
* Use existing open source frameworks (Casbin, Cerbos, OpenFGA, SpiceDB).
106+
* Do not build a custom engine.
107+
* The chosen technology must:
108+
109+
- Support **ABAC** to allow growth beyond role-only systems.
110+
- Provide **explicit and clear permission checks** in code, similar in clarity to Django's ``user.has_perm``.
111+
- Avoid introducing obscure or confusing query styles.
112+
113+
* Provide an **adapter layer** that:
114+
115+
- Translates Open edX concepts into the engine model.
116+
- Keeps Open edX services engine-agnostic.
117+
- Ensures consistent logging and decision tracing.
118+
119+
VII. Extensibility
120+
===================
121+
122+
Make roles, permissions, and models pluggable
123+
---------------------------------------------
124+
* Extensibility should include:
125+
126+
- Adding **custom roles** that can be composed from or unioned with existing permissions.
127+
- Adding **new permissions (verbs)** that build on top of existing ones.
128+
- Defining **new models/resources** (e.g., "workspace", "assignment") and expressing their relations to existing ones (e.g., organization → course → unit).
129+
130+
* Applications must keep calling the same consistent check (e.g., *can(subject, action, object)*), while the schema or policy evolves underneath.
131+
132+
Examples of extensibility patterns (conceptual)
133+
-----------------------------------------------
134+
* **Custom role**: Extend an existing permission by unioning a new relation. For instance, add a `custom_reader` role to courses and union it with `view`: ``permission view = reader + custom_reader + parent->view`` Applications still check `can(user, "view", course)`. The system resolves the new role transparently.
135+
136+
* **Augment a role**: Introduce a new relation (e.g., `custom_editor`) and extend `edit` so that both `editor` and `custom_editor` grant edit rights: ``permission edit = editor + custom_editor``
137+
138+
* **New permission**: Define a new verb (e.g., `review`) that piggybacks on existing ones: ``permission review = view + edit``
139+
140+
* **New resource type**: Introduce a new definition (e.g., `workspace`) and declare how its permissions inherit from or compose with existing resources.
141+
142+
Extensibility principles
143+
------------------------
144+
* **Declarative, not in code**: Extending behavior should be done in policy/schema files, not by editing application logic.
145+
* **Stable API**: Applications continue using the same verbs while extensions happen under the hood.
146+
* **Consistency**: Extensions must still use the canonical vocabulary (Subject-Action-Object-Context).
147+
148+
VIII. Auditability
149+
=================
150+
151+
Make all decisions explainable
152+
------------------------------
153+
* Every decision must have a trace:
154+
155+
- Which policy was used.
156+
- Which attributes were checked.
157+
- The effect (allow/deny).
158+
159+
* Logs must let admins ask: "Why was this action allowed or denied?"
160+
* Traces must capture runtime values so audits remain possible later.
161+
* Permission checks in code must be **explicit and self-documenting**, so developers and stakeholders can easily understand how authorization is asked for in the system.
162+
163+
IX. Security
164+
============
165+
166+
Protect policies and logs against tampering
167+
--------------------------------------------
168+
169+
* The system must guarantee the integrity of authorization policies and decision logs.
170+
* Policies and logs should be stored or managed in a way that makes tampering detectable.
171+
172+
Consequences
173+
************
174+
1. **Strong audit needs.** We must build a central log of all decisions, including attributes and matched policies.
175+
2. **Attribute management.** ABAC requires attributes to be available and normalized. We must also capture their values in logs.
176+
3. **Scoped RBAC transition.** Some parts may use RBAC first, but the chosen system must support full ABAC.
177+
4. **Readable policies.** Even if technical, policies must be presented in a way non-technical people can review.
178+
5. **Scope consistency.** The system must provide a consistent definition and handling of scopes and resource hierarchies across all services, so that policies and checks have the same meaning everywhere.
179+
6. **Operational costs.** Centralized enforcement and logging require infra and processes (storage, retention, review).
180+
7. **Performance impact.** Logging and attributes add overhead. We must design caching and retention strategies.
181+
8. **Migration work.** Old in-code checks must be replaced step by step with policies.
182+
9. **Querying system.** The authorization model must support query-style checks (e.g., "list all objects this user can edit") at least as well as the current bridgekeeper system, either by integration or by providing equivalent functionality.
183+
184+
Rejected Alternatives
185+
*********************
186+
* **RBAC-only**: too limited for contextual decisions.
187+
* **ReBAC**: rejected because it adds complexity and we lack strong use cases today.
188+
- While ReBAC solves inheritance and recursive relationships well, it introduces complexity
189+
and a different way of thinking about authorization.
190+
* **In-code checks**: not auditable or shareable.
191+
* **Custom-built engine**: unnecessary when proven frameworks exist.
192+
193+
References
194+
**********
195+
WIP
196+
197+
Glossary
198+
********
199+
* **Policy**: A declarative rule that defines which subjects can perform which actions on which objects under which context. Policies are stored outside of code, versioned, and auditable.
200+
201+
* **RBAC (Role-Based Access Control)**: Authorization model where access is granted based on roles assigned to users.
202+
203+
* **Scoped RBAC**: A variant of RBAC where roles apply within a specific scope (e.g., organization, course, library).
204+
205+
* **ABAC (Attribute-Based Access Control)**: Authorization model where access is granted based on attributes of the subject, object, and context (e.g., user's organization, resource type, time of day).
206+
207+
* **PBAC (Policy-Based Access Control)**: An approach where policies (rules) are stored outside code and evaluated by a policy engine. Policies can implement RBAC, ABAC, or other models.
208+
209+
* **ReBAC (Relationship-Based Access Control)**: Authorization model where access decisions are based on explicit relationships between subjects and objects, often modeled as a graph.
210+
211+
* **S-A-O-C (Subject-Action-Object-Context)**: The canonical shape of any authorization check: *is Subject allowed to perform Action on Object under Context?*
212+
213+
* **Authorization check**: The explicit way a service asks whether an operation is allowed, always expressed in S-A-O-C form.
214+
215+
* **Query check**: A pattern where the system returns all objects of type X on which a subject can perform a given action, under a given context.

0 commit comments

Comments
 (0)