Clean up permissions utility functions

This commit is contained in:
Jeremy Stretch 2020-06-02 09:26:45 -04:00
parent 7b01ba9776
commit 85e932bfc1
4 changed files with 19 additions and 18 deletions

View File

@ -6,7 +6,7 @@ from django.contrib.auth.models import Group
from django.db.models import Q from django.db.models import Q
from users.models import ObjectPermission from users.models import ObjectPermission
from utilities.permissions import permission_is_exempt, resolve_permission from utilities.permissions import permission_is_exempt, resolve_permission, resolve_permission_ct
class ObjectPermissionBackend(ModelBackend): class ObjectPermissionBackend(ModelBackend):
@ -42,8 +42,7 @@ class ObjectPermissionBackend(ModelBackend):
return perms return perms
def has_perm(self, user_obj, perm, obj=None): def has_perm(self, user_obj, perm, obj=None):
app_label, codename = perm.split('.') app_label, action, model_name = resolve_permission(perm)
action, model_name = codename.split('_')
# Superusers implicitly have all permissions # Superusers implicitly have all permissions
if user_obj.is_active and user_obj.is_superuser: if user_obj.is_active and user_obj.is_superuser:
@ -114,7 +113,7 @@ class RemoteUserBackend(_RemoteUserBackend):
permissions_list = [] permissions_list = []
for permission_name, attrs in settings.REMOTE_AUTH_DEFAULT_PERMISSIONS.items(): for permission_name, attrs in settings.REMOTE_AUTH_DEFAULT_PERMISSIONS.items():
try: try:
content_type, action = resolve_permission(permission_name) content_type, action = resolve_permission_ct(permission_name)
# TODO: Merge multiple actions into a single ObjectPermission per content type # TODO: Merge multiple actions into a single ObjectPermission per content type
obj_perm = ObjectPermission(actions=[action], attrs=attrs) obj_perm = ObjectPermission(actions=[action], attrs=attrs)
obj_perm.save() obj_perm.save()

View File

@ -19,33 +19,36 @@ def get_permission_for_model(model, action):
) )
def get_permission_action(name): def resolve_permission(name):
""" """
Return the action component (e.g. view or add) from a permission name. Given a permission name, return the app_label, action, and model_name components. For example, "dcim.view_site"
returns ("dcim", "view", "site").
:param name: Permission name in the format <app_label>.<action>_<model> :param name: Permission name in the format <app_label>.<action>_<model>
""" """
try: try:
return name.split('.')[1].split('_')[0] app_label, codename = name.split('.')
action, model_name = codename.rsplit('_', 1)
except ValueError: except ValueError:
raise ValueError( raise ValueError(
f"Invalid permission name: {name}. Must be in the format <app_label>.<action>_<model>" f"Invalid permission name: {name}. Must be in the format <app_label>.<action>_<model>"
) )
return app_label, action, model_name
def resolve_permission(name):
def resolve_permission_ct(name):
""" """
Given a permission name, return the relevant ContentType and action. For example, "dcim.view_site" returns Given a permission name, return the relevant ContentType and action. For example, "dcim.view_site" returns
(Site, "view"). (Site, "view").
:param name: Permission name in the format <app_label>.<action>_<model> :param name: Permission name in the format <app_label>.<action>_<model>
""" """
app_label, codename = name.split('.') app_label, action, model_name = resolve_permission(name)
action, model_name = codename.split('_')
try: try:
content_type = ContentType.objects.get(app_label=app_label, model=model_name) content_type = ContentType.objects.get(app_label=app_label, model=model_name)
except ContentType.DoesNotExist: except ContentType.DoesNotExist:
raise ValueError(f"Unknown app/model for {name}") raise ValueError(f"Unknown app_label/model_name for {name}")
return content_type, action return content_type, action
@ -56,8 +59,7 @@ def permission_is_exempt(name):
:param name: Permission name in the format <app_label>.<action>_<model> :param name: Permission name in the format <app_label>.<action>_<model>
""" """
app_label, codename = name.split('.') app_label, action, model_name = resolve_permission(name)
action, model_name = codename.split('_')
if action == 'view': if action == 'view':
if ( if (

View File

@ -7,7 +7,7 @@ from django.urls import reverse, NoReverseMatch
from rest_framework.test import APIClient from rest_framework.test import APIClient
from users.models import ObjectPermission, Token from users.models import ObjectPermission, Token
from utilities.permissions import resolve_permission from utilities.permissions import resolve_permission_ct
from .utils import disable_warnings, post_data from .utils import disable_warnings, post_data
@ -33,7 +33,7 @@ class TestCase(_TestCase):
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>. Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
""" """
for name in names: for name in names:
ct, action = resolve_permission(name) ct, action = resolve_permission_ct(name)
obj_perm = ObjectPermission(actions=[action]) obj_perm = ObjectPermission(actions=[action])
obj_perm.save() obj_perm.save()
obj_perm.users.add(self.user) obj_perm.users.add(self.user)

View File

@ -28,7 +28,7 @@ from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset from extras.querysets import CustomFieldQueryset
from utilities.exceptions import AbortTransaction from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
from utilities.permissions import get_permission_action, get_permission_for_model from utilities.permissions import get_permission_for_model, resolve_permission
from utilities.utils import csv_format, prepare_cloned_fields from utilities.utils import csv_format, prepare_cloned_fields
from .error_handlers import handle_protectederror from .error_handlers import handle_protectederror
from .forms import ConfirmationForm, ImportForm from .forms import ConfirmationForm, ImportForm
@ -64,7 +64,7 @@ class ObjectPermissionRequiredMixin(AccessMixin):
if user.has_perms((permission_required, *self.additional_permissions)): if user.has_perms((permission_required, *self.additional_permissions)):
# Update the view's QuerySet to filter only the permitted objects # Update the view's QuerySet to filter only the permitted objects
action = get_permission_action(permission_required) action = resolve_permission(permission_required)[1]
self.queryset = self.queryset.restrict(user, action) self.queryset = self.queryset.restrict(user, action)
return True return True
@ -233,7 +233,7 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
# Compile a dictionary indicating which permissions are available to the current user for this model # Compile a dictionary indicating which permissions are available to the current user for this model
permissions = {} permissions = {}
for action in ('add', 'change', 'delete', 'view'): for action in ('add', 'change', 'delete', 'view'):
perm_name = '{}.{}_{}'.format(model._meta.app_label, action, model._meta.model_name) perm_name = get_permission_for_model(model, action)
permissions[action] = request.user.has_perm(perm_name) permissions[action] = request.user.has_perm(perm_name)
# Construct the table based on the user's permissions # Construct the table based on the user's permissions