5

I've got an endpoint built with Django Rest Framework, to which I now want to add permissions so that only users belonging to a certain group can access an endpoint. So I'm using token based access and inspired by this example I'm trying to use the following code:

class ReadPermission(BasePermission):
    def has_permission(self, request, view):
        return request.user.groups.filter(name=settings.GROUP_POST_DATA).exists()

class MyEndpoint(mixins.ListModelMixin, viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated, ReadPermission]
    http_method_names = ['get']
    # etc

But unfortunately I get anAttributeError: 'User' object has no attribute 'groups'

Why doesn't the user object have groups?

kramer65
  • 50,427
  • 120
  • 308
  • 488
  • Can you show the authentication class you are using if it is custom? It could be that the user object attached to the request isn't the right one – Ken4scholars Sep 28 '20 at 12:54
  • Can you add the versions of Django and Django REST Framework? – JPG Sep 28 '20 at 13:35
  • Your code fragment is correct; can you show the registration of MyEndpoint (in urls.py) ? I would also insert a breakpont (pdb.set_trace() is OK) in ReadPermission .has_permission() and inspect "request.user". Is it a User ? Then try "dir(request.user)" to show the attributes of the object. Is "groups" present ? And finally: request.user.groups.all(). Can you see a queryset of groups? – Mario Orlandi Sep 28 '20 at 14:56

2 Answers2

5

Seems like you're not using or inheriting from the default Django User model (or the AbstractUser) from django.contrib.auth.models, which have the ManyToMany relationship to Groups.

If you're using some custom User model you can simply add the PermissionsMixin from the aforementioned module to inheritance (see docs). And also make sure that django.contrib.auth is in your INSTALLED_APPS setting.

aberezh
  • 68
  • 4
  • Thank you for your suggestion. Unfortunately I don't define a custom user model. Also, I added `django.contrib.auth` to the `INSTALLED_APPS`. I added a breakpoint to the view to inspect the element. Would you have any idea how I can inspect it from pdb to check whether it has the many to many relationships? – kramer65 Sep 28 '20 at 12:28
2

The issue must be in you authentication_classes--(DRF doc) attribute (or DEFAULT_AUTHENTICATION_CLASSES settings -- (DRF doc))

Behind the scenes, the DRF authentication classes fetch the auth user from the database and assign it to the request object only after the specified request entity (like Token, JWT Token, CSRF Token, etc) validated against the database. This validation and assigning process is happening in the authentication classes.

Suppose, if Django or DRF failed to identify the requested user, it will assign the AnonymousUser--(Django doc) object to the request.

Fortunately, the AnonymousUser object does have a groups and user_permissions attributes. Which means, neither request.user.groups nor request.user.user_permissions will not raise any AttributeError exceptions.

Coming to your case, the request.user.groups raised an exception, which indicates

  1. the received User object is neither settings.AUTH_USER_MODEL nor AnonymousUser
  2. the MyEndpoint is missing a authentication_classes attributes, hence DRF uses the DEFAULT_AUTHENTICATION_CLASSES, which may contain a custom class, which may not built properly

Solution

I assume you need to use the token-based auth system, so I am using the DRF's TokenAuthentication -- (DRF doc) here.

Assigning authentication_classes = (TokenAuthentication,) in the view class will definitely sets the request.user to either settings.AUTH_USER_MODEL instance or AnonymousUser.

from rest_framework.authentication import TokenAuthentication


class MyEndpoint(
    mixins.ListModelMixin,
    viewsets.GenericViewSet
):
    permission_classes = [IsAuthenticated, ReadPermission]
    http_method_names = ['get']
    authentication_classes = (TokenAuthentication,)

Note

The issue does not belong to the permission_classes settings, but the authentication_classes


Still not solved??

  1. Is it reproducible? (your code snippet is working nicely in my machine, probably everyone's machine) If so, add the steps to reproduce
  2. What is the value of MIDDLEWARE? Do you have any custom one?
  3. What is the value of UNAUTHENTICATED_USER?
JPG
  • 82,442
  • 19
  • 127
  • 206