Avoiding typos in django permission strings

According to the documents, user permissions can be created and used as follows:

class Task(models.Model): ... class Meta: permissions = ( ("view_task", "Can see available tasks"), ) 

Using permission:

 user.has_perm('app.view_task') 

Source: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#custom-permissions

If there is a typo in the permission line. For example: I use user.has_perm('app.view_tasks') , then this typo will not be noticed.

Goal 1

I would like to receive an exception or warning if existing permissions are not used.

Goal 2

To avoid typos, I would like to have constants: user.PERM_APP_VIEW_TASKS or something like that.

+5
source share
2 answers

Goal 1

Cancel the has_perm method of the has_perm class from my backends.py file:

 import logging from difflib import get_close_matches from django.conf import settings from django.contrib.auth.backends import ModelBackend class ModelBackendHelper(ModelBackend): def has_perm(self, user_obj, perm, obj=None): if not user_obj.is_active: return False else: obj_perms = self.get_all_permissions(user_obj, obj) allowed = perm in obj_perms if not allowed: if settings.DEBUG: similar = get_close_matches(perm, obj_perms) if similar: logging.warn("{0} not found, but is similar to: {1}".format(perm, ','.join(similar))) return allowed 

How it works:

The same logic has_perm , but if settings.DEBUG is True , and similar versions of perm are found, then display a warning log of the WARN level:

 WARNING:root:myapp.view_tasks not found, but is similar to: myapp.view_task 

Change the value of AUTHENTICATION_BACKENDS in settings.py :

 AUTHENTICATION_BACKENDS = ['myapp.backends.ModelBackendHelper'] 

It can be used both in production and development environments, but I personally would not include it on the production site, I hope that when everything is done, production permits will be consolidated.

Goal 2

The permissions apply to models and for this DRY I reuse the permissions defined in Meta :

 from django.db import models class Task(models.Model): name = models.CharField(max_length=30) description = models.TextField() class Meta: permissions = ( ("view_task", "Can see available tasks"), ) def _get_perm(model, perm): for p in model._meta.permissions: if p[0] == perm: return p[0] err = "Permission '{0}' not found in model {1}".format(perm, model) raise Exception(err) def format_perm(perm): return '{0}.{1}'.format(__package__, perm) def get_perm(model, type): return format_perm(_get_perm(model, type)) PERM_APP_VIEW_TASK = get_perm(Task, "view_task") 

Permissions can be accessed using get_perm or using the PERM_APP_VIEW_TASK shortcut:

 models.PERM_APP_VIEW_TASK # or get_perm(Task, "view_task") # or format_perm(_get_perm(Task, "view_task")) 

If a missing permission is get_perm will raise an Exception :

 PERM_APP_VIEW_TASK = get_perm(Task, "add_task") 

Message:

 Exception: Permission 'add_task' not found in model <class 'myapp.models.Task'> 
+2
source

Goal 1

Create your own decorator:

Attention! unverified code !!!!!

 @permission_required def assert_permission(permission): def real_decorator(original_function): def wrapper(request, *args, **kwargs): permission_object = Permission.objects.filter(codename=permission) if not permission_object.exists(): raise ValueError("This permission does not exist!") original_function(request, *args, **kwargs) return wrapper return real_decorator 

Goal 2

Add constants to model classes and refer to them for specific permissions. Example:

 class Task(models.Model): MY_PERMISSION_CONSTANT = 'app.view_task' ... class Meta: permissions = ( (MY_PERMISSION_CONSTANT, "Can see available tasks"), ) @permission_required(Task.MY_PERMISSION_CONSTANT) def some_view(request): pass 
0
source

Source: https://habr.com/ru/post/1257957/


All Articles