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.
rules.allow use the same syntax as
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 :)