I'd like to share with you my Django authorization library (https://github.com/pgorecki/django-cancan) I wrote some time ago and which I'm happily using for couple of my projects now.
My main motivation was to create a rule-based permission system which can be used both for checking per-object access rights and for generating access-based querysets. As a result, you do not need to write your own querysets for retrieving accessible objects, django-cancan
will handle it automatically. For example, cancan can generate a queryset of articles which can be modified by the current user, based on a the user's role or permissions.
Here is an actual usage example from one my latest projects, with chat functionality: the exchange of messages between customers and consultants.
The data model is pretty simple:
class Thread(models.Model):
customer = models.ForeignKey(User, ...)
consultant = models.ForeignKey(User, ...)
class Message(models.Model):
thread = models.ForeignKey(Thread, ...)
content = models.TextField()
First, you define a function that grants actions based on user role.
def define_access_rules(user, rules):
# user == request.user
if not user.is_authenticated:
return
rules.allow("create", Thread)
rules.allow("create", Message)
rules.allow("view", Thread, customer=user)
rules.allow("view", Message, thread__customer=user)
rules.allow("view", Message, thread__consultant=user)
if user.is_staff:
rules.allow("view", Thread, consultant=user)
rules.allow("view", Message)
if user.is_superuser:
rules.allow("view", Thread)
rules.allow("view", Message)
In this example, client (logged-in user) is allowed to create conversation threads, view all threads he started, including all messages within those threads. On top of that, consultant (is_staff=True) can list threads he is assigned to, and superusers have unlimited access to both models.
Note that rules.allow
use the same syntax as Model.objects.filter
.
Then the access control is implemented by overriding the get_queryset
method to return a list of Threads accessible by request.user
and by adding has_permission
method to check if user can perform a view action.
class ThreadList(ListView):
model = Thread
def get_queryset(self):
ability = request.ability
return ability.queryset_for("view", Thread)
class ThreadDetail(PermissionRequiredMixin, DetailView):
model = Thread
def test_func(self):
ability = request.ability
thread = self.get_object()
return ability.can("view", thread)
Check out project README for more examples, including template-level checks, DRF integrations, etc.
I hope you will enjoy django-cancan as much as I do :)
Top comments (0)