import operator from django.core.exceptions import ObjectDoesNotExist from django.http import Http404 from rest_framework.permissions import BasePermission from funkwhale_api.common import preferences class ConditionalAuthentication(BasePermission): def has_permission(self, request, view): if preferences.get("common__api_authentication_required"): return (request.user and request.user.is_authenticated) or ( hasattr(request, "actor") and request.actor ) return True class OwnerPermission(BasePermission): """ Ensure the request user is the owner of the object. Usage: class MyView(APIView): model = MyModel permission_classes = [OwnerPermission] owner_field = 'owner' owner_checks = ['read', 'write'] """ perms_map = { "GET": "read", "OPTIONS": "read", "HEAD": "read", "POST": "write", "PUT": "write", "PATCH": "write", "DELETE": "write", } def has_object_permission(self, request, view, obj): method_check = self.perms_map[request.method] owner_checks = getattr(view, "owner_checks", ["read", "write"]) if method_check not in owner_checks: # check not enabled return True owner_field = getattr(view, "owner_field", "user") owner_exception = getattr(view, "owner_exception", Http404) try: owner = operator.attrgetter(owner_field)(obj) except ObjectDoesNotExist: raise owner_exception if not owner or not request.user.is_authenticated or owner != request.user: raise owner_exception return True class PrivacyLevelPermission(BasePermission): """ Ensure the request actor have access to the object considering the privacylevel configuration of the user. request.user is None if actor, else its Anonymous if user is not auth. """ def has_object_permission(self, request, view, obj): if ( not hasattr(obj, "user") and hasattr(obj, "actor") and not obj.actor.is_local ): # it's a remote actor object. It should be public. # But we could trigger an update of the remote actor data # to avoid leaking data (#2326) return True privacy_level = ( obj.actor.user.privacy_level if hasattr(obj, "actor") else obj.user.privacy_level ) obj_actor = obj.actor if hasattr(obj, "actor") else obj.user.actor if privacy_level == "everyone": return True # user is anonymous if hasattr(request, "actor"): request_actor = request.actor elif request.user and request.user.is_authenticated: request_actor = request.user.actor else: return False if privacy_level == "instance": # user is local if request.user and hasattr(request.user, "actor"): return True elif hasattr(request, "actor") and request.actor and request.actor.is_local: return True else: return False elif privacy_level == "me" and obj_actor == request_actor: return True elif privacy_level == "followers" and ( request_actor in obj.user.actor.get_approved_followers() ): return True else: return False