Sfoglia il codice sorgente

Clean up permissions utility functions

Jeremy Stretch 5 anni fa
parent
commit
85e932bfc1

+ 3 - 4
netbox/netbox/authentication.py

@@ -6,7 +6,7 @@ from django.contrib.auth.models import Group
 from django.db.models import Q
 
 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):
@@ -42,8 +42,7 @@ class ObjectPermissionBackend(ModelBackend):
         return perms
 
     def has_perm(self, user_obj, perm, obj=None):
-        app_label, codename = perm.split('.')
-        action, model_name = codename.split('_')
+        app_label, action, model_name = resolve_permission(perm)
 
         # Superusers implicitly have all permissions
         if user_obj.is_active and user_obj.is_superuser:
@@ -114,7 +113,7 @@ class RemoteUserBackend(_RemoteUserBackend):
         permissions_list = []
         for permission_name, attrs in settings.REMOTE_AUTH_DEFAULT_PERMISSIONS.items():
             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
                 obj_perm = ObjectPermission(actions=[action], attrs=attrs)
                 obj_perm.save()

+ 11 - 9
netbox/utilities/permissions.py

@@ -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>
     """
     try:
-        return name.split('.')[1].split('_')[0]
+        app_label, codename = name.split('.')
+        action, model_name = codename.rsplit('_', 1)
     except ValueError:
         raise ValueError(
             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
     (Site, "view").
 
     :param name: Permission name in the format <app_label>.<action>_<model>
     """
-    app_label, codename = name.split('.')
-    action, model_name = codename.split('_')
+    app_label, action, model_name = resolve_permission(name)
     try:
         content_type = ContentType.objects.get(app_label=app_label, model=model_name)
     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
 
@@ -56,8 +59,7 @@ def permission_is_exempt(name):
 
     :param name: Permission name in the format <app_label>.<action>_<model>
     """
-    app_label, codename = name.split('.')
-    action, model_name = codename.split('_')
+    app_label, action, model_name = resolve_permission(name)
 
     if action == 'view':
         if (

+ 2 - 2
netbox/utilities/testing/testcases.py

@@ -7,7 +7,7 @@ from django.urls import reverse, NoReverseMatch
 from rest_framework.test import APIClient
 
 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
 
 
@@ -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>.
         """
         for name in names:
-            ct, action = resolve_permission(name)
+            ct, action = resolve_permission_ct(name)
             obj_perm = ObjectPermission(actions=[action])
             obj_perm.save()
             obj_perm.users.add(self.user)

+ 3 - 3
netbox/utilities/views.py

@@ -28,7 +28,7 @@ from extras.models import CustomField, CustomFieldValue, ExportTemplate
 from extras.querysets import CustomFieldQueryset
 from utilities.exceptions import AbortTransaction
 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 .error_handlers import handle_protectederror
 from .forms import ConfirmationForm, ImportForm
@@ -64,7 +64,7 @@ class ObjectPermissionRequiredMixin(AccessMixin):
         if user.has_perms((permission_required, *self.additional_permissions)):
 
             # 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)
 
             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
         permissions = {}
         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)
 
         # Construct the table based on the user's permissions