Skip to content

Commit ec2e182

Browse files
[FC-0099] docs: ADR proposing authorization foundation for openedx ecosystem (openedx#31)
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 f345ada commit ec2e182

1 file changed

Lines changed: 201 additions & 0 deletions

File tree

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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:
13+
14+
* Uses a clear and consistent vocabulary throughout.
15+
* Explicitly supports industry standards and is built on battle-tested technologies.
16+
* Is flexible but still simple to maintain.
17+
* Can explain every decision (the system should be transparent on why access was granted or not).
18+
* Enforces decisions in a unified and centralized way, rather than ad-hoc implementations for immediate needs.
19+
* Supports query-based access patterns out of the box.
20+
* Focuses 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) the course ``course-v1:OpenedX+DemoX+DemoCourse`` (object) as part of Org A (context)?
39+
- Can Bob (subject) read (action) the library ``lib:DemoX:CSPROB`` (object)?
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:course-v1:OpenedX+DemoX+DemoCourse``, ``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 "instance", "org", "course").
51+
* Scope is part of the **Context** in S-A-O-C checks.
52+
53+
III. Authorization Paradigm
54+
===========================
55+
56+
Adopt ABAC as the goal; Scoped RBAC as a first step
57+
---------------------------------------------------
58+
* We recommend **ABAC** as the main model for Open edX authorization.
59+
* **Scoped RBAC** may be used pragmatically as a first step, with the ambition of moving into a more granular system with ABAC.
60+
* **RBAC** handles role-based permissions well (e.g., "admins can edit any record").
61+
* **ABAC** adds finer control by using attributes of subjects, resources, and context (e.g., "editors can edit only in their assigned organizations or locations").
62+
* **ReBAC** is not chosen because it adds complexity and we do not have strong use cases today.
63+
64+
- Although ReBAC solves interesting problems out of the box (inheritance, recursive relationships), it introduces a mental shift in how to think about authorization so we're not explicitly adopting it for now.
65+
- 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**.
66+
67+
* **Simplicity principle**: avoid adding features like deep role inheritance or complex hierarchies until there are clear use cases that require them.
68+
69+
IV. Policy Definition
70+
=====================
71+
72+
Externalize policies
73+
--------------------
74+
* Policies must be defined and managed externally (e.g., in policy files or a database store), not embedded directly in application logic. The default model is an allowlist: actions are permitted only when explicitly granted.
75+
76+
- Prefer declarative policy definitions (e.g., JSON, YAML, policy language) over in-code checks like ``if user.is_superuser``.
77+
- Prefer explicit permission checks over implicit role lookups in business logic.
78+
79+
* Policies must explicitly show whether access comes from:
80+
81+
- **Default roles** (out-of-the-box), or
82+
- **Extensions** (plugin-based).
83+
84+
* Policies must be versioned, reviewable, and easy to share.
85+
* If policies are not easy to read, provide an abstracted or friendly view.
86+
* Show the **effect** of policies when available (allow/deny).
87+
88+
V. Enforcement
89+
==============
90+
91+
Use centralized enforcement
92+
---------------------------
93+
* Authorization checks must go through a single path, not spread across ad-hoc implementations.
94+
* Centralized enforcement can take two possible forms:
95+
96+
- A **central service** that acts as the decision point for all checks.
97+
- A **shared adapter/library** that is the only way services can ask for permissions.
98+
99+
* In both cases, services must not embed authorization logic directly.
100+
101+
VI. Engines and Integration
102+
===========================
103+
104+
Use proven frameworks with ABAC support and an adapter
105+
------------------------------------------------------
106+
* Use existing open source frameworks (`Casbin <https://casbin.org>`_, `Cerbos <https://www.cerbos.dev>`_, `OpenFGA <https://authzed.com/spicedb>`_, `SpiceDB <https://spicedb.dev>`_, `Ory Keto <https://www.ory.sh/keto>`_, etc.).
107+
* Recommend against building a custom engine since authorization is a well-established domain with many existing solutions, reinventing the wheel introduces unnecessary complexity and maintenance burden.
108+
* The chosen technology must:
109+
110+
- Support **ABAC** to allow growth beyond role-only systems.
111+
- Provide **explicit and clear permission checks** in code, similar in clarity to Django's ``user.has_perm``.
112+
- Avoid introducing obscure or confusing query styles.
113+
114+
* Provide an **adapter layer** that:
115+
116+
- Translates Open edX concepts into the engine model.
117+
- Keeps Open edX services engine-agnostic.
118+
- Ensures consistent logging and decision tracing.
119+
120+
VII. Extensibility
121+
==================
122+
123+
Make roles, permissions, and resources pluggable
124+
------------------------------------------------
125+
* Extensibility should include:
126+
127+
- Adding **custom roles** that can be composed from or unioned with existing permissions.
128+
- Adding **new permissions (verbs)** that build on top of existing ones.
129+
- Defining **new resources** (e.g., "assignment") and expressing their relations to existing ones (e.g., platform → organization → course).
130+
131+
* Applications must keep calling the same consistent check (e.g., *can(subject, action, object)*), while the schema or policy evolves underneath.
132+
133+
VIII. Auditability
134+
==================
135+
136+
Make all decisions explainable
137+
------------------------------
138+
* Every decision must have a trace:
139+
140+
- Which policy was used.
141+
- Which attributes were checked.
142+
- The effect (allow/deny).
143+
144+
* Logs must let admins ask: "Why was this action allowed or denied?"
145+
* Traces must capture runtime values so audits remain possible later.
146+
* 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.
147+
148+
IX. Security
149+
============
150+
151+
Protect policies and logs against tampering
152+
--------------------------------------------
153+
154+
* The system must guarantee the integrity of authorization policies and decision logs.
155+
* Policies and logs should be stored or managed in a way that makes tampering detectable.
156+
157+
Consequences
158+
************
159+
1. **Strong audit needs.** We must build a central log of all decisions, including attributes and matched policies.
160+
2. **Attribute management.** ABAC requires attributes to be available and normalized. We must also capture their values in logs.
161+
3. **Scoped RBAC transition.** Some parts may use RBAC first, but the chosen system must support full ABAC.
162+
4. **Readable policies.** Even if technical, policies must be presented in a way non-technical people can review.
163+
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.
164+
6. **Performance impact.** Logging and attributes add overhead. We must design caching and retention strategies.
165+
7. **Migration work.** Old in-code checks must be replaced step by step with policies.
166+
8. **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.
167+
168+
Rejected Alternatives
169+
*********************
170+
* **RBAC-only**: too limited for contextual decisions.
171+
* **ReBAC**: rejected because it adds complexity and we lack strong use cases today.
172+
- While ReBAC solves inheritance and recursive relationships well, it introduces complexity and a different way of thinking about authorization.
173+
* **In-code checks**: not auditable or shareable.
174+
* **Custom-built engine**: unnecessary when proven frameworks exist.
175+
176+
References
177+
**********
178+
- `AuthZ Key Concepts <https://openedx.atlassian.net/wiki/spaces/OEPM/pages/5177999395>`_
179+
- `AuthZ Architecture Approach <https://openedx.atlassian.net/wiki/spaces/OEPM/pages/5176229910>`_
180+
- `PRD Roles and Permissions <https://openedx.atlassian.net/wiki/spaces/OEPM/pages/4724490259>`_
181+
182+
Glossary
183+
********
184+
185+
* **Action**: The operation attempted on a resource (e.g., view, edit, delete).
186+
* **Attribute**: Property of a user or resource used in ABAC (e.g., user.profile.department == course.org).
187+
* **Authorization check**: The explicit way a service asks whether an operation is allowed, always expressed in S-A-O-C form.
188+
* **Authorization models**: Frameworks or approaches that define how to express who can do what, on which resource, and under which conditions. Common models include RBAC, ABAC, and ReBAC.
189+
190+
* **RBAC (Role-Based Access Control)**: Authorization model where access is granted based on roles assigned to users.
191+
* **Scoped RBAC**: A variant of RBAC where roles apply within a specific scope (e.g., organization, course, library).
192+
* **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).
193+
* **ReBAC (Relationship-Based Access Control)**: Authorization model where access decisions are based on explicit relationships between subjects and objects, often modeled as a graph.
194+
195+
* **Permission**: Atomic unit of access (e.g., ``CREATE_COURSE``, ``EDIT_ROLE``).
196+
* **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.
197+
* **Relationship**: Link between entities granting access in ReBAC (e.g., user:alice#editor@course:math101).
198+
* **Resource**: The object being accessed (e.g., Course).
199+
* **Role**: A collection of permissions assigned to a user (e.g., Instructor).
200+
* **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?*
201+
* **Scope**: The boundary where a role applies (e.g., Instructor in Course A, Admin in Org B).

0 commit comments

Comments
 (0)