Django: derive permissions from user_type - django

I am implementing permissions functionality into my webapp. The requirements specified by the customer are that users can perform some actions based on what kind of user they are. The kind of user is defined based on a combination of user_type:user_role. This is the model:
class User(AbstractUser):
usertype = models.IntegerField(choices=UserType.CHOICES, default=UserType.BUYER_USER)
role = models.IntegerField(choices=UserRole.CHOICES, default=UserRole.NORMAL_USER)
For example we can have:
adviser:regular: can manager_orders
adviser:admin: can do what an adviser:regular does and also manage_advisers (this is no django admin, but an admin for advisers!)
(there are more user_type:user_role)
This seems to conflict with the django permission system, which is based on what permission a user has, instead of what kind of user it is.
That is, when using the django permission system I need to define all possible permissions and assign those permissions to the affected user. Instead, with the approach that I am planning, I would need to derive the permission from the user_type:user_role.
Does it make sense to use the django permission system in this context? How can I derive the permission from the user_type:user_role

If you don't want to stick to #ShangWangs approach in the comments, I would recommend taking a look at Django Object Based Permissions.
With this, you are able to grant permissions to users for objects that derive from a super Model you want to apply the permissions to.

Per the Django docs, you can do this with a custom authentication backend:
https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#handling-authorization-in-custom-backends
Any time Django needs to check if a user has a particular permission it will delegate to the auth backend, passing the current user object as an arg. At this point you can check the usertype and role fields and return appropriate boolean value.
example from the docs:
class SettingsBackend(object):
...
def has_perm(self, user_obj, perm, obj=None):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
From what you describe in your question you do not need the complication of 'object-level' permissions as mentioned by others (since you don't have unique permissions for each user, they are based on the type and role)

Related

Create custom permission classes

I have designed my own RBAC system for my Django app. Unlike the original Django's role-based that uses only permissions, content_types, groups, and users table, I designed mine so that it includes a model for defined operations (i.e. read, write, print), the objects (i.e. Post, Events, Documents), the roles (Writer Level 1, Events Manager) and the necessary relationships i.e. my own permissions table which takes in the object reference and a matching operation reference (i.e. Post | add).
My problem now is I am not quite sure how to implement this in DRF. When I use permission_class = (DjangoObjectPermissions,) and someone sends a 'POST' request to the Post model/table, we all know Django will check if the user has Can add post permission. I want to do this as well but I want Django to refer to my RBAC models/tables.
I want to write my own permission classes in my permissions.py but I might need a bit hint on how to implement this.
I've read you can create custom permissions, but I am not sure if that also means you can enforce your own RBAC tables with it.
Alright after checking rest_frameworks' permissions.py, I think I can enforce my own permission class behavior this way:
Create a custom permission class that subclasses rest_framework.permissions.BasePermission
override has_object_permission() method
Inside the method, write a logic that maps the requesting user into my custom RBAC models to check whether or not he has given the appropriate permission
Return the appropriate boolean
According to the DRF Permissions doc
As with DjangoModelPermissions, this permission must only be applied to views that have a .queryset property or .get_queryset() method. Authorization will only be granted if the user is authenticated and has the relevant per-object permissions and relevant model permissions assigned.
So, you should define either queryset attribute or get_queryset(...) method in your view class.
Example:
class MyViewClass(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
...
...
...
def get_queryset(self):
return SomeModel.objects.filter(some_field='foo')
...
...
...
The permissions to a User or groups can be controlled via Django's Admin site as well as Django shell

Django DRF role level permission

I have developed API's using DRF. I am struggling to relate the authorization part from Django's default permission which we define in the admin section for each and every role to the API.
Let's say I have two API's Customer Management and Customer Sales and have two roles created from them at the Django admin portal. manager role will only manage customer (add, view, delete and update) whereas sales role will manage sales (add, view, delete and update) for every customer.
When I try testing them in the admin portal the permissions work fine. The corresponding role has corresponding access. If I use the same with REST API it fails to comply with permission which is defined in the backend. It is like both the roles are able to access both the API's.
How do I handle this? Should I implement my own permission system ignoring old one (auth_permission, auth_group_permissions, auth_user_user_permissions) used in Django or is there any workaround to use Django permissions to make this work?
You can make your permission class as below
class CustomPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated():
return True if request.has_perm('can_read') else False # or stuff similar to this
return False
And use this CustomPermission class to your APIView 's attribute.
For more information on DRF permissions visit https://www.django-rest-framework.org/api-guide/permissions/

restrict fields according to user permissions in django

I'm building an API using Django rest framework. I have to restrict access to fields (both read or write access) according to the kind of user logged in. How do I go about it ? I'm considering writing separate serializers for different user roles (I will get an access token with every request using which I can authenticate the user, the next step will get me the user's roles, according to which I want to restrict what fields the user can see/edit).
In case you want to give certain users model level permissions to conduct certain actions you can do this with custom permissions like so:
class T21Turma(models.Model):
class Meta:
permissions = (("can_view_boletim", "Can view boletim"),
("can_view_mensalidades", "Can view mensalidades"),)
Then you can either make several serializers and swap them out in the views based on the permissions, or you can modify the fields of the serializer dynamically.

Django Auth, Users and Permissions

I have a Organization and Employee models
class Organization(models.Model):
is_active = models.BooleanField()
name = models.CharField(u'Name', max_length = 255)
...
class Employee(models.Model):
user = models.OneToOneField(User)
organization = models.ForeignKey(Organization)
...
Will it be good if I use AUTH_PROFILE_MODULE, so Employee becomes user profile?
This way I can use Django permission system to set employees permissions, like
can see all documents
can see own organization documents
can see his own documents
Is is OK to have a permissions that are not a global one like "can see all documents"?
And what If I also want to have a permissions per Organization? How to do this? And how to distinguish permissions per Organization and per Employee?
Edit: I'm using Django 1.4
In short, yes, you're ok.
Because:
1) Using AUTH_PROFILE_MODULE=Employee will make Employee instance to be available for instance in this way:
def view(request):
employee_instance = request.user.get_profile()
2) Using custom permissions is easy, see: https://docs.djangoproject.com/en/dev/topics/auth/#custom-permissions
Edit:
having custom permissions on organizations is possible as well, probably best if you create permissions programatically, like mentioned in the manual, this way:
content_type = ContentType.objects.get(app_label='myapp', model='Organization')
permission = Permission.objects.create(codename='can_do_something', name='Can Do something',
content_type=content_type)
now, you have permission aware organization model, you just assign it to your user.
To clarify more:
Django auth system is sort of a fixed ACL. You assign roles to a user (or group) and that's pretty much it. Django offers helper wrapper function to easily filter out users who don't have a given permission. If you need to decide at runtime and/or in more generic way, whether an object has permission to do something, you either need full blown ACL system (and which django.auth is not) or you code that kind of behavior yourself. This depends on your needs and obviously on the need to manage those permissions. In the OP's case, the behavior is fixed, therefore I would recommend just coding this in and be happy. But the needs may vary and so does the solution. Django auth is good at assigning static permissions to user, gropu or a "profile" object. What that means to your app is up to you in the end.
So in this case, the good solution would be to have a fixed set of permissions like "can view own documents" or "can view organization documents" that is assigned to user/group. And you app should decide, what it means and serve documents accordingly, taking either runtime state in the account or using models structure to determine the proper data set to serve.

Setting Django admin permissions programmatically

The Django site I'm working on has the possibility for users to sign up for an account. To provide them with some editing functionality, I use the built-in Django admin. However, I'm having a problem: After a user has signed up, they don't have any permissions inside the Django admin, not even view permissions. Thus my question: How do I, in code, assign admin permissions to the user for the relevant models, in the same way I can assign them manually in the "User Permissions" section when editing the user in the admin? I've already tried with the usual has_xxx_permissions() using custom ModelAdmin classes, but that didn't work. So my guess is that I overlooked something obvious. Any ideas?
https://docs.djangoproject.com/en/dev/topics/auth/default/#permissions-and-authorization
new_user.user_permissions.add(permission1, permission2, etc...)
For your purposes, it would probably be much more easy and and efficient to assign all new users to a particular group, and then give that group all the permissions the user needs. Any member of the group will inherit those permissions as well.
You can create the group and assign the permissions to it in the admin. Then, you just need to add something like the following to your registration code.
try:
group = Group.objects.get(name='The User Group')
except Group.DoesNotExist:
# group should exist, but this is just for safety's sake, it case the improbable should happen
pass
else:
user.groups.add(group)
dgel's answer pointed me in the direction which lead to a working solution for me. Essentially, what he seems to be suggesting is:
Retrieve a ContentType for the model you want to set permissions for. In this context, a content type is an object that holds information about a Django model.
Create a Permission object consisting of the content type and the action you want to allow inside the admin, using Permission.objects.get(). The only difficulty here is figuring out the codename parameter, which, for admin permissions, consists of an action ("add", "change" or "delete"), an underscore, and the model name. So if you have a model called Foo and you want to create all permissions for it, you'll need 3 permissions, each with the content type of your Foo model plus the code names add_foo, change_foo, and delete_foo.
Assign these permissions using user.user_permissions.add(permission).
Head over to dgel's answers for code examples. Looking at a data dump of the auth app (manage.py dumpdata auth) of an existing Django database provided me with insights into the inner workings of permissions, too.
I'll answer your question exactly since I found this question with Google. I'll show what I'm doing in Django 1.9 with groups, then show how to do it to a user.
from django.contrib.auth.models import Group, Permission
group, __ = Group.objects.get_or_create(name='my_group')
permissions = Permission.objects.all()
for p in permissions:
group.permissions.add(p)
group.save()
It's pretty easy to adapt to user:
from django.contrib.auth.models import Permission
permissions = Permission.objects.all()
for p in permissions:
youruser.user_permissions.add(p)
youruser.save()
I prefer group because you may be adding permissions in the future and can just add to group instead of re-doing all users.
As of Django 1.6:
Every User has a many-to-many field user_permissions to Permission - you can add permissions to this:
your_user.user_permissions.add(permission)
v1.6 Docs:
Django.contrib.auth API (shows User, Group and Permission objects)
Auth default permissions (shows how to clear, add, remove)