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

Django: Resetting Passwords (with internal tools)

Time Capsule for $25

Vagrant error: * Unknown configuration section 'hostmanager'.