Group permission implementation

Wondering how to implement a Group restrictions on my Django site. I have function based views so I believe I need to;

from django.contrib.auth.decorators import permission_required

@permission_required('polls.add_choice')
def my_view(request):
    ...

As mentioned in the docs.

My question is regarding the syntax of the values in the brackets.
If my group is named coaches and I have the permissions set in the group what goes in the brackets?

As you can see my group “coaches” has three permissions. I can’t locate anywhere in the Django docs that has an example of how to implement the group. Like:

@permission_required('coaches')
def my_view(request):
    ...

Know what I mean?

You don’t check for permissions by group, you check permissions by permission. The groups are granted permissions, allowing members of the group to access different resources with different permission names.

So your my_view view might require the can view player. A view called post_view may require the can view post permission.

This gives you the flexibility to create a “coaches” group with both permissions, but a “player” group with only the can view player permission.

What I was hoping to do is restrict the group using tags in the template the same way its done with

{% if user.is_authenticated %}
 ... stuff they can't see here ...
{% endif %}

Do I need to create custom tags for this?

Don’t think “groups”, think “permissions”. A “permission” exists to allow access to be granted to one or more User and Group objects.

A “permission” can be checked to allow a section of a template to be rendered.

See Authentication data in templates for details.

I’m still confused. I create a group, assign permissions to the group, put users in the group, correct?
So what I want to do is restrict the group access. For instance I have a public group, they should not see our tryout roster. That’s for the coach group. So I want to restrict the tryouts navbar links so that only the coach group can see them. But your saying I can only restrict the users using their permissions not the group that their in. Seems like I have to implement group permissions with some extra code.
I was thinking of permission groups like a SharePoint implementation.

How about this…Using permissions would I do something like

{% if perms.tryouts.player.can_vew_player %}
links
{% endif %}

Still struggling with the point of groups. I mean I get what there supposed to do but I don’t get how to do it. If that makes sense.

But you don’t restrict access by group. You restrict (or allow) access by Permission.

A person has all permissions assigned directly to them, in addition to all permissions assigned to all groups they are members of. The purpose of groups then is to serve as a “collection of permissions”. If you’ve got 50 people who all need the same set of permissions, you don’t want to manage 50 sets of permission assignments. Create a group with that set of permissions, and assign the 50 people to that group. If that group needs 15 different permissions, then you grant all 15 permissions to that group.

The idea is that this provides you with far more granularity and flexibility with managing access.

If you are familiar with working with a Role-Based Access Control system, think “Role = Group”.

So, looking at your example:

Ok, so the “view tryouts” permission is only assigned to the Coach group. Every member of the Coach group is then going to (implicitly) have the “view tryouts” permission.

The permissions are the permissions associated with the resource so it might be something like:
{% if perms.tryouts.can_vew_tryout_menu %} (Or whatever permission you wish to create / use for this purpose. If you wish to use an existing permission, that’s entirely valid. That decision should be based on “business requirements” - there’s no technical reason to choose one option over the other.)

I do understand the group philosophy. But not the syntax required to implement. I’ll give the template variable perms a shot. I think the two level attribute lookup is what is required here.

User.has_perm()

Thanks for the help Ken. BTW, I enjoyed your interview on Django Chat. Those podcasts are great. I listen to them most day’s when I walk my dog.

I don’t know what you mean by “two level attribute lookup”, but if you’re referring to what you see in the Admin screen vs what I posted, no. The display format shown in the admin screen uses different fields of the Permission model than what you use to reference that permission in the code.

Permissions

Yes, that would be the appropriate use.

Thanks @KenWhitesell for the insight, however I have same issue which I didn’t found the solution. suppose that I have a User u2 which is related to a groupt test_g, however I created a permission and assigned it to test_g not u2 directly.
my issue is, in my case I want to use a decorator like permission_required to check if the request.user.group => user_g has this specific permission access to be granted otherwise not. but permission_requried just checks permissions assigned to Users not Groups.
I know I can use user_passes_test django decorator for this purpose but isn’t there other decorator or solution for django functions based views?

u2 = User.objects.get('myuser')

u2.groups.all()
<QuerySet [<Group: test_g>]>

u2.groups.first().permissions.all()
<QuerySet [<Permission: auth | گروه | Can view test tab>, <Permission: auth | گروه | Can you see amir>]>

# this permission <Permission: auth | گروه | Can you see amir> has shot_code of "can_you_see_amir"

u2.has_perm('can_you_see_amir')
False

same issue is for view


@permission_required('can_you_see_amir')  #this will return false because group permission doesnt check
def test_view():
    .
    .
    .

That is not an accurate statement. The permission_required checks for permissions assigned to the User, in addition to the permissions assigned to the Groups that the user belongs to. If the User is a member of Group_g, and the permission is assigned to Group_g, then the User would pass the permission_required check.

1 Like

I wanted to implement this as well, so I created my own custom mixin, just like LoginRequiredMixin.

Here’s the mixin.py code

from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import PermissionRequiredMixin


# GroupRequiredMixin
class GroupRequiredMixin:
    required_groups = []

    def has_group_permissions(self):
        user = self.request.user
        return any(group in user.groups.values_list('name', flat=True) for group in self.required_groups)

    def dispatch(self, request, *args, **kwargs):
        if not self.has_group_permissions():
            raise PermissionDenied()
        return super().dispatch(request, *args, **kwargs)


class CustomGroupRequiredMixin(GroupRequiredMixin, PermissionRequiredMixin):
    def has_permission(self):
        if self.has_group_permissions():
            return True
        return super(PermissionRequiredMixin, self).has_permission()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # set the required_groups attribute if not already set
        if not hasattr(self, 'required_groups'):
            self.required_groups = []

Here’s the implementation within the view:

class MyView(LoginRequiredMixin, CustomGroupRequiredMixin, View):
    required_groups = ["coaches"]
    ...

If you’re using method-based views it then use the mixin as a decorator.
Here’s the decorator code:

from functools import wraps


def custom_group_required(required_groups):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            user = request.user
            if any(group in user.groups.values_list('name', flat=True) for group in required_groups):
                return view_func(request, *args, **kwargs)
            else:
                raise PermissionDenied
        return wrapper
    return decorator

How you’d use it on a view.

@custom_group_required(['coaches'])
def my_view(request):
    ...

I also created a tag filter. Here’s the code
To create template tag filters, you should refer to this page: https://docs.djangoproject.com/en/5.0/howto/custom-template-tags/

from django import template


@register.filter(name='has_group')
def has_group(user, group_name):
    return user.groups.filter(name=group_name).exists()

Here’s the HTML implementation:

{% if request.user|has_group:"coaches" %}
<th data-column="id;id" data-format="<a href='view/-id-'>View</a> | <a href='edit/-id-'>Edit</a>">Actions</th>
{% else %}
<th data-column="id" data-format="<a href='view/-id-'>View</a>">Actions</th>
{% endif %}

Let me know if you have any questions.