33#
44# This model supports:
55# - Scoped role assignments (user roles tied to specific contexts)
6- # - Action grouping (manage → read/write/delete to reduce duplication)
6+ # - Action grouping (manage → read/write/edit/ delete to reduce duplication)
77# - System-wide roles (global scope "*" applies everywhere)
8- # - Scope-based permissions (authorization based on context, not specific objects)
98# - Negative rules (deny overrides allow for exceptions)
10- # - Namespace support (course-v1:*, lib:*, org:*, etc.)
11- # - Extensibility (new resource types only require new scope namespaces)
12- #
13- # DESIGN PRINCIPLE:
14- # - Authorization is scope-based, not object-based
15- # - Each request is explicitly scoped (course, org, global, etc.)
16- # - Permissions are granted within scopes, eliminating need for object matching
17- # - Containment relationships are handled by the application layer
18- #
19- # NOT handled here (deferred to application):
20- # - Resource grouping/containment (app resolves parent-child relationships)
21- # - Role inheritance across scopes (app checks multiple scopes explicitly)
22- # - Object lifecycle consistency (app handles cleanup on delete)
9+ # - Namespace support (course:*, lib:*, org:*, etc.)
10+ # - Extensibility (new resource types just need new namespaces)
2311############################################
2412
2513[request_definition]
26- # Request format: subject, action, scope
14+ # Request format: subject (user) , action, scope (specific resource being accessed)
2715#
2816# sub = subject/principal with namespace (e.g., "user:alice", "service:lms")
2917# act = action with namespace (e.g., "act:read", "act:manage", "act:edit-courses")
3018# scope = authorization scope context (e.g., "org:OpenedX", "course-v1:...", "*" for global)
3119#
3220# SCOPE SEMANTICS:
33- # - Scope determines the authorization context and which role assignments apply
34- # - "*" = global scope (system-wide roles apply everywhere)
35- # - "org:OpenedX" = organization-scoped roles (apply within OpenedX org)
21+ # Scope determines the authorization context and which role assignments apply
22+ # - "*" = global scope (system-wide roles apply everywhere)
23+ # - "org:OpenedX" = organization-scoped roles (apply within OpenedX org)
3624# - "course-v1:..." = course-scoped roles (apply within specific course)
3725#
3826# Application must provide appropriate scope based on business logic.
3927r = sub, act, scope
4028
4129[policy_definition]
42- # Policy format: subject, action, scope, effect
30+ # Policy format: subject (role) , action, scope (pattern) , effect
4331#
4432# sub = role or user with namespace (e.g., "role:org_admin", "user:bob")
45- # act = action identifier (e.g., "act:manage", "act:read", "act:edit-courses"). Uses g2 relation for action grouping.
46- # scope = scope where policy applies (e.g., "*", "org:OpenedX ", "course-v1:... ")
33+ # act = action identifier (e.g., "act:manage", "act:read", "act:edit-courses")
34+ # scope = scope where policy applies (e.g., "*", "org:* ", "course-v1:*", "lib:* ")
4735# eft = "allow" or "deny" (deny overrides allow for exceptions)
4836p = sub, act, scope, eft
4937
5038[role_definition]
51- # g: Role assignments (without scope)
52- # Format: user/subject, role
53- #
54- # This is a simplified role assignment where users are assigned roles globally,
55- # without being tied to specific scopes. All role assignments apply system-wide.
39+ # g: Role assignments with scope
40+ # Format: user/subject, role, scope
5641#
5742# Examples:
58- # g, user:alice, role:org_admin # Alice is org admin
59- # g, user:bob, role:course_instructor # Bob is course instructor
60- # g, user:carol, role:platform_admin # Carol is platform admin
61- # g, service:lms, role:system_service # LMS service has system-wide access
43+ # g, user:alice, role:org_admin, org:OpenedX # Alice is org admin for OpenedX
44+ # g, user:bob, role:course_instructor, course-v1:... # Bob is instructor for specific course
45+ # g, user:carol, role:library_admin, * # Carol is global library admin
6246#
6347# Role hierarchy (optional):
64- # g, role:org_admin, role:org_editor # org_admin inherits org_editor permissions
65- #
66- # NOTE: Without scope in role assignments, authorization control must rely entirely
67- # on policy definitions (p) to restrict access to appropriate scopes/contexts.
68- g = _, _
48+ # g, role:org_admin, role:org_editor, org:OpenedX # org_admin inherits org_editor permissions
49+ g = _, _, _
6950
7051# g2: Action grouping and implications
7152# Maps high-level actions to specific actions to reduce policy duplication
@@ -92,26 +73,19 @@ e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
9273[matchers]
9374# Authorization matching logic
9475#
95- # SUBJECT MATCHING:
96- # - g(r.sub, p.sub): check if request subject matches policy subject (role assignment)
97- # This handles user-to-role mappings defined in the role_definition section
76+ # ROLE MATCHING:
77+ # - g(r.sub, p.sub, r.scope): check if subject has role in requested scope
78+ # - g(r.sub, p.sub, "*"): check if subject has role in all resources in the scope
79+ #
80+ # SCOPE MATCHING:
81+ # - keyMatch(r.scope, p.scope): scope matches pattern
9882#
9983# ACTION MATCHING:
84+ # - r.act == p.act: exact action match
10085# - g2(p.act, r.act): policy action implies requested action via grouping
101- # Allows high-level actions (manage) to grant specific actions (read/write/delete)
102- #
103- # SCOPE MATCHING:
104- # - keyMatch(r.scope, p.scope): check if request scope matches policy scope
105- # Supports wildcard matching (e.g., "*" matches any scope)
106- # Enables hierarchical scope matching for nested authorization contexts
10786#
10887# All conditions must be true for a policy to match:
109- # 1. Subject must have the required role (via role assignment)
110- # 2. Policy action must imply the requested action (via action grouping)
111- # 3. Request scope must match the policy scope (with wildcard support)
112- #
113- # SCOPE-BASED AUTHORIZATION:
114- # The matcher uses keyMatch for flexible scope matching, allowing policies
115- # to apply to specific scopes (org:OpenedX) or globally (*), providing
116- # fine-grained control over authorization contexts.
117- m = g(r.sub, p.sub) && g2(p.act, r.act) && keyMatch(r.scope, p.scope)
88+ # 1. Subject must have role in scope OR global role
89+ # 2. Scope must match pattern
90+ # 3. Action must match OR inherit via action grouping
91+ m = (g(r.sub, p.sub, r.scope) || g(r.sub, p.sub, "*")) && keyMatch(r.scope, p.scope) && (r.act == p.act || g2(p.act, r.act))
0 commit comments