Skip to content
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

Role not working with DRF ViewSet #99

Open
StefanoFioravanzo opened this issue Apr 19, 2019 · 9 comments
Open

Role not working with DRF ViewSet #99

StefanoFioravanzo opened this issue Apr 19, 2019 · 9 comments

Comments

@StefanoFioravanzo
Copy link

Hello, I am trying to implement a DRF based application with some object level permissions.

I tired the following simple configuration with not success:

u = User.objects.get(username='user1')
assign_role(u, "test_role")

views.py

class MyObjectViewSet(HasRoleMixin, viewsets.ReadOnlyModelViewSet):
    queryset = MyObject.objects.all()
    serializer_class = MyObjectSerializer
    allowed_roles = ['test_role']

roles.py

class TestRole(AbstractUserRole):
    available_permissions = {
        'list_my_objects': True,
    }

I can successfully login in with user1, but calling the get API over that view results in a 403 Forbidden error.

@filipeximenes
Copy link
Contributor

What authentication methods did you enable in your application? Can you send an example on how you are making the request (how are you passing credentials)?

@StefanoFioravanzo
Copy link
Author

I am using Django Knox for token based authentication. So I perform authentication by retrieving a token using the /login API.

Before testing django-role-permissions I made sure that token authentication worked as expected. I set the following settings:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',)
}

to require authentication for each view. Then sending a request to a view with the token worked as expected.

I did not change the requests format (I'm using Postman and saved them there) when testing Roles.

@StefanoFioravanzo
Copy link
Author

@filipeximenes I think I might have some wrong configuration. I tried the Quick Start guides from the docs with the same roles defined there (Doctor and Nurse) and I always get true when calling has_permission():

>>> user= User.objects.get(username='user1')
>>> assign_role(user, 'doctor')
<class 'config.settings.roles.Doctor'>
>>> from rolepermissions.checkers import has_permission
>>> has_permission(user, 'create_medical_record')
True
>>> has_permission(user, 'edit_patient_file')
True
>>> has_permission(user, 'non_existent_perm')
True

I tried several other attempts both with has_permission and has_role functions and they always return True, even if they should not.

I am using a custom user model with no particular customizations at the moment:

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    ...

To configure django-roles-permissions I included the app in INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'rolepermissions'
]

as the last entry in the array. Should it be included before some other django app?

I also have in my settings base.py:

AUTH_USER_MODEL = 'profiles.User'
ROLEPERMISSIONS_MODULE = 'config.settings.roles'

Any idea on what I could be doing wrong? Thank you!!

@filipeximenes
Copy link
Contributor

filipeximenes commented Apr 24, 2019

@StefanoFioravanzo this is really weird. The only thing that comes to mind is if you are using a superuser:

def has_permission(user, permission_name):
"""Check if a user has a given permission."""
if user and user.is_superuser:
return True
return permission_name in available_perm_names(user)

Can you confirm this is not the case?

@StefanoFioravanzo
Copy link
Author

@filipeximenes Yes, that user was indeed superuser. My bad, while trying out different options to make django roles work I checked the superuser flag and then forgot to remove it.

Now the Quick Start guide example works as expected, though I am still getting the 403 error when using the HasRoleMixin.

One thing I noticed is that request.user in dispatch method of HasRoleMixin returns django.contrib.auth.models.AnonymousUser, that does not pass the authentication barrier.

But if I send a request to a view that does not extend HasRoleMixin I get the correct user. In my case the view subclasses viewsets.ReadOnlyModelViewSet and request.user in the list method of ListModelMixin returns the correct user object.

I think my issue might be outside of the scope of django-role-permissions but I am completely clueless. Do you have any idea of what could cause this?

@filipeximenes
Copy link
Contributor

Very weird. Could you try using a GenericView instead of a ViewSet to see what happens?

@StefanoFioravanzo
Copy link
Author

Hey @filipeximenes , just tried replacing viewsets.ReadOnlyModelViewSet with generics.ListAPIView but I get the same behavior

@colin-byrne-1
Copy link

I had a similar issue (or the same issue), and I fixed it by enabling session authentication as well as token authentication. Without digging much into it, my guess is that the HasRoleMixin is trying to access the user via the session information, and getting anonymous user.

I was using django-rest-auth and had a setting REST_SESSION_LOGIN = False. When I set that to True(which initiates a session when you log in with a token) everything worked as expected.

@aaron-baby
Copy link

I have dig it a bit, IMO the root cause due to HasRoleMixin use Django HttpRequest instead of rest_framework.Request

solution:

in the view
class FakeImportView(ModelViewSet):
    permission_classes = [GroupAPIGETPermission, ]


# define role permission
class GroupBasePermission(BasePermission):
    message = "No group permission"
    group_name = ""
    def has_permission(self, request, view):
        """
        Should simply return, or raise a 403 response.
        """
        try:
            print(request.user)
            request.user.groups.get(name=self.group_name)
        except Group.DoesNotExist:
            return False
        return True


class GroupAPIGETPermission(GroupBasePermission):
    """
        Checks to see if a user is in a particular group
    """

    group_name = "data_importer"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants