Conversation
|
Thanks for the pull request, @BryanttV! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
There was a problem hiding this comment.
Thank you so much for this! I haven't tested this so this first review is only a static scan of the code. My main concern is how to maintain the extensibility we've built so far with multiple classes, and whether we could leverage that to include this (globs) as a new scope. Curious to know what you think!
|
Hi @mariajgrimaldi, thanks for the review!
I definitely agree with your approach. I think we could handle new scopes specifically at the glob level, for example, in this case, something like |
To me, it sounds like having 2 separate glob classes would be better ( |
41f4c49 to
c8e02fa
Compare
c8e02fa to
f7a21b9
Compare
7f23636 to
8f49024
Compare
8f49024 to
a08f22c
Compare
89615f8 to
7c0d757
Compare
793d325 to
f4dfa7e
Compare
|
@mariajgrimaldi @rodmgwgu, thank you very much for the review! I’ve just made the final changes according to your valuable suggestions. A new base class ( |
| namespace, external_key = namespaced_key.split(AUTHZ_POLICY_ATTRIBUTES_SEPARATOR, 1) | ||
|
|
||
| # Check if this is a glob pattern (contains wildcard) | ||
| is_glob = GLOBAL_SCOPE_WILDCARD in external_key |
openedx_authz/api/data.py
Outdated
| if is_glob: | ||
| # Try to get glob-specific class first | ||
| glob_subclass = mcs.glob_registry.get(namespace) | ||
| if glob_subclass and glob_subclass.validate_external_key(external_key): |
There was a problem hiding this comment.
What if it's somehow invalid? L325 raises an error but here it will break I think
There was a problem hiding this comment.
Is it possible for a subclass to be invalid for globs but valid for exact matches? 🤔
There was a problem hiding this comment.
What if it's somehow invalid? L325 raises an error but here it will break I think
It won’t break here.
If is_glob is true and the glob subclass validation fails, the code intentionally falls back to the standard scope subclass and runs its validate_external_key. If that also fails, we raise ValueError (invalid format).
So the failure path is still controlled.
Is it possible for a subclass to be invalid for globs but valid for exact matches? 🤔
Mmm, in theory, yes, a glob-invalid key could be exact-valid if a namespace’s exact validator allows *.
In our current lib / course-v1 implementations, exact validators do not accept wildcard keys, so it still ends in ValueError.
There was a problem hiding this comment.
If is_glob is true and the glob subclass validation fails, the code intentionally falls back to the standard scope subclass and runs its validate_external_key. If that also fails, we raise ValueError (invalid format).
So the failure path is still controlled.
Mmmm but the error would be a bit misleading, no? Asking for a format it doesn't correspond with glob classes.
There was a problem hiding this comment.
Yes, you're right. I included new exceptions specifically for glob scopes and updated the tests: 23f340e
There was a problem hiding this comment.
I tested this in my local by calling functions manually in the CLI, it's looking good, it behaved as expected.
This is what I ran:
from openedx_authz.api import assign_role_to_user_in_scope, unassign_role_from_user, get_user_role_assignments, is_user_allowed
# Courses
assign_role_to_user_in_scope('contributor', 'course_staff', 'course-v1:OpenedX+*') # Success
is_user_allowed('contributor', 'courses.manage_advanced_settings', 'course-v1:OpenedX+DemoX+DemoCourse') # True
is_user_allowed('contributor', 'courses.manage_advanced_settings', 'course-v1:OpenedX+DemoX+OtherCourse') # True
is_user_allowed('contributor', 'courses.manage_advanced_settings', 'course-v1:OpenedX2+DemoX+DemoCourse') # False
assign_role_to_user_in_scope('contributor', 'course_staff', 'course-v1:OpenedX+**') # Failed as expected
assign_role_to_user_in_scope('contributor', 'course_staff', 'course-v1:OpenedX*') # Failed as expected
# Libraries
assign_role_to_user_in_scope('contributor', 'library_contributor', 'lib:WGU:*') # Success
is_user_allowed('contributor', 'content_libraries.view_library', 'lib:WGU:CSPROB') # True
is_user_allowed('contributor', 'content_libraries.view_library', 'lib:WGU:OTHER') # True
is_user_allowed('contributor', 'content_libraries.view_library', 'lib:WGU2:CSPROB') # False
assign_role_to_user_in_scope('contributor', 'library_contributor', 'lib:WGU:**') # Failed as expected
assign_role_to_user_in_scope('contributor', 'library_contributor', 'lib:WGU*') # Failed as expectede1fd890 to
3e7b441
Compare
3e7b441 to
e498015
Compare
d2e8055 to
d3d4238
Compare
bmtcril
left a comment
There was a problem hiding this comment.
Looks good, thanks for all of the work on this!
Closes: #172
Description
This PR implements organization-level glob pattern matching for role assignments in the Casbin enforcer, enabling administrators to assign roles across all courses or libraries within a specific organization using a single policy rule.
Previously, the AuthZ system only supported:
lib:WGU:CSPROBorcourse-v1:OpenedX+DemoX+DemoCourse)Now, administrators can assign roles using organization-level glob patterns:
lib:DemoX:*(matches all libraries in DemoX org)course-v1:OpenedX+*(matches all courses in OpenedX org)Changes
Core Functionality
Domain Matching Function: Added
key_match_functo the Casbin enforcer to enable glob-like pattern matching at the end of scope stringsAuthzEnforcer._initialize_enforcer()to register the matching function viaenforcer.add_named_domain_matching_func("g", key_match_func)course-v1:OpenedX+*Validation Rules:
course-v1:OpenedX+*,lib:DemoX:*(org-level wildcards)course-v1*(wildcard before org)course-v1:*(wildcard without org prefix)course-v1:OpenedX+Course*(wildcard at course level)lib:DemoX:Slug*(wildcard at slug level)Example Use Case
Before (required one role assignment per course):
After (single org-level assignment):
Permission Check:
Technical Details
Casbin Model Behavior
The
key_match_funcfunction fromcasbin.utilenables pattern matching where:course-v1:OpenedX+*matches any string starting withcourse-v1:OpenedX+lib:DemoX:*matches any string starting withlib:DemoX:This is applied to the domain (scope) parameter of the "g" grouping function in the Casbin model, allowing role assignments with glob patterns to match concrete resource scopes during permission checks.
Glob Scope Base Class
OrgGlobDatais the base class for organization-scoped glob scope keys.It represents organization-wide patterns of the form:
{NAMESPACE}:{ORG}{ID_SEPARATOR}*Concrete glob scope types such as
OrgContentLibraryGlobDataandOrgCourseOverviewGlobDatainherit fromOrgGlobDataand specialize it for their respective namespaces.Glob Scope Data Classes
OrgContentLibraryGlobData(lib:ORG:*): New scope type representing “all libraries in organization ORG”.OrgCourseOverviewGlobData(course-v1:ORG+*): New scope type representing “all courses in organization ORG”.Security Considerations
Testing Instructions
To test these changes, you can check the REST API. You can download this open-edx-authz.postman_collection.json, OR go to
{lms_domain}/api-docs/#/authzOR use the DRF UI from the browser.Test Plan
Related Documentation
Merge checklist
Check off if complete or not applicable: