Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django queryset permissions

Tags:

python

django

I am building a quite complex Django application to be used on top of and email scanning service. The Django application is written using Python 3.5+

This application primarily uses Django Rest Framework to handle communication with the frontend in the browser.

The issue that I am currently having is that I try to implement the concept of a System Administrator, Domain Administrator and Application User

The System Administrator is basically the "normal" django superuser and is therefore capable of doing everything and see every record in the system.

The Domain Administrator is user who manages one or more email domains. I keep track of this using a Many2Many relationship between the users and the domains. The idea would then be to predefine a filter, so that the log of messages processed, will be automatically filtered to show only messages where the sender domain or the recipient domain equal a domain in the list of domains that the given user is assigned to.

The same would be true for blacklisting/whitelisting policies.

If the Domain Administrator is not assigned to any domains, then no data is shown.

The Application User is basically any authenticated user with one or more domains assigned to them, using the same Many2Many relationship as the Domain Administrator. If no domains are assigned, then no data is shown.

I have found some other solution here on Stackoverflow on making the request.user available to the QuerySet in the ModelManager, but that does not seem like the correct way to handle it.

I have looked at django-guardian, django-authority and django-permissions, but none of them seem to be affecting the QuerySet or the resulting list of objects.

Does anyone have a suggestion for Django package/addon that can be used to handle this or maybe an idea for how this could be handled?

like image 301
Kenneth_H Avatar asked May 18 '18 15:05

Kenneth_H


People also ask

How do I give permission in Django?

With Django, you can create groups to class users and assign permissions to each group so when creating users, you can just assign the user to a group and, in turn, the user has all the permissions from that group. To create a group, you need the Group model from django. contrib. auth.

What are object level permissions Django?

Object Permissions allow you to assign a permission to an instance of any Model in your django project. This app provides a authentication backend that works with Django >= 1.2. This specific implementation includes the ability to assign permissions to Users and UserGroups.

What is permissions Safe_methods?

Requests for unauthorised users will only be permitted if the request method is one of the "safe" methods; GET , HEAD or OPTIONS . This permission is suitable if you want to your API to allow read permissions to anonymous users, and only allow write permissions to authenticated users.

What is QuerySet?

A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.


2 Answers

DRF's GenericAPIView has a get_queryset method that you can override to perform custom filtering:

def get_queryset(self):
    qs = super(YourView, self).get_queryset()
    return self.filter_queryset_for_user(qs, request.user)


def filter_queryset_for_user(self, qs, user):
    pass  # Your logic here

This is not necessarily a bad idea; DRF docstrings recommend overriding this:

You may want to override this if you need to provide different querysets depending on the incoming request.

like image 62
rtindru Avatar answered Sep 28 '22 20:09

rtindru


I'm the author of django-cancan library https://github.com/pgorecki/django-cancan which strives to solve the exact problem you are describing.

The philosophy is as following: first, you determine per-user abilities, then in a view, you can check user abilities for a given object, model, or you can retrieve a queryset based on those abilities.

The declaration part looks like this:

def declare_abilities(user, ability):
    if not user.is_authenticated:
        # Allow anonymous users to view only published articles
        ability.can('view', Article, published=True)
    else:
        # logged in user can view any article...
        ability.can('view', Article)
        # ... and change his own
        ability.can('change', Article, author=user)
        # ... and add new ones
        ability.can('add', Article)

    if user.is_superuser:
        # Allow superuser to view and change any article
        ability.can('view', Article)
        ability.can('change', Article)

Then you can you can check for abilites on a per-object level:

def article_detail_view(request, pk):
    article = Article.objects.get(pk=pk)
    if request.ability.can('view', article):
        ...

or on a model level:

def article_create_view(request, pk):
    if request.ability.can('add', Article):
        ...

or get a queryset with accessible objects:

def another_list_view(request, pk):
    articles = request.ability.queryset_for('view', Article)
    ...
like image 22
pgorecki Avatar answered Sep 28 '22 20:09

pgorecki