sintonia/api/libretime_api/permissions.py

106 lines
3.5 KiB
Python

from secrets import compare_digest
from django.conf import settings
from rest_framework.permissions import BasePermission
from rest_framework.request import Request
from .core.models import Role
REQUEST_PERMISSION_TYPE_MAP = {
"GET": "view",
"HEAD": "view",
"OPTIONS": "view",
"POST": "change",
"PUT": "change",
"DELETE": "delete",
"PATCH": "change",
}
def get_own_obj(request, view):
user = request.user
if user is None or user.role != Role.HOST:
return ""
if request.method == "GET":
return ""
qs = view.queryset.all()
try:
model_owners = []
for model in qs:
owner = model.get_owner()
if owner not in model_owners:
model_owners.append(owner)
if len(model_owners) == 1 and user in model_owners:
return "own_"
except AttributeError:
return ""
return ""
def get_permission_for_view(request, view):
try:
permission_type = REQUEST_PERMISSION_TYPE_MAP[request.method]
if view.__class__.__name__ == "APIRootView":
return f"{permission_type}_apiroot"
model = view.model_permission_name
own_obj = get_own_obj(request, view)
return f"{permission_type}_{own_obj}{model}"
except AttributeError:
return None
def check_authorization_header(request: Request):
auth_header = request.headers.get("authorization", "")
if auth_header.startswith("Api-Key"):
token = auth_header.split()[1]
return compare_digest(token, settings.CONFIG.general.api_key)
return False
class IsAdminOrOwnUser(BasePermission):
"""
Implements Django Rest Framework permissions. This is separate from
Django's standard permission system. For details see
https://www.django-rest-framework.org/api-guide/permissions/#custom-permissions
"""
def has_permission(self, request, view):
if request.user.is_superuser():
return True
return False
def has_object_permission(self, request, view, obj):
if request.user.is_superuser():
return True
return obj.username == request.user
class IsSystemTokenOrUser(BasePermission):
"""
Implements Django Rest Framework permissions. This is separate from
Django's standard permission system. For details see
https://www.django-rest-framework.org/api-guide/permissions/#custom-permissions
This permission allows services (liquidsoap, 3rd-party, etc) to connect with
an API-Key header. All standard-users (i.e. not using the API-Key) have their
permissions checked against Django's standard permission system.
"""
def has_permission(self, request, view):
if request.user and request.user.is_authenticated:
perm = get_permission_for_view(request, view)
# Required as view_apiroot is a permission not linked to a specific
# model. This use-case allows users to view the base of the API
# explorer. Their assigned group permissions determine further access
# into the explorer.
if perm == "view_apiroot":
return True
return request.user.has_perm(perm)
return check_authorization_header(request)
def has_object_permission(self, request, view, obj):
if request.user and request.user.is_authenticated:
perm = get_permission_for_view(request, view)
return request.user.has_perm(perm, obj)
return check_authorization_header(request)