Technical Approach: Roles and Permissions
This document is an early proposal. Comments and suggestions are welcome, and the eventual direction we land on may be radically different.
Context
This doc outlines the technical approach for implementing PRD Roles & Permissions.
OEP-66 gives an excellent overview of what currently exists in our platform, and how we would integrate a custom scopes and permissions storage backend with Django’s authorization. 2U implemented much of the design outlined in the OEP in the CourseRoles branch of edx-platform, but the project was abandoned before it was merged. The next round of roles and permissions discovery work that eventually led to PRD Roles & Permissions happened after OEP-66 was written.
The major differences between OEP-66 and this latest work are:
The new design’s data model for resources will need to be more generic than the data model proposed in OEP-66, so that we can better represent things that are not courses or organizations. Scopes for grouping these resources will also be more complex, e.g. “this group of five courses”.
The new product direction currently has no provision for services outside of Studio/LMS, or the mapping of system-level roles to service level roles. This is currently a gap that we need to better explore, though it might not make it in to the MVP.
Strategy
Roles and Permissions modeling is generic functionality, meaning that we should favor adopting an off-the-shelf solution as much as possible. Unfortunately, I haven’t been able to find anything that would meet our requirements, so we’re going to have to roll our own.
Our ideal end goal would be a generic Django library with its own community that can be spun out of our project and maintained by others. This has implications for how we build:
Repository: Standalone repo in the openedx GitHub organization.
License: Apache
Keep Open edX specific logic and roles out of the base library.
We can have a separate top level package with Open edX specific logic in it if it eases early development, as long as it is easily removable and the dependency is strictly one-way–i.e. the base library doesn’t have any references to it.
We should bias towards simplicity of implementation wherever possible, e.g. avoid things like role inheritance and potentially other advanced features that might overly complicate the code. This is going to be a fuzzy line because we’re building this explicitly because nothing out there supports all the requirements, in particular things around Scopes, Custom Roles, and plugin extensibility.
MVP Candidate: Content Libraries
The new Content Libraries application would make a good candidate for an MVP of this framework:
It’s relatively low-risk, since this is not yet a widely adopted feature, having only been added as a beta in Sumac.
The use cases are in many cases a subset of what we would see for Courses, e.g. view, publish, create, etc.
At the same time, there are some interesting edge cases that could help test out the model, like how library authors and creators might be granted read access to other libraries in their organization by default.
The Complexity of Modeling Scopes
Evaluation of Existing Frameworks
This is a brief summary of libraries and frameworks examined. I omitted some of the ones that were clearly discontinued (like django-rbac). I very recently ran into a new (two month-old) library named Django-Ultra-RBAC which seems to have some advanced features that may be relevant. I haven’t had the time to assess it yet.
Framework | Summary |
---|---|
Django’s built-in permission framework has no concept of parameterized permissions, meaning you can’t say, “This group/role can edit this particular library.” You can only specify that they can edit libraries as a whole. | |
Guardian tries to act as a natural extension of Django permissions functionality by bringing per-model-instance permissions. This still maps poorly to more complex permissions that work across many models (e.g. “publish”). Modeling Scopes as queryable things would also be challenging here, e.g. “All courses in this organization except this list.” Guardian wants to make explicit grants to one resource at time. | |
This is the closest thing I’ve found to what we need, and introduces arbitrary parameters. It also introduces permissions (which it calls “privileges”) that are not tied to specific models. What it doesn’t have are scopes, custom roles, or generally the ability to treat permissions as data that can be queried on. (Though some kinds of scopes could be emulated by using parameter lists, I think this would present performance issues down the road.) The last release was in December of 2023. | |
Bridgekeeper is an unopinionated permission querying mechanism, rather than a roles/permissions model. So while we’ll probably want to use it to query our roles and permissions (especially during any transitional phase), it’s not useful as a backing store. | |
Much of edx-rbac is focused on propagating roles across services. We might still use this (or part of this) for that purpose, but the data models aren’t sufficient for modeling things like custom roles and scopes. |