supertokens / supertokens-python

Python SDK for SuperTokens
https://supertokens.com
Other
130 stars 38 forks source link

AttributeError when accessing request.supertokens #130

Closed kozubaeff closed 2 years ago

kozubaeff commented 2 years ago

When trying to access supertokens attribute of HttpRequestin Django Rest Views, error is thrown - AttributeError: 'Request' object has no attribute 'supertokens'. Steps to reproduce:

  1. Create viewset and use method_decorator for verifying session:
    
    @method_decorator(verify_session(), name='dispatch')
    class SomeViewSet(CreateModelMixin, ReadOnlyModelViewSet):
     ...
2. Access `request.supertokens` in `viewset` method:

def create(self, request, *args, **kwargs): session: SessionContainer = request.supertokens ....

execreate commented 2 years ago

Actually, there is a little error in the problem statement. The error is happening not in the view, but in the permission classes. Here is more context on what is happening.

The viewset:

@method_decorator(verify_session(), name='dispatch')
class CreateAppointmentViewSet(CreateModelMixin,
                               RetrieveModelMixin,
                               DestroyModelMixin,
                               GenericViewSet):
    queryset = Appointment.objects.filter(cancelled=False)
    serializer_class = AppointmentSerializer

    def get_permissions(self):
        if self.action == 'create':
            self.permission_classes = [AppointmentCreatePermission]
        elif self.action == 'retrieve':
            self.permission_classes = [AppointmentReadPermission]
        elif self.action == 'destroy':
            self.permission_classes = [AppointmentDeletePermission]
        return super().get_permissions()

Permission class example:

class AppointmentCreatePermission(permissions.BasePermission):
    def has_permission(self, request, view):
        session: SessionContainer = request.supertokens        # the error is happening here
        current_access_token_payload = session.get_access_token_payload()

and here is the stack trace:

2022-04-27T13:13:02.966602468Z Traceback (most recent call last):
2022-04-27T13:13:02.966607690Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/request.py", line 416, in __getattr__
2022-04-27T13:13:02.966612269Z     return getattr(self._request, attr)
2022-04-27T13:13:02.966616396Z AttributeError: 'WSGIRequest' object has no attribute 'supertokens'
2022-04-27T13:13:02.966620630Z 
2022-04-27T13:13:02.966624730Z During handling of the above exception, another exception occurred:
2022-04-27T13:13:02.966629141Z 
2022-04-27T13:13:02.966632989Z Traceback (most recent call last):
2022-04-27T13:13:02.966637624Z   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
2022-04-27T13:13:02.966654749Z     response = get_response(request)
2022-04-27T13:13:02.966659401Z   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
2022-04-27T13:13:02.966663520Z     response = wrapped_callback(request, *callback_args, **callback_kwargs)
2022-04-27T13:13:02.966685189Z   File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
2022-04-27T13:13:02.966689522Z     return view_func(*args, **kwargs)
2022-04-27T13:13:02.966693746Z   File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 69, in view
2022-04-27T13:13:02.966698004Z     return self.dispatch(request, *args, **kwargs)
2022-04-27T13:13:02.966702175Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
2022-04-27T13:13:02.966706427Z     response = self.handle_exception(exc)
2022-04-27T13:13:02.966710567Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
2022-04-27T13:13:02.966714919Z     self.raise_uncaught_exception(exc)
2022-04-27T13:13:02.966719026Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
2022-04-27T13:13:02.966723400Z     raise exc
2022-04-27T13:13:02.966727601Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
2022-04-27T13:13:02.966731852Z     response = handler(request, *args, **kwargs)
2022-04-27T13:13:02.966736038Z   File "/usr/local/lib/python3.9/site-packages/drf_spectacular/views.py", line 69, in get
2022-04-27T13:13:02.966740567Z     return self._get_schema_response(request)
2022-04-27T13:13:02.966744741Z   File "/usr/local/lib/python3.9/site-packages/drf_spectacular/views.py", line 77, in _get_schema_response
2022-04-27T13:13:02.966749222Z     data=generator.get_schema(request=request, public=self.serve_public),
2022-04-27T13:13:02.966769161Z   File "/usr/local/lib/python3.9/site-packages/drf_spectacular/generators.py", line 262, in get_schema
2022-04-27T13:13:02.966773333Z     paths=self.parse(request, public),
2022-04-27T13:13:02.966778461Z   File "/usr/local/lib/python3.9/site-packages/drf_spectacular/generators.py", line 208, in parse
2022-04-27T13:13:02.966782647Z     if not (public or self.has_view_permissions(path, method, view)):
2022-04-27T13:13:02.966786777Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/schemas/generators.py", line 236, in has_view_permissions
2022-04-27T13:13:02.966790934Z     view.check_permissions(view.request)
2022-04-27T13:13:02.966794921Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 332, in check_permissions
2022-04-27T13:13:02.966799253Z     if not permission.has_permission(request, self):
2022-04-27T13:13:02.966804861Z   File "/usr/src/app/apps/mysupertokens/permission_classes/appointment/appointment.py", line 29, in has_permission
2022-04-27T13:13:02.966812512Z     session: SessionContainer = request.supertokens
2022-04-27T13:13:02.966816772Z   File "/usr/local/lib/python3.9/site-packages/rest_framework/request.py", line 418, in __getattr__
2022-04-27T13:13:02.966820859Z     return self.__getattribute__(attr)
2022-04-27T13:13:02.966825012Z AttributeError: 'Request' object has no attribute 'supertokens'
execreate commented 2 years ago

The error must be in drf_spectacular. It has a mechanism to hide the endpoints that are not permitted for the current user, so it tries to call the permission classes. The issue must be that a direct permissions call uses just an empty request which does not go through the verify_session wrapper.

Since only superusers in our project have access to API doc which is provided by drf_spectacular, I just added superuser check to permission classes:

    def has_permission(self, request, view):
        if hasattr(request, 'user') and request.user is not None and request.user.is_superuser:
            return True

and it works just fine :)