Reputation: 13178
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
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