From 540339683cabb4cf2ec794d9aeb271e5d8fcfab7 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 13 Jun 2023 14:33:41 -0700 Subject: [PATCH] 12589 add user group manager --- netbox/users/models.py | 8 ++++++-- netbox/users/views.py | 27 ++++++++++++++++++++++++++- netbox/utilities/querysets.py | 6 ++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/netbox/users/models.py b/netbox/users/models.py index 220e93724..b90ec901b 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -2,7 +2,7 @@ import binascii import os from django.conf import settings -from django.contrib.auth.models import Group, User +from django.contrib.auth.models import Group, GroupManager, User, UserManager from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField from django.core.validators import MinLengthValidator @@ -52,11 +52,15 @@ class AdminUser(User): proxy = True +class NetBoxUserManager(UserManager.from_queryset(RestrictedQuerySet)): + pass + + class NetBoxUser(User): """ Proxy contrib.auth.models.User for the UI """ - objects = RestrictedQuerySet.as_manager() + objects = NetBoxUserManager() class Meta: verbose_name = 'User' diff --git a/netbox/users/views.py b/netbox/users/views.py index aa82d4cbe..16e355fc5 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -4,7 +4,7 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash, get_user_model from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.models import update_last_login +from django.contrib.auth.models import Group, User, update_last_login from django.contrib.auth.signals import user_logged_in from django.db.models import Count from django.http import HttpResponseRedirect @@ -22,6 +22,7 @@ from netbox.authentication import get_auth_backend_display, get_saml_idps from netbox.config import get_config from netbox.views import generic from utilities.forms import ConfirmationForm +from utilities.permissions import get_permission_for_model from utilities.querysets import RestrictedQuerySet from utilities.views import register_model_view from . import filtersets, forms, tables @@ -347,12 +348,18 @@ class NetBoxUserListView(generic.ObjectListView): filterset_form = forms.UserFilterForm table = tables.UserTable + def get_required_permission(self): + return get_permission_for_model(User, 'view') + @register_model_view(NetBoxUser) class NetBoxUserView(generic.ObjectView): queryset = NetBoxUser.objects.all() template_name = 'users/user.html' + def get_required_permission(self): + return get_permission_for_model(User, 'view') + def get_extra_context(self, request, instance): # Compile changelog table changelog = ObjectChange.objects.restrict(request.user, 'view').filter(user=request.user).prefetch_related( @@ -371,16 +378,27 @@ class NetBoxUserEditView(generic.ObjectEditView): queryset = NetBoxUser.objects.all() form = forms.UserForm + def get_required_permission(self): + # self._permission_action is set by dispatch() to either "add" or "change" depending on whether + # we are modifying an existing object or creating a new one. + return get_permission_for_model(User, self._permission_action) + @register_model_view(NetBoxUser, 'delete') class NetBoxUserDeleteView(generic.ObjectDeleteView): queryset = NetBoxUser.objects.all() + def get_required_permission(self): + return get_permission_for_model(User, 'delete') + class NetBoxUserBulkImportView(generic.BulkImportView): queryset = NetBoxUser.objects.all() model_form = forms.UserImportForm + def get_required_permission(self): + return get_permission_for_model(User, 'add') + class NetBoxUserBulkEditView(generic.BulkEditView): queryset = NetBoxUser.objects.all() @@ -388,12 +406,19 @@ class NetBoxUserBulkEditView(generic.BulkEditView): table = tables.UserTable form = forms.UserBulkEditForm + def get_required_permission(self): + return get_permission_for_model(User, 'change') + class NetBoxUserBulkDeleteView(generic.BulkDeleteView): queryset = NetBoxUser.objects.all() filterset = filtersets.UserFilterSet table = tables.UserTable + def get_required_permission(self): + return get_permission_for_model(User, 'delete') + + # # Groups # diff --git a/netbox/utilities/querysets.py b/netbox/utilities/querysets.py index ba4b28418..e5cbe3894 100644 --- a/netbox/utilities/querysets.py +++ b/netbox/utilities/querysets.py @@ -1,3 +1,4 @@ +from django.contrib.contenttypes.models import ContentType from django.db.models import Prefetch, QuerySet from users.constants import CONSTRAINT_TOKEN_USER @@ -46,8 +47,9 @@ class RestrictedQuerySet(QuerySet): :param action: The action which must be permitted (e.g. "view" for "dcim.view_site"); default is 'view' """ # Resolve the full name of the required permission - app_label = self.model._meta.app_label - model_name = self.model._meta.model_name + ct = ContentType.objects.get_for_model(self.model) + app_label = ct.app_label + model_name = ct.model permission_required = f'{app_label}.{action}_{model_name}' # Bypass restriction for superusers and exempt views