Skip to main content

Users, Groups and their Permissions in Django + Recipes


Django supports security models and methods out of the box. They are Group and Permission objects. Permission is m2m related to internal Django User. I helps you relay on request.user later in your code.

You often come to situations where you may need a view to be accessed only by certain group of users.  For example you have the app that has two groups of users. One can search and another one can Index files. Simplest approach is to use Groups here. 

In fact you may use permissions in case your app will have several unique users that might do some stuff. In general best approach is to use Group to specify type of users and Permission to specify the role of users in this group. So if you will have Group called 'search' and it will have permission with name, say 'search stuff'. So when you will call:
def my_view(request):    
    # ... my view actions ...    
    user_permissions = request.user.user_permissions.all()
    for p in user_permissions:
        print p
will print something like 'search stuff'. If this request user is in group 'search'. Because Django User is m2m related to Group and Permission objects. So we can create a Group full of Permissions and User within that group will have all those permissions.

Now that we had main idea in head and may think about some tasty and handy stuff for our "most used" needs.

Group required view decorator.

We have several methods/ways here. Brad Montgomery suggested that we will use user passes test decorator. In his Blog. I'll copy and modify his example a bit to use group name used in this article.
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test

@login_required
@user_passes_test(lambda u: u.groups.filter(name='search').count() == 0, login_url='/myapp/denied/')
def some_view(request):    
    # Do whatever this view should do
This stuff may suite you well. BUT imho there is much tastier approach.

There is one view decorator. Django Snippets. It is called view decorator and you can use it like so:
@group_required('admins','editors')
def myview(request, id):
     # ...
And in case stuff will disappear in the net. Here is the snippet itself:

Nice and simple isn't it? If takes a grop name from the list that you provide him and checks user authorship in there.

Programmatically creating groups and users.

Note that Django docs is always the best explanation. But sometimes insufficient for new byes. 
from django.contrib.auth.decorators import user_passes_test

def group_required(*group_names):    
    """Requires user membership in at least one of the groups passed in."""    
     def in_groups(u):       
        if u.is_authenticated():            
            if bool(u.groups.filter(name__in=group_names)) | u.is_superuser:
                return True        
            return False    
    return user_passes_test(in_groups)
Let's start from adding a group. You may use groups to define roles of users.
from django.contrib.auth.models import Group, Permission

different_users = Group(name='Different Users')
different_users.save()
outstanding_users = Group(name='Outstanding Users')
outstanding_users.save()
Now that we have groups set up and ready to go we can setup permissions for them. Django associates permission with model.Models object, but not it's instance. So you need to select a model to operate and  apply a content type to it. Note you may even inven your own content type for those needs.
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

custom_ct = ContentType.objects.get(app_label='app_name',
                                    model='usertype',
                                    name='User Type')
can_search = Permission(name='Can Search',
                       codename='can_search_something',
                       content_type=custom_ct)
can_search.save()
can_index = Permission(name='Can Index',
                       codename='can_index_something',
                       content_type=custom_ct)
can_index.save()
We have earlier defined 2 permissions and can now apply them to groups we have created:
different_users.permissions.add(can_search)
outstanding_users.permissions = [can_search, can_index]
Now we can use our request user to apply groups to him:
def my_view(request):
    user = request.user    
    user.groups.add(different_users)
    # ... or    
    user.groups.add(outstanding_users)
Now it came to:
>>> user.has_perm('app_name.can_search_something')
True
>>> user.has_perm('app_name.can_index_something')
False

# .. or in case of second group authorship
>>> user.has_perm('app_name.can_search_something')
True
>>> user.has_perm('app_name.can_index_something')
True
You can use it wherever you like this. For e.g. in templates:
{% if perms.app_name.can_search_something %}
    Here is your search form.
{% else %}
    Nothing here. Move along!
{% endif %}

Hope some stuff here helps you like it did to me. May the code be with you! Comment me ;)

Comments

  1. Good article! The code that creates the custom permissions have two typos.

    can_view.save() should be can_search.save()
    can_modify.save() should be can_index.save()

    ReplyDelete
    Replies
    1. Oh thanks a million... Updated.
      copy-paste error ;) Great that it helped someone!

      Delete
  2. In which files should I insert @group_required snippet and user groups creating code?

    ReplyDelete
    Replies
    1. And perms from perms.app_name.can_search_something is what kind of variable?
      Thanks please help.

      Delete
    2. in your views.py just above
      @group_required
      def viewname(request): ...

      Delete

Post a Comment

Popular posts from this blog

Pretty git Log

SO you dislike git log output in console like me and do not use it... Because it looks like so: How about this one? It's quite easy... Just type: git log - - graph - - pretty = format : '%Cred%h%Creset -%C ( yellow ) %d%Creset %s %Cgreen ( %cr) %C ( bold blue ) <%an>%Creset' - - abbrev - commit - - It may be hard to enter such an easy command every time. Let's make an alias instead... Copypaste this to your terminal: git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --" And use simple command to see this pretty log instead: git lg Now in case you want to see lines that changed use: git lg - p In order for this command to work remove  the -- from the end of the alias. May the code be with you! NOTE: this article is a rewritten copy of  http://coderwall.com/p/euwpig?i=3&p=1&t=git   and have b...

Django: Resetting Passwords (with internal tools)

I have had a task recently. It was about adding a forms/mechanism for resetting a password in our Django based project. We have had our own registration system ongoing... It's a corporate sector project. So you can not go and register yourself. Admins (probably via LDAP sync) will register your email/login in system. So you have to go there and only set yourself a password. For security reasons you can not register. One word. First I've tried to find standart decision. From reviewed by me were: django-registration and django password-reset . These are nice tools to install and give it a go. But I've needed a more complex decision. And the idea was that own bicycle is always better. So I've thought of django admin and that it has all the things you need to do this yourself in no time. (Actually it's django.contrib.auth part of django, but used out of the box in Admin UI) You can find views you need for this in there. they are: password_reset password_reset_...

Vagrant error: * Unknown configuration section 'hostmanager'.

Sometimes you get a vagrant environment or boilerplate with a Vagrantfile config in there and do a vagrant up command. And see some errors. like this: There are errors in the configuration of this machine . Please fix the following errors and try again : Vagrant: * Unknown configuration section 'hostmanager'. To fix this one needs: $ vagrant plugin install vagrant - hostmanager Installing the ' vagrant-hostmanager ' plugin . This can take a few minutes . . . Fetching : vagrant - hostmanager - 1.8 .6 . gem ( 100 % ) Installed the plugin ' vagrant-hostmanager (1.8.6) ' ! So command to fix this as follows: vagrant plugin install vagrant-hostmanager