Per object permissions for Django

Overview

django-guardian

https://travis-ci.org/django-guardian/django-guardian.svg?branch=devel

django-guardian is an implementation of per object permissions [1] on top of Django's authorization backend

Documentation

Online documentation is available at https://django-guardian.readthedocs.io/.

Requirements

  • Python 3.5+
  • A supported version of Django (currently 2.2+)

Travis CI tests on Django version 2.2, 3.0, 3.1, and master.

Installation

To install django-guardian simply run:

pip install django-guardian

Configuration

We need to hook django-guardian into our project.

  1. Put guardian into your INSTALLED_APPS at settings module:
INSTALLED_APPS = (
 ...
 'guardian',
)
  1. Add extra authorization backend to your settings.py:
AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend', # default
    'guardian.backends.ObjectPermissionBackend',
)
  1. Create guardian database tables by running:

    python manage.py migrate
    

Usage

After installation and project hooks we can finally use object permissions with Django.

Lets start really quickly:

>>> from django.contrib.auth.models import User, Group
>>> jack = User.objects.create_user('jack', '[email protected]', 'topsecretagentjack')
>>> admins = Group.objects.create(name='admins')
>>> jack.has_perm('change_group', admins)
False
>>> from guardian.models import UserObjectPermission
>>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
<UserObjectPermission: admins | jack | change_group>
>>> jack.has_perm('change_group', admins)
True

Of course our agent jack here would not be able to change_group globally:

>>> jack.has_perm('change_group')
False

Admin integration

Replace admin.ModelAdmin with GuardedModelAdmin for those models which should have object permissions support within admin panel.

For example:

from django.contrib import admin
from myapp.models import Author
from guardian.admin import GuardedModelAdmin

# Old way:
#class AuthorAdmin(admin.ModelAdmin):
#    pass

# With object permissions support
class AuthorAdmin(GuardedModelAdmin):
    pass

