-
-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DjangoRestFramework Integration #175
Comments
I was able to get something manually patched together, in case this is useful for others: from typing import Any, List, Optional, Tuple, Type
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.core.handlers.wsgi import WSGIRequest
from django_downloadview import DownloadResponse, ObjectDownloadView
from rest_framework.authentication import BaseAuthentication, BasicAuthentication
from rest_framework.permissions import BasePermission, DjangoObjectPermissions
from rest_framework_simplejwt.authentication import JWTAuthentication
class DRFAuthenticatedObjectDownloadView(ObjectDownloadView):
"""A generic file download view that automatically authenticates the user and
validates permissions using DRF middleware."""
permissions_class: Type[BasePermission] = DjangoObjectPermissions
# Note: This needs to be kept in sync with
# settings.py REST_FRAMEWORK DEFAULT_AUTHENTICATION_CLASSES
auth_classes: List[Type[BaseAuthentication]] = [BasicAuthentication, JWTAuthentication]
def authenticate(self, request: WSGIRequest) -> None:
"""Updates request.user if the client has sent headers that configured ``auth_classes``
successfully authenticate.
"""
for auth_class in self.auth_classes:
auth_resp: Optional[Tuple[User, None]] = auth_class().authenticate(request)
if auth_resp is not None:
request.user = auth_resp[0]
return
def has_permission(self, request: WSGIRequest) -> None:
"""Validate that the current User has appropriate access permissions to a Model.
Raises:
PermissionDenied: If the user does not have the required permissions.
"""
instance = self.get_object()
permissions = self.permissions_class()
if not (
permissions.has_permission(request, self)
and permissions.has_object_permission(request, self, instance)
):
raise PermissionDenied()
def get(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> DownloadResponse:
"""Authenticate user and check permissions before returning the file download."""
self.authenticate(request)
self.has_permission(request)
return super().get(request, *args, **kwargs) |
I guess this proxy would be useful as part of the code of django-downloadview what do you think? |
Yes, some kind of integration story with DRF would be nice as DRF does not have a native story for how serve authenticated or accelerated-download files. I've only implemented this for a project on I'm not sure on the exact best design for a general solution. |
@johnthagen I'm trying to do something similar for StorageDownloadView. Maybe could you help me with the implementation? At first, it would be enough if only the authentication is checked for a user. I have modified your code, but I was unable to get it working correctly. It downloads the file even when the user is not logged in. |
See: encode/django-rest-framework#7702
DRF does not currently have a way to authenticate file downloads or serve them efficiently through a reverse proxy such as NGINX.
Integrating
django-downloadview
with DRF is challenging becausedjango-downloadview
does not interact with DRF authentication middleware (e.g.REST_FRAMEWORK
:'DEFAULT_AUTHENTICATION_CLASSES'
.django-downloadview
views cannot take advantage of'rest_framework.authentication.BasicAuthentication'
or (third-party)'rest_framework_simplejwt.authentication.JWTAuthentication'
authentication middleware. This results in thedjango-downloadview
View
being presented theAnonymousUser
user, and thus being rejected.The text was updated successfully, but these errors were encountered: