Description
We are currently performing a hard load of the policy when validating the permissions a user has in the permissions/validate/me endpoint. This is done to ensure that the most up-to-date policy is used at the time of validation.
|
@apidocs.schema( |
|
body=PermissionValidationSerializer(help_text="The permissions to validate", many=True), |
|
responses={ |
|
status.HTTP_200_OK: PermissionValidationResponseSerializer, |
|
status.HTTP_400_BAD_REQUEST: "The request data is invalid", |
|
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated", |
|
}, |
|
) |
|
def post(self, request: HttpRequest) -> Response: |
|
"""Validate one or more permissions for the authenticated user.""" |
|
AuthzEnforcer.get_enforcer().load_policy() |
|
|
|
serializer = PermissionValidationSerializer(data=request.data, many=True) |
|
serializer.is_valid(raise_exception=True) |
|
data = serializer.validated_data |
|
|
|
username = request.user.username |
|
response_data = [] |
|
for permission in data: |
|
try: |
|
action = permission["action"] |
|
scope = permission["scope"] |
|
allowed = api.is_user_allowed(username, action, scope) |
|
response_data.append({"action": action, "scope": scope, "allowed": allowed}) |
|
except ValueError as e: |
|
logger.error(f"Error validating permission for user {username}: {e}") |
|
return Response(data={"message": "Invalid scope format"}, status=status.HTTP_400_BAD_REQUEST) |
|
except Exception as e: # pylint: disable=broad-exception-caught |
|
logger.error(f"Error validating permission for user {username}: {e}") |
|
return Response( |
|
data={"message": "An error occurred while validating permissions"}, |
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR, |
|
) |
|
|
|
serializer = PermissionValidationResponseSerializer(response_data, many=True) |
|
return Response(serializer.data, status=status.HTTP_200_OK) |
This change was made because, when relying only on the SyncEnforcer, which reloads the policy every 5 seconds, an odd behavior has been observed where the policy seems to be completely cleared from memory periodically. As a result, when permissions are validated, the system incorrectly returns that the user does not have them, even though they do exist in the database.
The main issue with performing a hard load is that the entire policy is reloaded every time permission validation is needed. This is not a sustainable solution due to the large number of policies that may exist in the database, which would significantly affect the performance of an endpoint that is widely used across the platform.
Impact
- Performance degradation: Continuously performing a hard load of the entire policy on every permission validation causes high database and memory usage, increasing response times for the permissions/validate/me endpoint.
- Scalability issues: As the number of policies grows, the cost of each validation request increases linearly, making this approach unsustainable for production environments with large datasets.
- Inconsistent authorization results: The unexpected behavior of the SyncEnforcer (clearing policies from memory) may cause users to temporarily lose permissions that actually exist in the database.
Possible Approaches
- Investigate the SyncEnforcer behavior: Review how the
SyncEnforcer instance is managed in memory. Validate whether it is being reinitialized or losing its internal policy cache due to concurrency or lifecycle issues.
- Implement selective or partial policy reloads: Instead of a full hard load, reload only the policies related to the specific user, role, or scope being validated. This approach would significantly reduce the I/O and memory overhead of loading the full policy on every request.
Description
We are currently performing a hard load of the policy when validating the permissions a user has in the
permissions/validate/meendpoint. This is done to ensure that the most up-to-date policy is used at the time of validation.openedx-authz/openedx_authz/rest_api/v1/views.py
Lines 96 to 131 in e374fbf
This change was made because, when relying only on the
SyncEnforcer, which reloads the policy every 5 seconds, an odd behavior has been observed where the policy seems to be completely cleared from memory periodically. As a result, when permissions are validated, the system incorrectly returns that the user does not have them, even though they do exist in the database.The main issue with performing a hard load is that the entire policy is reloaded every time permission validation is needed. This is not a sustainable solution due to the large number of policies that may exist in the database, which would significantly affect the performance of an endpoint that is widely used across the platform.
Impact
Possible Approaches
SyncEnforcerinstance is managed in memory. Validate whether it is being reinitialized or losing its internal policy cache due to concurrency or lifecycle issues.