Skip to content

Commit dff3863

Browse files
[FC-0099] feat: add API function to return scopes IDs for user-role (openedx#117)
1 parent ca5f899 commit dff3863

File tree

5 files changed

+65
-1
lines changed

5 files changed

+65
-1
lines changed

CHANGELOG.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ Unreleased
1616

1717
*
1818

19+
0.9.0 - 2025-10-27
20+
******************
21+
22+
Added
23+
=====
24+
25+
* Function API to retrieve scopes for a given role and subject.
26+
1927
0.8.0 - 2025-10-24
2028
******************
2129

openedx_authz/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
import os
66

7-
__version__ = "0.8.0"
7+
__version__ = "0.9.0"
88

99
ROOT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))

openedx_authz/api/roles.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"get_subject_role_assignments_for_role_in_scope",
3838
"get_all_subject_role_assignments_in_scope",
3939
"get_subject_role_assignments",
40+
"get_scopes_for_role_and_subject",
4041
]
4142

4243
# TODO: these are the concerns we still have to address:
@@ -373,3 +374,22 @@ def get_subjects_for_role(role: RoleData) -> list[SubjectData]:
373374
enforcer = AuthzEnforcer.get_enforcer()
374375
policies = enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.ROLE.value, role.namespaced_key)
375376
return [SubjectData(namespaced_key=policy[GroupingPolicyIndex.SUBJECT.value]) for policy in policies]
377+
378+
379+
def get_scopes_for_role_and_subject(role: RoleData, subject: SubjectData) -> list[ScopeData]:
380+
"""Get all the scopes where a specific subject is assigned a specific role.
381+
382+
Args:
383+
role (RoleData): The role to filter scopes.
384+
subject (SubjectData): The subject to filter scopes.
385+
386+
Returns:
387+
list[ScopeData]: A list of scopes where the subject is assigned the specified role.
388+
"""
389+
enforcer = AuthzEnforcer.get_enforcer()
390+
policies = enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.SUBJECT.value, subject.namespaced_key)
391+
return [
392+
ScopeData(namespaced_key=policy[GroupingPolicyIndex.SCOPE.value])
393+
for policy in policies
394+
if policy[GroupingPolicyIndex.ROLE.value] == role.namespaced_key
395+
]

openedx_authz/api/users.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
batch_assign_role_to_subjects_in_scope,
1717
batch_unassign_role_from_subjects_in_scope,
1818
get_all_subject_role_assignments_in_scope,
19+
get_scopes_for_role_and_subject,
1920
get_subject_role_assignments,
2021
get_subject_role_assignments_for_role_in_scope,
2122
get_subject_role_assignments_in_scope,
@@ -198,3 +199,19 @@ def get_users_for_role(role_external_key: str) -> list[UserData]:
198199
"""
199200
users = get_subjects_for_role(RoleData(external_key=role_external_key))
200201
return [UserData(namespaced_key=user.namespaced_key) for user in users]
202+
203+
204+
def get_scopes_for_role_and_user(role_external_key: str, user_external_key: str) -> list[ScopeData]:
205+
"""Get all scopes where a specific user has been assigned a specific role.
206+
207+
Args:
208+
role_external_key (str): The role to filter scopes (e.g., 'instructor').
209+
user_external_key (str): ID of the user (e.g., 'john_doe').
210+
211+
Returns:
212+
list[ScopeData]: A list of scopes where the user has the specified role.
213+
"""
214+
return get_scopes_for_role_and_subject(
215+
RoleData(external_key=role_external_key),
216+
UserData(external_key=user_external_key),
217+
)

openedx_authz/tests/api/test_roles.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
get_permissions_for_active_roles_in_scope,
1919
get_permissions_for_single_role,
2020
get_role_definitions_in_scope,
21+
get_scopes_for_role_and_subject,
2122
get_subject_role_assignments,
2223
get_subject_role_assignments_for_role_in_scope,
2324
get_subject_role_assignments_in_scope,
@@ -505,6 +506,24 @@ def test_get_role_assignments_in_scope(self, role_name, scope_name, expected_cou
505506

506507
self.assertEqual(len(role_assignments), expected_count)
507508

509+
def test_get_scopes_for_role_and_subject(self):
510+
"""Test retrieving scopes for a given role and subject.
511+
512+
Expected result:
513+
- The scopes associated with the specified role and subject are correctly retrieved.
514+
"""
515+
role_name = "library_author"
516+
subject_name = "liam"
517+
expected_scopes = {"lib:Org4:art_101", "lib:Org4:art_201", "lib:Org4:art_301"}
518+
519+
scopes = get_scopes_for_role_and_subject(
520+
RoleData(external_key=role_name),
521+
SubjectData(external_key=subject_name),
522+
)
523+
524+
scope_names = {scope.external_key for scope in scopes}
525+
self.assertEqual(scope_names, expected_scopes)
526+
508527

509528
@ddt
510529
class TestRoleAssignmentAPI(RolesTestSetupMixin):

0 commit comments

Comments
 (0)