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

Allow separate permissions per View in ViewSet #1067

Closed
Anton-Shutik opened this issue Aug 28, 2013 · 14 comments
Closed

Allow separate permissions per View in ViewSet #1067

Anton-Shutik opened this issue Aug 28, 2013 · 14 comments

Comments

@Anton-Shutik
Copy link
Contributor

Hi guys,
I've just updated to the latest version(2.3.7) and was exited! It is awesome!

My question is:
Can I specify permission_classes per view in ViewSet?
I want allow ALL users to create instances, but only AUTHENTICATED can view them.Please see code below:

class MyModelViewSet(viewsets.ModelViewSet):
    authentication_classes = (SessionAuthentication, )
    permission_classes = (IsAuthenticated, )

    model = MyModel
    serializer_class = MyModelSerializer

    def retrieve(self, request, *args, **kwargs):
        #retrieve code goes here

    def update(self, request, *args, **kwargs):
        #update code goes here

    @decorators.action(permission_classes=(AllowAny, ))
    def create(self, request, *args, **kwargs):
        #how to allow UNAUTHENTICATED users access the url?

I use the approach registering customers. Is there any feature that can help me? The code above actually not working, it checks only "IsAuthenticated" permission, so only authenticated users can access "create" view.

@tomchristie
Copy link
Member

Can I specify permission_classes per view in ViewSet?

You could override .get_permissions() if needed, see here, and brief mention in docs. If you checked request.kwargs you'd be able to determine if you're in a list or detaill type view and modify which permissions to return accordingly

However the more sensible way to tackle this would be to create a custom permission class that allows POST requests from any user and only allows other other requests from authenticated users.

Take a look at the custom permissions docs and consider overriding IsAuthenticated to a custom IsCreationOrIsAuthenticated.

Incidentally, usage questions are probably best directed at the discussion group.

Hope that helps! :)

@Anton-Shutik
Copy link
Contributor Author

IsCreationOrIsAuthenticated - great idea!

Thanks :)

@kot-behemoth
Copy link

I've been looking at how to implement this for a while, and the crucial thing to know is that self on .get_permissions() has an action property which actually holds the name of the method that was called, so it's possible to switch on that.

However, a IsCreationOrIsAuthenticated is certainly a useful one (I'd probably call it IsAuthenticatedOrWriteOnly), so here's a very simple implementation for people coming here from googling:

class IsCreationOrIsAuthenticated(permissions.BasePermission):

    def has_permission(self, request, view):
        if not request.user.is_authenticated():
            if view.action == 'create':
                return True
            else:
                return False
        else:
            return True

@me21
Copy link

me21 commented Sep 19, 2017

Why downvote the last answer?

@xordoquy
Copy link
Collaborator

The code example violates the separation of concerns that DRF tries to isolate.
This being said, I'd rather have someone explaining than just down voting :(

@assembledadam
Copy link

assembledadam commented Nov 1, 2017

Alternatively (for Googlers etc), here's a couple of custom permissions classes with more restrictions than @kot-behemoth's suggestion (which when used alone, allows for any authenticated user to view/update/delete any user's records).

When used together, these permissions permit:

  • Anonymous create/POST
  • Logged-in users to retrieve/GET and update/PUT/PATCH their own records (and nobody else's)
  • All actions for staff/admin users

They disallow:

  • Deletion/DELETE of any records unless staff/admin
  • Viewing/GET all records (list) unless staff/admin

I would expect this to be a fairly common requirement of a User API endpoint.

Note: AnonCreateAndUpdateOwnerOnly relies on each record having an 'id' field. For an endpoint other than User, just change this whatever your record's user ownership field is (e.g. 'owner').

class AnonCreateAndUpdateOwnerOnly(permissions.BasePermission):
    """
    Custom permission:
        - allow anonymous POST
        - allow authenticated GET and PUT on *own* record
        - allow all actions for staff
    """

    def has_permission(self, request, view):
        return view.action == 'create' or request.user and request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        return view.action in ['retrieve', 'update', 'partial_update'] and obj.id == request.user.id or request.user.is_staff

class ListAdminOnly(permissions.BasePermission):
    """
    Custom permission to only allow access to lists for admins
    """

    def has_permission(self, request, view):
        return view.action != 'list' or request.user and request.user.is_staff

@slyapustin
Copy link
Contributor

@assembledadam
That part should be updated:
and obj.id == request.user.id
to:
and obj.user.id == request.user.id
And empty user field checks should be added too.

@assembledadam
Copy link

In my case, this permission is exclusively used on the 'User' endpoint (model), so the code is correct for that. It needs to be slightly modified to match your fields if not for User, as mentioned in the italics section of the post.

@Allan-Nava

This comment has been minimized.

@rpkilby

This comment has been minimized.

@Allan-Nava

This comment has been minimized.

@rpkilby

This comment has been minimized.

@serpulga
Copy link

If overriding the permission_classes of the ViewSet isn't the behavior of @action(permission_classes=(,)), then what is this keyword argument of the action decorator used for?

@xordoquy
Copy link
Collaborator

@serpulga @action decorator is not meant to be used with the generic methods such as create / list / retrieve / update / delete / partial_update.
However that's the way to go for extra actions.

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

10 participants