Joseph
Joseph

Reputation: 13178

Django guardian: How to give all users the permissions of the anonymous user

I am using django-guardian to implement per-object permissions in my Django project. I've run into an issue where registered users are unable to view objects that anonymous users are able to view. I had thought that if the anonymous user has a permission, then a registered user should have the same permission (I can't imagine a part of my website where I would want anonymous users to be able to do something and registered users not be able to do something).

from core.models import MyObject
from django.contrib.auth.models import User
from guardian.shortcuts import 
from guardian.utils import get_anonymous_user
m = MyObject.objects.get(id=1)
u = User.objects.get(username="MyUser")
anon = get_anonymous_user()
anon.has_perm('view_object', m)
# ^ Prints True
u.has_perm('view_object', m)
# ^ Prints False

In my project, I have some objects that can be "public" or "private". When a user marks the object as "Public", I grant the "view_object" permission to the anonymous user. My view's are protected using the PermissionRequiredMixin, like so:

class MyObjectDetailsView(PermissionRequiredMixin, DetailView):
    model = MyObject
    permission_required = 'view_object'

Does Django Guardian provide some way of giving registered users the same permissions as anonymous users? Or maybe there is some way to subclass the PermissionRequiredMixin to allow the action if the user doesn't have the permission but the anonymous user does?

Upvotes: 5

Views: 1834

Answers (1)

Joseph
Joseph

Reputation: 13178

I've come up with a workaround by subclassing PermissionRequiredMixin like so:

from guardian.mixins import PermissionRequiredMixin
from guardian.utils import get_anonymous_user


class PermissionRequiredMixinWithAnonymous(PermissionRequiredMixin):
    def check_permissions(self, request):
        forbidden = super(PermissionRequiredMixinWithAnonymous, self).check_permissions(request)
        if forbidden:
            perms = self.get_required_permissions(request)
            anon = get_anonymous_user()
            obj = self.get_permission_object()
            has_permissions = all(anon.has_perm(perm, obj) for perm in perms)
            if has_permissions:
                forbidden = None
        return forbidden

If the permission check fails for the logged in user (this is the call to super()), then the check is basically re-run for the anonymous user. I based the code in the if forbidden: block on PermissionRequiredMixin.check_permissions and guardian.utils.get_403_or_None (the latter of which is called from the former). I had to do it this way because both check_permissions and get_403_or_None use request.user.

Upvotes: 4

Related Questions