mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-05 16:37:43 +00:00
fba879e739
When declaring scopes with our OIDC provider, they require us to prefix each scope with our application name. This is to prevent reserving generic scopes like rooms:list for only our app, as they manage a large federation. I’m proposing a workaround where, if a resource server prefix is detected in the scope, it’s stripped out. This solution is simple and sufficient in my opinion. Since the scopes are defined in the database, I don’t want to update them directly. Additionally, each self-hosted instance may have a different application name, so the prefix should be configurable via a Django setting.
85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
"""Permission handlers for application-delegated API access."""
|
|
|
|
import logging
|
|
from typing import Dict
|
|
|
|
from django.conf import settings
|
|
|
|
from rest_framework import exceptions, permissions
|
|
|
|
from .. import models
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class BaseScopePermission(permissions.BasePermission):
|
|
"""Base class for scope-based permission checking.
|
|
|
|
Subclasses must define `scope_map` attribute mapping actions to required scopes.
|
|
"""
|
|
|
|
scope_map: Dict[str, str] = {}
|
|
|
|
def has_permission(self, request, view):
|
|
"""Check if the JWT token contains the required scope for this action.
|
|
|
|
Args:
|
|
request: DRF request object with authenticated user
|
|
view: ViewSet instance
|
|
|
|
Returns:
|
|
bool: True if permission granted
|
|
|
|
Raises:
|
|
PermissionDenied: If required scope is missing from token
|
|
"""
|
|
# Get the current action (e.g., 'list', 'create')
|
|
action = getattr(view, "action", None)
|
|
if not action:
|
|
raise exceptions.PermissionDenied(
|
|
"Insufficient permissions. Unknown action."
|
|
)
|
|
|
|
required_scope = self.scope_map.get(action)
|
|
if not required_scope:
|
|
# Action not in scope_map, deny by default
|
|
raise exceptions.PermissionDenied(
|
|
f"Insufficient permissions. Required scope: {required_scope}"
|
|
)
|
|
|
|
token_payload = request.auth
|
|
token_scopes = token_payload.get("scope")
|
|
|
|
if not token_scopes:
|
|
raise exceptions.PermissionDenied("Insufficient permissions.")
|
|
|
|
# Ensure scopes is a list (handle both list and space-separated string)
|
|
if isinstance(token_scopes, str):
|
|
token_scopes = token_scopes.split()
|
|
|
|
if settings.OIDC_RS_SCOPES_PREFIX:
|
|
token_scopes = [
|
|
scope.replace(f"{settings.OIDC_RS_SCOPES_PREFIX}:", "")
|
|
for scope in token_scopes
|
|
]
|
|
|
|
if required_scope not in token_scopes:
|
|
raise exceptions.PermissionDenied(
|
|
f"Insufficient permissions. Required scope: {required_scope}"
|
|
)
|
|
|
|
return True
|
|
|
|
|
|
class HasRequiredRoomScope(BaseScopePermission):
|
|
"""Permission class for Room-related operations."""
|
|
|
|
scope_map = {
|
|
"list": models.ApplicationScope.ROOMS_LIST,
|
|
"retrieve": models.ApplicationScope.ROOMS_RETRIEVE,
|
|
"create": models.ApplicationScope.ROOMS_CREATE,
|
|
"update": models.ApplicationScope.ROOMS_UPDATE,
|
|
"partial_update": models.ApplicationScope.ROOMS_UPDATE,
|
|
"destroy": models.ApplicationScope.ROOMS_DELETE,
|
|
}
|