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)