diff --git a/config/encryptid_auth.py b/config/encryptid_auth.py index ba60c88..76c9faf 100644 --- a/config/encryptid_auth.py +++ b/config/encryptid_auth.py @@ -159,6 +159,7 @@ def get_or_create_user(claims: dict): try: from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed + from rest_framework import permissions class EncryptIDAuthentication(BaseAuthentication): """ @@ -186,6 +187,50 @@ try: def authenticate_header(self, request): return 'Bearer realm="encryptid"' + class SpacePermission(permissions.BasePermission): + """ + DRF permission that enforces SharedSpace visibility rules. + + Expects the view to implement get_space_config(request) returning + {'visibility': str, 'owner_did': str} or None (no space context). + """ + + def has_permission(self, request, view): + get_config = getattr(view, 'get_space_config', None) + if not get_config: + return True + + config = get_config(request) + if not config: + return True # No space context — allow + + visibility = config.get('visibility', 'public') + + # Public spaces: anyone can read and write + if visibility == 'public': + return True + + # Public-read spaces: anyone can read, auth required to write + if visibility == 'public_read': + if request.method in permissions.SAFE_METHODS: + return True + return request.user and request.user.is_authenticated + + # Authenticated spaces: any logged-in user + if visibility == 'authenticated': + return request.user and request.user.is_authenticated + + # Members-only: must be the space owner + if visibility == 'members_only': + if not (request.user and request.user.is_authenticated): + return False + owner_did = config.get('owner_did', '') + if not owner_did: + return False + return getattr(request.user, 'email', '') == owner_did + + return False + except ImportError: # DRF not installed — skip pass