|
| 1 | +0012: Glob Support For Role Assignments |
| 2 | +####################################### |
| 3 | + |
| 4 | +Status |
| 5 | +****** |
| 6 | + |
| 7 | +**Draft** - *2026-03-18* |
| 8 | + |
| 9 | +Context |
| 10 | +******* |
| 11 | + |
| 12 | +The current authorization system is based on Casbin and models: |
| 13 | + |
| 14 | +- **Permissions per role** (``p`` policies), where the ``scope`` field may already include the existing namespace wildcard ``^*`` (for example, ``lib^*`` meaning "any scope in the ``lib`` namespace"). |
| 15 | +- **Role assignments** (``g`` policies), which link a subject to a role within a scope. |
| 16 | + |
| 17 | +The current Casbin model treats the ``scope`` field in ``g`` policies as an **exact match**. This is sufficient when roles are granted for a single, concrete scope value, but it is limiting when operators need to: |
| 18 | + |
| 19 | +- Assign roles that are valid for a **set of resources** that share a common prefix (for example, all courses or all content libraries belonging to a given organization). |
| 20 | +- Avoid enumerating a large or evolving set of resources one by one, which increases operational overhead and risk of drift. |
| 21 | + |
| 22 | +This ADR proposes enabling glob-like matching for role assignments, so that a single ``g`` policy can represent a multi-scope assignment such as: |
| 23 | + |
| 24 | +.. code:: text |
| 25 | +
|
| 26 | + g, user^contributor, role^course_staff, course-v1^course-v1:OpenedX+* |
| 27 | +
|
| 28 | +In that example, checking a permission such as: |
| 29 | + |
| 30 | +.. code:: text |
| 31 | +
|
| 32 | + authz_api.is_user_allowed("contributor", "courses.manage_advanced_settings", "course-v1:OpenedX+Some+Course") |
| 33 | +
|
| 34 | +should be allowed if the user's role assignment matches the ``course-v1:OpenedX+*`` pattern. |
| 35 | + |
| 36 | +At the same time, we must preserve the guarantees of the authorization model: |
| 37 | + |
| 38 | +- **Safety**: Glob patterns must not accidentally grant permissions outside the intended identifier boundary. |
| 39 | +- **Clarity**: Patterns must be easy to understand and reason about for operators and auditors. |
| 40 | +- **Extensibility**: The mechanism should be general enough to support future use cases without requiring a redesign of the model. |
| 41 | + |
| 42 | +Decision |
| 43 | +******** |
| 44 | + |
| 45 | +We will introduce support for glob-like matching on the ``scope`` field of **role assignments** (``g`` policies), combined with explicit validation in the public APIs that manage those assignments. |
| 46 | + |
| 47 | +The decision is intentionally **general**: the core change is to allow glob matching for scopes in ``g`` policies, and to guard its usage with well-defined, namespace-specific validation rules. This creates a foundation that can be extended later without changing the Casbin model again. |
| 48 | + |
| 49 | +1. Enable glob matching on ``g`` scopes in the enforcer |
| 50 | +======================================================= |
| 51 | + |
| 52 | +We will configure the ``AuthzEnforcer`` to use a domain/scope matching function for ``g`` policies that supports glob-like suffixes. Concretely: |
| 53 | + |
| 54 | +- The enforcer will register a domain matching function for the ``g`` (grouping) function (for example, using ``key_match_func``). |
| 55 | +- This matching function will treat ``*`` as a wildcard at the **end** of the string (suffix wildcard). That is, patterns such as ``course-v1:OpenedX+*`` will match ``course-v1:OpenedX+SOME+COURSE``, but the model will not rely on complex patterns or regular expressions. |
| 56 | +- Matching is **case-sensitive**. Scope comparisons follow exact string semantics for non-wildcard characters (for example, ``course-v1:openedx+*`` does not match ``course-v1:OpenedX+...``). |
| 57 | +- Existing ``g`` policies that use exact scopes remain valid and continue to behave identically. They follow the same validation path as before. Only scopes containing a ``*`` suffix glob take a different API-side validation path. |
| 58 | + |
| 59 | +This change allows the Casbin engine to evaluate role assignments that apply to a family of scopes instead of a single exact value, without modifying the underlying storage schema (``CasbinRule``) or the overall request format (``r = sub, act, scope``). |
| 60 | + |
| 61 | +2. Validate glob scopes at the API boundary |
| 62 | +=========================================== |
| 63 | + |
| 64 | +All APIs that create, update, or delete role assignments (i.e., policies of type ``g``) must validate any scope that includes a glob pattern. The goals of validation are: |
| 65 | + |
| 66 | +- **Constrain** what forms of glob are permitted for each namespace. |
| 67 | +- **Reject** malformed or overly broad patterns that would be difficult to reason about or audit. |
| 68 | + |
| 69 | +The following rules apply initially: |
| 70 | + |
| 71 | +- The glob character (``*``) is only supported as a **suffix wildcard**. It cannot appear in the middle of a scope identifier. |
| 72 | +- A glob pattern represents a **boundary-constrained prefix match** for the external key portion of a scope within its namespace. In practice, this means matching is limited to a well-defined identifier boundary, and the glob cannot be used to create overly broad or unsafe matches. |
| 73 | +- For any glob patterns (courses, libraries, or future namespaces), malformed inputs (such as mid-string wildcards or prefixes that do not match expected key formats or identifier boundaries) are **rejected**. |
| 74 | +- Additional namespaces must define their own, explicit validation rules before accepting glob scopes. If a namespace does not have a custom matcher/validator, any ``*`` in its scope is rejected and only exact scopes are accepted. |
| 75 | +- As needs evolve, more glob pattern types can be added safely by introducing namespace-specific semantics and validations (for example, additional prefix boundaries such as program/tenant prefixes, or narrower matching strategies if required). |
| 76 | + |
| 77 | +Examples of valid/invalid namespace scope globs |
| 78 | +----------------------------------------------- |
| 79 | + |
| 80 | +Given the current suffix-glob policy (single trailing ``*`` at a namespace-approved boundary): |
| 81 | + |
| 82 | +- Invalid: ``*`` (unbounded across all namespaces/scopes). |
| 83 | +- Invalid: ``c*`` or ``l*`` (not a valid namespaced scope prefix). |
| 84 | +- Invalid: ``course-v1*`` or ``lib*`` (missing ``:`` and required namespace structure). |
| 85 | +- Invalid: ``course-v1:*`` or ``lib:*`` (wildcard starts before a valid prefix boundary inside the ``course-v1`` or ``lib`` key). |
| 86 | +- Invalid: ``course-v1:O*`` or ``lib:MIT*`` (wildcard starts mid-segment. Prefix boundary is not complete). |
| 87 | +- Valid: ``course-v1:OpenedX+*`` (wildcard starts at an approved boundary for the ``course-v1`` namespace). |
| 88 | +- Valid: ``lib:MITx:*`` (wildcard starts at an approved boundary for the ``lib`` namespace). |
| 89 | + |
| 90 | +These validation rules are implemented in the Open edX layer (API / data layer), not in the Casbin matcher itself. The enforcer remains general-purpose. The domain-specific semantics of what constitutes an acceptable glob pattern are enforced at the boundary where user/operator input is turned into policies. |
| 91 | + |
| 92 | +3. Keep the model general and extensible |
| 93 | +======================================== |
| 94 | + |
| 95 | +By introducing glob support in role assignments in a boundary-constrained way, we unlock a set of future extensions without redesigning the model: |
| 96 | + |
| 97 | +- **Other scope types** |
| 98 | + |
| 99 | + - Scope types with hierarchical or prefix-based identifiers (for example, libraries or other content groupings) can adopt glob pattern support by: |
| 100 | + |
| 101 | + - Defining their own namespace-specific rules for valid suffix globs. |
| 102 | + - Reusing the same enforcer-level domain matching capability. |
| 103 | + |
| 104 | +- **Future matching strategies** |
| 105 | + |
| 106 | + - If, in the future, there is a strong need for more expressive matching (for example, segment-based matching or multiple wildcards), these can be introduced as **new, explicitly-scoped features** with their own validation rules and migration story. |
| 107 | + - For now, we deliberately keep glob support simple and limited (single trailing ``*``) to minimize complexity and security risk. |
| 108 | + |
| 109 | +Consequences |
| 110 | +************ |
| 111 | + |
| 112 | +Positive consequences |
| 113 | +===================== |
| 114 | + |
| 115 | +- **Increased expressiveness**: Operators can express multi-scope role assignments (for example, "course staff for all courses in organization OpenedX") without enumerating each course in individual ``g`` policies. |
| 116 | +- **Reduced operational overhead**: New resources that fall under an existing glob pattern automatically inherit the appropriate role assignments, reducing the need for ongoing manual updates. |
| 117 | +- **Better alignment with real-world use cases**: Many organizational setups naturally require "all resources under this prefix" semantics. Glob pattern support maps directly to those needs. |
| 118 | +- **Clear extension path**: The mechanism is generic enough to be reused for other namespaces (such as organization or library scopes), as long as each namespace defines and enforces its own validation rules. |
| 119 | + |
| 120 | +Negative consequences / risks |
| 121 | +============================= |
| 122 | + |
| 123 | +- **Security and safety**: If validation is misconfigured or bypassed, glob patterns could unintentionally grant access beyond the intended identifier boundary. This risk is mitigated by: |
| 124 | + |
| 125 | + - Enforcing validation in the Open edX API layer. |
| 126 | + - Restricting globs to trailing ``*`` patterns. |
| 127 | + - Defining precise, namespace-specific rules. |
| 128 | + |
| 129 | +- **Complexity in mental model**: Operators and developers must understand that some role assignments apply to families of scopes instead of a single scope. This can be addressed by: |
| 130 | + |
| 131 | + - Providing clear documentation and examples for glob-based assignments. |
| 132 | + - Exposing introspection tooling that explains which policies matched a given decision. |
| 133 | + |
| 134 | +- **Performance considerations**: Glob matching adds some overhead to Casbin evaluations. However: |
| 135 | + |
| 136 | + - The cost of simple suffix matching is low. |
| 137 | + - The policy store still uses the same schema and indexing strategy. |
| 138 | + - The feature should be used primarily for coarse-grained groupings (e.g., by organization), not for highly fragmented patterns. |
| 139 | + |
| 140 | +Rejected Alternatives |
| 141 | +********************** |
| 142 | + |
| 143 | +- **Keep exact matching only for 'g' scopes** |
| 144 | + |
| 145 | + - Pros: |
| 146 | + |
| 147 | + - Simpler to reason about. |
| 148 | + - No changes to matcher configuration. |
| 149 | + - Cons: |
| 150 | + |
| 151 | + - Does not scale for environments with many resources per organization. |
| 152 | + - Forces operators to maintain large numbers of nearly-identical assignments. |
| 153 | + |
| 154 | +- **Introduce full regular-expression support on scopes** |
| 155 | + |
| 156 | + - Pros: |
| 157 | + |
| 158 | + - Maximum flexibility for expressing patterns. |
| 159 | + - Cons: |
| 160 | + |
| 161 | + - Harder to reason about and audit. |
| 162 | + - Higher risk of misconfiguration and security overshoot. |
| 163 | + - Potentially worse performance. |
| 164 | + |
| 165 | +References |
| 166 | +********** |
| 167 | + |
| 168 | +- `Casbin function documentation (matching functions) <https://casbin.org/docs/function/>`_ |
0 commit comments