admin.site.register(Author, AuthorAdmin)
[1] Great paper about this feature is available at djangoadvent articles.
Comments
  • get_objects_for_user with any_perm=True leads to type error in postgres.

    get_objects_for_user with any_perm=True leads to type error in postgres.

    This, I believe, is related to object_pk being a VARCHAR(255), whereas the id fields are usually INT types in the UserObjectPermission model

    A sample of query being generated which is failing (at the "app_model"."id" IN clause). The query is relatively longer. I am posting only a part of it.

    SELECT <Fields> FROM "report_report"   
    WHERE ("app_model"."id" IN (  
          SELECT U0."object_pk" FROM "guardian_userobjectpermission" U0   
         INNER JOIN "auth_permission" U2 ON ( U0."permission_id" = U2."id" )   
         WHERE (U0."user_id" = 11 AND U2."content_type_id" = 10 AND U2."codename" IN (abc, xyz, efg)))
    

    Terminal error:

    psycopg2.ProgrammingError: operator does not exist: integer = character varying
    LINE 1: ... FROM "app_model" WHERE ("app_model"."id" IN (SELECT...
                                                                 ^
    HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
    

    Stack: Django==1.8.7 django-guardian==1.3.2 python==3.4.3 Linux Mint==17.2 (64-bit) Postgres==9.3.10

    opened by liquidscorpio 41
  • user.has_perm(

    user.has_perm("perm", obj) behaves unexpectedly

    If I use standard user.has_perm("perm") method, then it will return True only, if user has a global permission "perm". And if user.has_perm("perm", obj) is used, it'll teturn True if user have permission to access this particular object. But it will return False, even if user has a global permission "perm", which is quite unexpected for me, because I assume, that having global permission should give user access to all objects. Am I right?

    Enhancement 
    opened by Dzejkob 28
  • Permission for specific object in AdminPanel

    Permission for specific object in AdminPanel

    Hi, is possibile have the same feature of this example:

    >>> from django.contrib.auth.models import User, Group
    >>> jack = User.objects.create_user('jack', '[email protected]', 'topsecretagentjack')
    >>> admins = Group.objects.create(name='admins')
    >>> jack.has_perm('change_group', admins)
    False
    >>> from guardian.models import UserObjectPermission
    >>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
    <UserObjectPermission: admins | jack | change_group>
    >>> jack.has_perm('change_group', admins)
    True
    
    

    Inside the Panel admin?

    Thanks for support. Best Regards, Allan

    opened by Allan-Nava 21
  • LoginRequired and PermissionRequired view mixins

    LoginRequired and PermissionRequired view mixins

    Using the django's or guardian's 'loginrequired' and 'permissionrequired' decorators with the new class based views is tricky and cumbersome - django documetations shows some pointers here: https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating-class-based-views

    A different approach would to use view mixins which in my opinion is the easiest and most readable solution. An example class based view with a permission required check could look like this:

    class FitterEditView(PermissionRequiredMixin, UpdateView):
        """
        ...
        """
    
        ### PermissionRequiredMixin settings
        permission_required = 'fitters.change_fitter'
    
        ### UpdateView settings
        context_object_name="fitter"
        queryset = Fitter.objects.all()
        form_class = FitterForm
        ...
    

    Below is my first attempt at these mixins created for a project I am currently working on. These mixins are designed to work both with vanilla django or with django-guradian. If this is something you would consider including in the future I would love to assist.

    On the final note I want to say thank you for this fantastic app which I have been using it since ver 0.2.

    class LoginRequiredMixin(object):
        """ 
        A login required mixin for use with class based views. This Class is a light wrapper around the
        `login_required` decorator and hence function parameters are just attributes defined on the class.
    
        Due to parent class order traversal this mixin must be added as the left most 
        mixin of a view.
    
        The mixin has exaclty the same flow as `login_required` decorator:
    
            If the user isn't logged in, redirect to settings.LOGIN_URL, passing the current 
            absolute path in the query string. Example: /accounts/login/?next=/polls/3/.
    
            If the user is logged in, execute the view normally. The view code is free to 
            assume the user is logged in.
    
        **Class Settings**
            `redirect_field_name - defaults to "next"
            `login_url` - the login url of your site
    
        """
        redirect_field_name = REDIRECT_FIELD_NAME
        login_url = None
    
        @method_decorator(login_required(redirect_field_name=redirect_field_name, login_url=login_url))
        def dispatch(self, request, *args, **kwargs):
            return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
    
    class PermissionRequiredMixin(object):
        """ 
        A view mixin that verifies if the current logged in user has the specified permission 
        by wrapping the ``request.user.has_perm(..)`` method.
    
        If a `get_object()` method is defined either manually or by including another mixin (for example
        ``SingleObjectMixin``) or ``self.object`` is defiend then the permission will be tested against 
        that specific instance.
    
        .. NOTE: Testing of a permission against a specific object instance requires an authentication backend
                 that supports. Please see ``django-guardian`` to add object level permissions to your project.  
    
        The mixin does the following:  
    
            If the user isn't logged in, redirect to settings.LOGIN_URL, passing the current 
            absolute path in the query string. Example: /accounts/login/?next=/polls/3/.
    
            If the `raise_exception` is set to True than rather than redirect to login page
            a `PermisionDenied` (403) is raised.
    
            If the user is logged in, and passes the permission check than the view is executed
            normally.
    
        **Example Usage**
    
            class FitterEditView(PermissionRequiredMixin, UpdateView):
                ...
                ### PermissionRequiredMixin settings
                permission_required = 'fitters.change_fitter'
    
                ### other view settings
                context_object_name="fitter"
                queryset = Fitter.objects.all()
                form_class = FitterForm
                ...
    
        **Class Settings**
            `permission_required` - the permission to check of form "<app_label>.<permission codename>"
                                    i.e. 'polls.can_vote' for a permission on a model in the polls application.
    
            `login_url` - the login url of your site
            `redirect_field_name - defaults to "next"
            `raise_exception` - defaults to False - raise PermisionDenied (403) if set to True
    
        """
        ### default class view settings
        login_url = settings.LOGIN_URL
        raise_exception = False
        permission_required = None
        redirect_field_name=REDIRECT_FIELD_NAME
    
        def dispatch(self, request, *args, **kwargs):
            # call the parent dispatch first to pre-populate few things before we check for permissions
            original_return_value = super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)
    
            # verify class settings
            if self.permission_required == None or len(self.permission_required.split('.')) != 2:
                raise ImproperlyConfigured("'PermissionRequiredMixin' requires 'permission_required' attribute to be set to '<app_label>.<permission codename>' but is set to '%s' instead" % self.permission_required)
    
            # verify permission on object instance if needed
            has_permission = False
            if hasattr(self, 'object')  and self.object is not None: 
                has_permission = request.user.has_perm(self.permission_required, self.object)
            elif hasattr(self, 'get_object') and callable(self.get_object):
                has_permission = request.user.has_perm(self.permission_required, self.get_object())
            else:
                has_permission = request.user.has_perm(self.permission_required)
    
            # user failed permission
            if not has_permission:
                if self.raise_exception:
                    return HttpResponseForbidden()
                else:
                    path = urlquote(request.get_full_path())
                    tup = self.login_url, self.redirect_field_name, path
                    return HttpResponseRedirect("%s?%s=%s" % tup)
    
            # user passed permission check so just return the result of calling .dispatch()
            return original_return_value
    
    opened by danielsokolowski 21
  • Added method to prefetch the permissions for an iterable of objects

    Added method to prefetch the permissions for an iterable of objects

    The permissions are then stored in the cache.

    This helps reduce the number of database calls required when getting the permissions on a list of objects.

    Performance 
    opened by keattang 17
  • Django 1.8 GuardedModelAdminMixin.get_queryset exception

    Django 1.8 GuardedModelAdminMixin.get_queryset exception

    Hi,

    I'm using django-guardian (through django-userena) with Django 1.8. When trying to access User model in django admin, GuardedModelAdminMixin raises the following Exception:

    'super' object has no attribute 'queryset'

    In GuardedModelAdminMixin.get_queryset the problematic code is:

        def get_queryset(self, request):
            # Prefer the Django >= 1.6 interface but maintain
            # backward compatibility
            method = getattr(
                super(GuardedModelAdminMixin, self), 'get_queryset',
                super(GuardedModelAdminMixin, self).queryset)
    
    

    In Django 1.8 (I guess in all Django >= 1.6) queryset method does not exist, but this part

    super(GuardedModelAdminMixin, self).queryset is evaluated and raising Exception even though get_queryset method exists and it's found.

    opened by lalstef 17
  • Allow specifying an empty list of permissions for get_objects_for_user

    Allow specifying an empty list of permissions for get_objects_for_user

    This allows one to ask for all objects for which an user has any permission. In a way it mirrors get_users_with_perms.

    In addition, I fixed how the final query is made. Instead of creating an in-memory list of all IDs and then passing it in, you can pass all IDs simply as a subquery. This both improves the query and also allows more complicated basic queries (I was getting an error that I cannot use OR for two queries with "extra"). Also, it works now with documents which do not have integers as primary keys (I have UUIDs and int(v) does not work).

    Fixes #244.

    opened by mitar 17
  • Is this Django 1.8 ready?

    Is this Django 1.8 ready?

    I have never used django-guardian, and i am about to start a new project using Django 1.8 and was thinking about using it.

    i see many issues regarding erros when testing with Django 1.8 so my question is should i be using this right away? If not how long more or less till the official 1.8 version??

    thanks guys

    opened by psychok7 16
  • Update CHANGES for v2.3.0

    Update CHANGES for v2.3.0

    I tried the following picking list for the release, so the cherry-pick commands would apply successfully:

    git cherry-pick 9206ee4b32b3c97a7bc60440ce8221d483f1a24a
    git cherry-pick 448a6149be8de47a62bd31c71eee1d94e688c5ef
    git cherry-pick b12212babd8736015cd5b97a0a45dcc16c58c8a2
    git cherry-pick 414d3a4a7bbd0ac4c2ddac85e0dcc098cf130ca1
    git cherry-pick b338092f829b631cd59827bf29171f1ab405ff18
    git cherry-pick 8e8dab207296ee37aa1d19eaeebfad7d0642f138
    git cherry-pick 9c981804bbebe9d91f1d4d78292bedf292745986
    git cherry-pick d816cfe4dad04dca78417d9c8613b49822256c60
    
    opened by lnagel 14
  • Any staff user can gain permissions

    Any staff user can gain permissions

    Guardian admin views for managing object permissions does not check for user permission to make any changes in admin site. Any user logged in to admin site can access URL like http://127.0.0.1:8080/admin/main/complex/2/permissions/ and give change permissions to himself. Hows that possible? I'm using Django 1.7.7, guardian 1.2.5 with python 3.4

    Bug 
    opened by Sovetnikov 14
  • Cast object_pk to UUIDField when PK is a UUID

    Cast object_pk to UUIDField when PK is a UUID

    This fixes

    ProgrammingError: operator does not exist: uuid = character varying

    when having UUIDs as primary keys.

    UUIDs used to work up until 2.1, but when I upgraded to 2.2 it threw the above error. PR #637 changed the way the object_pk was casted/compared, but only for Integer/AutoField PKs. This PR handles UUIDs as well.

    opened by richarddewit 13
  • Support `only_with_perms_in` in `shortcuts.get_groups_with_perms`

    Support `only_with_perms_in` in `shortcuts.get_groups_with_perms`

    There is a filter option only_with_perms_in for shortcuts.get_users_with_perms.

    It would be nice to have the same filter option also for shortcuts.get_groups_with_perms.

    opened by Cornelicorn 0
  • get_perms returns empty list while get_user_perms returns permissions

    get_perms returns empty list while get_user_perms returns permissions

    I wrote a script to give me all objects which don't have enough permissions. I used shortcuts get_perms to check the permissions for the user and I found some entries because they returned []. But on closer inspection, I can see the obejcts have permissions and they are returned when I use get_user_perms.

    Shouldn't get_perms return a superset of get_user_perms?

    Here is my script, using guardian 2.4.0

    from guardian.shortcuts import get_user_perms, get_perms
    from app.models import PreferencesRecord
    
    for pref in tqdm.tqdm(PreferencesRecord.objects.all()):
        perms = get_perms(pref.user, pref)
        if len(perms) < 4:
            print(f"User {pref.user.username} ({pref.user.id}) has {len(perms)} permissions on {pref.id}")
            user_perms = get_user_perms(pref.user, pref)
            if user_perms != perms:
                print(f"But the user permissions return {user_perms}, while perms are {perms}")
    
    opened by BSVogler 0
  • How to suggest a selection at dropdown menu on the admin site

    How to suggest a selection at dropdown menu on the admin site

    Hi folks,

    I am try to suggest my favorite permission at admin site when adding a permission (dop down menu). How can I do that?

    image

    admin.py

    class DeploymentUserObjectPermissionAdmin(GuardedModelAdmin):
        list_display = ('permission', 'user', 'content_object')
        search_fields = ('permission', 'user', 'content_object')
        ordering = ('-permission',
    

    models.py

    class DeploymentUserObjectPermission(UserObjectPermissionBase):
        content_object = models.ForeignKey(Deployment, on_delete=models.CASCADE)
    
    opened by bauergeorg 2
  • Checking permission of tweaked direct-foreign-keys

    Checking permission of tweaked direct-foreign-keys

    Hi folks,

    I found that wonderfull example to add a special key to the permission.

    Here is my example (users/models.py):

    class DeploymentUserObjectPermission(UserObjectPermissionBase):
        content_object = models.ForeignKey(Deployment, on_delete=models.CASCADE)
    
    class DeploymentGroupObjectPermission(GroupObjectPermissionBase):
        content_object = models.ForeignKey(Deployment, on_delete=models.CASCADE)
    
    class ProjectUserObjectPermission(UserObjectPermissionBase):
        content_object = models.ForeignKey(Project, on_delete=models.CASCADE)
        type = models.ForeignKey(DeploymentType, on_delete=models.CASCADE)
    
    class ProjectGroupObjectPermission(GroupObjectPermissionBase):
        content_object = models.ForeignKey(Project, on_delete=models.CASCADE)
        type = models.ForeignKey(DeploymentType, on_delete=models.CASCADE)
    

    Here are the models of my app: (software/models.py):

    # Release, RC, Develop, Test
    class DeploymentType(models.Model):
    	name = models.CharField(max_length=100, default='unknown')
    	description = models.TextField()
    	image = models.ImageField(default='default_deploymenttype.jpg', upload_to='deploymenttype_pics')
    
    	def __str__(self):
    		return self.name
    
    class Project(models.Model):
    	title = models.CharField(max_length=100)
    	description = models.TextField()
    	date_created = models.DateTimeField(default=timezone.now)
    	author = models.ForeignKey(User, on_delete=models.CASCADE)
    	image = models.ImageField(default='default_project.jpg', upload_to='project_pics')
    
    	def __str__(self):
    		return self.title
    
    class Deployment(models.Model):
    	title = models.CharField(max_length=100)
    	project = models.ForeignKey(Project, on_delete=models.CASCADE)
    	file = models.FileField(null=True,blank=True,upload_to='Files')
    	content = models.TextField()
    	date_posted = models.DateTimeField(default=timezone.now)
    	author = models.ForeignKey(User, on_delete=models.CASCADE)
    	type = models.ForeignKey(DeploymentType, on_delete=models.CASCADE)
    
    	def __str__(self):
    		return self.title
    
    

    A Deployment is a part of a Project. Every Deployment has a type (RC, Release, Test, Develop, ...).

    I wan't to filter List Views and grant access by setting special permissions. But how? Can someone help me?

    opened by bauergeorg 2
A flask extension for managing permissions and scopes

Flask-Pundit A simple flask extension to organize resource authorization and scoping. This extension is heavily inspired by the ruby Pundit library. I

Anurag Chaudhury 39 Jan 23, 2021
An enhanced permission system which support object permission in Django

django-permission Author Alisue <[email protected]> Supported python versions Python 2.7, 3.3, 3.4, 3.5, 3.6 Supported django versions Django 1

Alisue 299 Dec 6, 2022
Django CAS 1.0/2.0/3.0 client authentication library, support Django 2.0, 2.1, 2.2, 3.0 and Python 3.5+

django-cas-ng django-cas-ng is Django CAS (Central Authentication Service) 1.0/2.0/3.0 client library to support SSO (Single Sign On) and Single Logou

django-cas-ng 347 Dec 18, 2022
Django-registration (redux) provides user registration functionality for Django websites.

Description: Django-registration provides user registration functionality for Django websites. maintainers: Macropin, DiCato, and joshblum contributor

Andrew Cutler 920 Jan 8, 2023
Complete Two-Factor Authentication for Django providing the easiest integration into most Django projects.

Django Two-Factor Authentication Complete Two-Factor Authentication for Django. Built on top of the one-time password framework django-otp and Django'

Bouke Haarsma 1.3k Jan 4, 2023
Django Admin Two-Factor Authentication, allows you to login django admin with google authenticator.

Django Admin Two-Factor Authentication Django Admin Two-Factor Authentication, allows you to login django admin with google authenticator. Why Django

Iman Karimi 9 Dec 7, 2022
Django-react-firebase-auth - A web app showcasing OAuth2.0 + OpenID Connect using Firebase, Django-Rest-Framework and React

Demo app to show Django Rest Framework working with Firebase for authentication

Teshank Raut 6 Oct 13, 2022
Object Moderation Layer

django-oml Welcome to the documentation for django-oml! OML means Object Moderation Layer, the idea is to have a mixin model that allows you to modera

Angel Velásquez 12 Aug 22, 2019
Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.

Welcome to django-allauth! Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (soc

Raymond Penners 7.7k Jan 1, 2023
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

Simple JWT 3.3k Jan 1, 2023
REST implementation of Django authentication system.

djoser REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such

Sunscrapers 2.2k Jan 1, 2023
Authentication Module for django rest auth

django-rest-knox Authentication Module for django rest auth Knox provides easy to use authentication for Django REST Framework The aim is to allow for

James McMahon 878 Jan 4, 2023
Authentication for Django Rest Framework

Dj-Rest-Auth Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well with SPAs (e.g React, Vue, Ang

Michael 1.1k Jan 3, 2023
Get inside your stronghold and make all your Django views default login_required

Stronghold Get inside your stronghold and make all your Django views default login_required Stronghold is a very small and easy to use django app that

Mike Grouchy 384 Nov 23, 2022
JSON Web Token Authentication support for Django REST Framework

REST framework JWT Auth Notice This project is currently unmaintained. Check #484 for more details and suggested alternatives. JSON Web Token Authenti

José Padilla 3.2k Dec 31, 2022
Awesome Django authorization, without the database

rules rules is a tiny but powerful app providing object-level permissions to Django, without requiring a database. At its core, it is a generic framew

null 1.6k Dec 30, 2022
An extension of django rest framework, providing a configurable password reset strategy

Django Rest Password Reset This python package provides a simple password reset strategy for django rest framework, where users can request password r

Anexia 363 Dec 24, 2022
This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)

Welcome to django-rest-auth Repository is unmaintained at the moment (on pause). More info can be found on this issue page: https://github.com/Tivix/d

Tivix 2.4k Jan 3, 2023
Authentication for Django Rest Framework

Dj-Rest-Auth Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well with SPAs (e.g React, Vue, Ang

Michael 1.1k Jan 3, 2023