Skip to content

Commit e374fbf

Browse files
authored
[FC-0099] fix: load policy in role and permissions views (#122)
1 parent fea481a commit e374fbf

File tree

5 files changed

+32
-27
lines changed

5 files changed

+32
-27
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ Change Log
1414
Unreleased
1515
**********
1616

17-
*
17+
0.12.0 - 2025-10-30
18+
********************
19+
20+
Changed
21+
=======
22+
23+
* Load authorization policies in permission class.
1824

1925
0.11.2 - 2025-10-30
2026
********************

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.11.2"
7+
__version__ = "0.12.0"
88

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

openedx_authz/rest_api/v1/permissions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from rest_framework.permissions import BasePermission
66

77
from openedx_authz import api
8+
from openedx_authz.engine.enforcer import AuthzEnforcer
89

910

1011
class PermissionMeta(type(BasePermission)):
@@ -182,6 +183,7 @@ def has_permission(self, request, view) -> bool:
182183
"""
183184
if request.user.is_superuser or request.user.is_staff:
184185
return True
186+
AuthzEnforcer.get_enforcer().load_policy()
185187
return self._get_permission_instance(request).has_permission(request, view)
186188

187189
def has_object_permission(self, request, view, obj) -> bool:
@@ -198,6 +200,7 @@ def has_object_permission(self, request, view, obj) -> bool:
198200
"""
199201
if request.user.is_superuser or request.user.is_staff:
200202
return True
203+
AuthzEnforcer.get_enforcer().load_policy()
201204
return self._get_permission_instance(request).has_object_permission(request, view, obj)
202205

203206

openedx_authz/rest_api/v1/views.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from openedx_authz import api
1818
from openedx_authz.constants import permissions
19+
from openedx_authz.engine.enforcer import AuthzEnforcer
1920
from openedx_authz.rest_api.data import RoleOperationError, RoleOperationStatus
2021
from openedx_authz.rest_api.decorators import authz_permissions, view_auth_classes
2122
from openedx_authz.rest_api.utils import (
@@ -102,23 +103,23 @@ class PermissionValidationMeView(APIView):
102103
)
103104
def post(self, request: HttpRequest) -> Response:
104105
"""Validate one or more permissions for the authenticated user."""
106+
AuthzEnforcer.get_enforcer().load_policy()
107+
105108
serializer = PermissionValidationSerializer(data=request.data, many=True)
106109
serializer.is_valid(raise_exception=True)
110+
data = serializer.validated_data
107111

108112
username = request.user.username
109113
response_data = []
110-
for perm in serializer.validated_data:
114+
for permission in data:
111115
try:
112-
action = perm["action"]
113-
scope = perm["scope"]
116+
action = permission["action"]
117+
scope = permission["scope"]
114118
allowed = api.is_user_allowed(username, action, scope)
115119
response_data.append({"action": action, "scope": scope, "allowed": allowed})
116120
except ValueError as e:
117121
logger.error(f"Error validating permission for user {username}: {e}")
118-
return Response(
119-
data={"message": "Invalid scope format"},
120-
status=status.HTTP_400_BAD_REQUEST,
121-
)
122+
return Response(data={"message": "Invalid scope format"}, status=status.HTTP_400_BAD_REQUEST)
122123
except Exception as e: # pylint: disable=broad-exception-caught
123124
logger.error(f"Error validating permission for user {username}: {e}")
124125
return Response(
@@ -283,15 +284,14 @@ def put(self, request: HttpRequest) -> Response:
283284
"""Assign multiple users to a specific role within a scope."""
284285
serializer = AddUsersToRoleWithScopeSerializer(data=request.data)
285286
serializer.is_valid(raise_exception=True)
287+
data = serializer.validated_data
286288

287-
role = serializer.validated_data["role"]
288-
scope = serializer.validated_data["scope"]
289289
completed, errors = [], []
290-
for user_identifier in serializer.validated_data["users"]:
290+
for user_identifier in data["users"]:
291291
response_dict = {"user_identifier": user_identifier}
292292
try:
293293
user = get_user_by_username_or_email(user_identifier)
294-
result = api.assign_role_to_user_in_scope(user.username, role, scope)
294+
result = api.assign_role_to_user_in_scope(user.username, data["role"], data["scope"])
295295
if result:
296296
response_dict["status"] = RoleOperationStatus.ROLE_ADDED
297297
completed.append(response_dict)
@@ -330,15 +330,14 @@ def delete(self, request: HttpRequest) -> Response:
330330
"""Remove multiple users from a specific role within a scope."""
331331
serializer = RemoveUsersFromRoleWithScopeSerializer(data=request.query_params)
332332
serializer.is_valid(raise_exception=True)
333+
data = serializer.validated_data
333334

334-
role = serializer.validated_data["role"]
335-
scope = serializer.validated_data["scope"]
336335
completed, errors = [], []
337-
for user_identifier in serializer.validated_data["users"]:
336+
for user_identifier in data["users"]:
338337
response_dict = {"user_identifier": user_identifier}
339338
try:
340339
user = get_user_by_username_or_email(user_identifier)
341-
result = api.unassign_role_from_user(user.username, role, scope)
340+
result = api.unassign_role_from_user(user.username, data["role"], data["scope"])
342341
if result:
343342
response_dict["status"] = RoleOperationStatus.ROLE_REMOVED
344343
completed.append(response_dict)

openedx_authz/tests/rest_api/test_views.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ class ViewTestMixin(BaseRolesTestCase):
3939
"""Mixin providing common test utilities for view tests."""
4040

4141
@classmethod
42-
def _assign_roles_to_users(
43-
cls,
44-
assignments: list[dict] | None = None,
45-
):
42+
def _assign_roles_to_users(cls, assignments: list[dict] | None = None):
4643
"""Helper method to assign roles to multiple users.
4744
4845
This method can be used to assign a role to a single user or multiple users
@@ -115,7 +112,7 @@ def setUpClass(cls):
115112
},
116113
{
117114
"subject_name": "regular_7",
118-
"role_name": "library_collaborator",
115+
"role_name": "library_contributor",
119116
"scope_name": "lib:Org3:LIB3",
120117
},
121118
{
@@ -168,9 +165,9 @@ def setUp(self):
168165
([{"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"}], [True]),
169166
# Single permission - denied (scope not assigned to user)
170167
([{"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org2:LIB2"}], [False]),
171-
# # Single permission - denied (action not assigned to user)
168+
# Single permission - denied (action not assigned to user)
172169
([{"action": "edit_library", "scope": "lib:Org1:LIB1"}], [False]),
173-
# # Multiple permissions - mixed results
170+
# Multiple permissions - mixed results
174171
(
175172
[
176173
{"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"},
@@ -793,9 +790,9 @@ def test_get_roles_pagination(self, query_params: dict, expected_count: int, has
793790
# Library Admin user
794791
("regular_5", status.HTTP_200_OK),
795792
# Library Author user
796-
# ("regular_6", status.HTTP_200_OK), # TODO: uncomment this when we have the explicit permissions
797-
# Library Collaborator user
798-
# ("regular_7", status.HTTP_200_OK), # TODO: uncomment this when we have the explicit permissions
793+
("regular_6", status.HTTP_200_OK),
794+
# Library Contributor user
795+
("regular_7", status.HTTP_200_OK),
799796
# Library User user
800797
("regular_8", status.HTTP_200_OK),
801798
# Regular user without permission

0 commit comments

Comments
 (0)