Skip to content

Commit ab9e2fb

Browse files
authored
[FC-0099] fix: return user count of the specific scope (openedx#121)
1 parent dff3863 commit ab9e2fb

6 files changed

Lines changed: 52 additions & 13 deletions

File tree

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.1 - 2025-10-28
20+
******************
21+
22+
Fixed
23+
=====
24+
25+
* Fix role user count to accurately filter users assigned to roles within specific scopes instead of across all scopes.
26+
1927
0.9.0 - 2025-10-27
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.9.0"
7+
__version__ = "0.9.1"
88

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

openedx_authz/api/roles.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,18 +362,23 @@ def get_all_subject_role_assignments_in_scope(
362362
return list(role_assignments_per_subject.values())
363363

364364

365-
def get_subjects_for_role(role: RoleData) -> list[SubjectData]:
366-
"""Get all the subjects assigned to a specific role.
365+
def get_subjects_for_role_in_scope(role: RoleData, scope: ScopeData) -> list[SubjectData]:
366+
"""Get all the subjects assigned to a specific role in a specific scope.
367367
368368
Args:
369369
role (RoleData): The role to filter subjects.
370+
scope (ScopeData): The scope to filter subjects.
370371
371372
Returns:
372-
list[SubjectData]: A list of subjects assigned to the specified role.
373+
list[SubjectData]: A list of subjects assigned to the specified role in the specified scope.
373374
"""
374375
enforcer = AuthzEnforcer.get_enforcer()
375376
policies = enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.ROLE.value, role.namespaced_key)
376-
return [SubjectData(namespaced_key=policy[GroupingPolicyIndex.SUBJECT.value]) for policy in policies]
377+
return [
378+
SubjectData(namespaced_key=policy[GroupingPolicyIndex.SUBJECT.value])
379+
for policy in policies
380+
if policy[GroupingPolicyIndex.SCOPE.value] == scope.namespaced_key
381+
]
377382

378383

379384
def get_scopes_for_role_and_subject(role: RoleData, subject: SubjectData) -> list[ScopeData]:

openedx_authz/api/users.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
get_subject_role_assignments,
2121
get_subject_role_assignments_for_role_in_scope,
2222
get_subject_role_assignments_in_scope,
23-
get_subjects_for_role,
23+
get_subjects_for_role_in_scope,
2424
unassign_role_from_subject_in_scope,
2525
)
2626

@@ -34,7 +34,7 @@
3434
"get_user_role_assignments_for_role_in_scope",
3535
"get_all_user_role_assignments_in_scope",
3636
"is_user_allowed",
37-
"get_users_for_role",
37+
"get_users_for_role_in_scope",
3838
]
3939

4040

@@ -188,16 +188,20 @@ def is_user_allowed(
188188
)
189189

190190

191-
def get_users_for_role(role_external_key: str) -> list[UserData]:
192-
"""Get all the users assigned to a specific role.
191+
def get_users_for_role_in_scope(role_external_key: str, scope_external_key: str) -> list[UserData]:
192+
"""Get all the users assigned to a specific role in a specific scope.
193193
194194
Args:
195195
role_external_key (str): The role to filter users (e.g., 'library_admin').
196+
scope_external_key (str): The scope to filter users (e.g., 'lib:DemoX:CSPROB').
196197
197198
Returns:
198-
list[UserData]: A list of users assigned to the specified role.
199+
list[UserData]: A list of users assigned to the specified role in the specified scope.
199200
"""
200-
users = get_subjects_for_role(RoleData(external_key=role_external_key))
201+
users = get_subjects_for_role_in_scope(
202+
RoleData(external_key=role_external_key),
203+
ScopeData(external_key=scope_external_key),
204+
)
201205
return [UserData(namespaced_key=user.namespaced_key) for user in users]
202206

203207

openedx_authz/rest_api/v1/views.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,13 @@ def get(self, request: HttpRequest) -> Response:
432432
"""Retrieve all roles and their permissions for a specific scope."""
433433
serializer = ListRolesWithScopeSerializer(data=request.query_params)
434434
serializer.is_valid(raise_exception=True)
435+
query_params = serializer.validated_data
435436

436-
generic_scope = get_generic_scope(serializer.validated_data["scope"])
437+
generic_scope = get_generic_scope(query_params["scope"])
437438
roles = api.get_role_definitions_in_scope(generic_scope)
438439
response_data = []
439440
for role in roles:
440-
users = api.get_users_for_role(role.external_key)
441+
users = api.get_users_for_role_in_scope(role.external_key, query_params["scope"].external_key)
441442
response_data.append(
442443
{
443444
"role": role.external_key,

openedx_authz/tests/api/test_roles.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get_subject_role_assignments,
2323
get_subject_role_assignments_for_role_in_scope,
2424
get_subject_role_assignments_in_scope,
25+
get_subjects_for_role_in_scope,
2526
unassign_role_from_subject_in_scope,
2627
)
2728
from openedx_authz.engine.enforcer import AuthzEnforcer
@@ -524,6 +525,26 @@ def test_get_scopes_for_role_and_subject(self):
524525
scope_names = {scope.external_key for scope in scopes}
525526
self.assertEqual(scope_names, expected_scopes)
526527

528+
@ddt_data(
529+
("library_author", "lib:Org4:art_101", {"liam"}),
530+
("library_author", "lib:Org4:art_201", {"liam"}),
531+
("library_author", "lib:Org4:art_301", {"liam"}),
532+
("non_existent_role", "lib:Org4:art_101", set()),
533+
("library_author", "sc:non_existent_scope", set()),
534+
("non_existent_role", "sc:non_existent_scope", set()),
535+
)
536+
@unpack
537+
def test_get_subjects_for_role_in_scope(self, role_name: str, scope_name: str, expected_subjects: set[str]):
538+
"""Test retrieving subjects for a given role in a specific scope.
539+
540+
Expected result:
541+
- The subjects associated with the specified role in the given scope are correctly retrieved.
542+
"""
543+
subjects = get_subjects_for_role_in_scope(RoleData(external_key=role_name), ScopeData(external_key=scope_name))
544+
545+
subject_names = {subject.external_key for subject in subjects}
546+
self.assertEqual(subject_names, expected_subjects)
547+
527548

528549
@ddt
529550
class TestRoleAssignmentAPI(RolesTestSetupMixin):

0 commit comments

Comments
 (0)