From bd67cfb4014bd96cf297e830eff4d71dbf36735b Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 6 Jun 2023 11:15:35 -0700 Subject: [PATCH] 125890 basic group objectpermission views --- netbox/netbox/navigation/menu.py | 4 +- netbox/users/forms/bulk_edit.py | 48 ++++++++ netbox/users/forms/bulk_import.py | 31 ++++- netbox/users/forms/filtersets.py | 62 ++++++++++ netbox/users/forms/model_forms.py | 30 +++++ ...user.py => 0004_netboxgroup_netboxuser.py} | 16 ++- netbox/users/models.py | 10 ++ netbox/users/tables.py | 30 +++++ netbox/users/urls.py | 19 +++- netbox/users/views.py | 106 ++++++++++++++++-- 10 files changed, 339 insertions(+), 17 deletions(-) rename netbox/users/migrations/{0004_netboxuser.py => 0004_netboxgroup_netboxuser.py} (59%) diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 5e8322657..b896f67ef 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -351,7 +351,9 @@ ADMIN_MENU = Menu( MenuGroup( label=_('Users'), items=( - get_model_item('users', 'user', _('Users'), actions=['add']), + get_model_item('users', 'netboxuser', _('Users'), actions=['add']), + get_model_item('users', 'netboxgroup', _('Groups'), actions=['add']), + get_model_item('users', 'objectpermission', _('Permissions'), actions=['add']), ), ), ), diff --git a/netbox/users/forms/bulk_edit.py b/netbox/users/forms/bulk_edit.py index dd2dc6aa9..c2b548e8d 100644 --- a/netbox/users/forms/bulk_edit.py +++ b/netbox/users/forms/bulk_edit.py @@ -11,6 +11,8 @@ from utilities.forms.fields import CommentField, DynamicModelChoiceField, Dynami from utilities.forms.widgets import DatePicker, NumberWithOptions __all__ = ( + 'GroupBulkEditForm', + 'ObjectPermissionBulkEditForm', 'UserBulkEditForm', ) @@ -36,3 +38,49 @@ class UserBulkEditForm(NetBoxModelBulkEditForm): nullable_fields = ( 'asns', 'description', 'comments', ) + + +class GroupBulkEditForm(NetBoxModelBulkEditForm): + asns = DynamicModelMultipleChoiceField( + queryset=ASN.objects.all(), + label=_('ASNs'), + required=False + ) + description = forms.CharField( + max_length=200, + required=False + ) + comments = CommentField( + label=_('Comments') + ) + + model = Provider + fieldsets = ( + (None, ('asns', 'description')), + ) + nullable_fields = ( + 'asns', 'description', 'comments', + ) + + +class ObjectPermissionBulkEditForm(NetBoxModelBulkEditForm): + asns = DynamicModelMultipleChoiceField( + queryset=ASN.objects.all(), + label=_('ASNs'), + required=False + ) + description = forms.CharField( + max_length=200, + required=False + ) + comments = CommentField( + label=_('Comments') + ) + + model = Provider + fieldsets = ( + (None, ('asns', 'description')), + ) + nullable_fields = ( + 'asns', 'description', 'comments', + ) diff --git a/netbox/users/forms/bulk_import.py b/netbox/users/forms/bulk_import.py index aa3718d24..060d771ee 100644 --- a/netbox/users/forms/bulk_import.py +++ b/netbox/users/forms/bulk_import.py @@ -1,15 +1,14 @@ from django import forms -from circuits.choices import CircuitStatusChoices -from circuits.models import * -from dcim.models import Site +from users.models import * from django.utils.translation import gettext as _ from netbox.forms import NetBoxModelImportForm -from tenancy.models import Tenant from utilities.forms import BootstrapMixin from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField __all__ = ( + 'GroupImportForm', + 'ObjectPermissionImportForm', 'UserImportForm', ) @@ -18,7 +17,27 @@ class UserImportForm(NetBoxModelImportForm): slug = SlugField() class Meta: - model = Provider + model = NetBoxUser fields = ( - 'name', 'slug', 'description', 'comments', 'tags', + 'email', + ) + + +class GroupImportForm(NetBoxModelImportForm): + slug = SlugField() + + class Meta: + model = NetBoxGroup + fields = ( + 'name', + ) + + +class ObjectPermissionImportForm(NetBoxModelImportForm): + slug = SlugField() + + class Meta: + model = ObjectPermission + fields = ( + 'name', ) diff --git a/netbox/users/forms/filtersets.py b/netbox/users/forms/filtersets.py index 473bddac9..cef567230 100644 --- a/netbox/users/forms/filtersets.py +++ b/netbox/users/forms/filtersets.py @@ -12,6 +12,8 @@ from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterFie from utilities.forms.widgets import DatePicker, NumberWithOptions __all__ = ( + 'GroupFilterForm', + 'ObjectPermissionFilterForm', 'UserFilterForm', ) @@ -44,3 +46,63 @@ class UserFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): required=False, label='Is Active', ) + + +class GroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): + model = NetBoxUser + fieldsets = ( + (None, ('q', 'filter_id',)), + ('Name', ('username', 'first_name', 'last_name')), + ('Security', ('is_superuser', 'is_staff', 'is_active')), + ) + username = forms.CharField( + required=False + ) + first_name = forms.CharField( + required=False + ) + last_name = forms.CharField( + required=False + ) + is_superuser = forms.BooleanField( + required=False, + label='Is Superuser', + ) + is_staff = forms.BooleanField( + required=False, + label='Is Staff', + ) + is_active = forms.BooleanField( + required=False, + label='Is Active', + ) + + +class ObjectPermissionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): + model = NetBoxUser + fieldsets = ( + (None, ('q', 'filter_id',)), + ('Name', ('username', 'first_name', 'last_name')), + ('Security', ('is_superuser', 'is_staff', 'is_active')), + ) + username = forms.CharField( + required=False + ) + first_name = forms.CharField( + required=False + ) + last_name = forms.CharField( + required=False + ) + is_superuser = forms.BooleanField( + required=False, + label='Is Superuser', + ) + is_staff = forms.BooleanField( + required=False, + label='Is Staff', + ) + is_active = forms.BooleanField( + required=False, + label='Is Active', + ) diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index fbf55ffa8..209e6b7da 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -18,7 +18,9 @@ from users.models import * __all__ = ( + 'GroupForm', 'LoginForm', + 'ObjectPermissionForm', 'PasswordChangeForm', 'TokenForm', 'UserConfigForm', @@ -160,3 +162,31 @@ class UserForm(BootstrapMixin, forms.ModelForm): 'username', 'first_name', 'last_name', 'email', 'groups', 'is_active', 'is_staff', 'is_superuser', 'last_login', 'date_joined', ] + + +class GroupForm(BootstrapMixin, forms.ModelForm): + + fieldsets = ( + ('User', ('username', 'first_name', 'last_name', 'email', )), + ) + + class Meta: + model = NetBoxUser + fields = [ + 'username', 'first_name', 'last_name', 'email', 'groups', + 'is_active', 'is_staff', 'is_superuser', 'last_login', 'date_joined', + ] + + +class ObjectPermissionForm(BootstrapMixin, forms.ModelForm): + + fieldsets = ( + ('User', ('username', 'first_name', 'last_name', 'email', )), + ) + + class Meta: + model = NetBoxUser + fields = [ + 'username', 'first_name', 'last_name', 'email', 'groups', + 'is_active', 'is_staff', 'is_superuser', 'last_login', 'date_joined', + ] diff --git a/netbox/users/migrations/0004_netboxuser.py b/netbox/users/migrations/0004_netboxgroup_netboxuser.py similarity index 59% rename from netbox/users/migrations/0004_netboxuser.py rename to netbox/users/migrations/0004_netboxgroup_netboxuser.py index 03355ea3c..7bb746bd5 100644 --- a/netbox/users/migrations/0004_netboxuser.py +++ b/netbox/users/migrations/0004_netboxgroup_netboxuser.py @@ -1,4 +1,4 @@ -# Generated by Django 4.1.9 on 2023-06-02 16:55 +# Generated by Django 4.1.9 on 2023-06-06 18:15 import django.contrib.auth.models from django.db import migrations @@ -11,6 +11,20 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='NetBoxGroup', + fields=[], + options={ + 'verbose_name': 'Group', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('auth.group',), + managers=[ + ('objects', django.contrib.auth.models.GroupManager()), + ], + ), migrations.CreateModel( name='NetBoxUser', fields=[], diff --git a/netbox/users/models.py b/netbox/users/models.py index 6b5b13b14..7bb0377e4 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -20,6 +20,7 @@ from utilities.utils import flatten_dict from .constants import * __all__ = ( + 'NetBoxGroup', 'NetBoxUser', 'ObjectPermission', 'Token', @@ -58,6 +59,15 @@ class NetBoxUser(User): verbose_name = 'User' proxy = True + +class NetBoxGroup(Group): + """ + Proxy contrib.auth.models.User for the UI + """ + class Meta: + verbose_name = 'Group' + proxy = True + # # User preferences # diff --git a/netbox/users/tables.py b/netbox/users/tables.py index 45a87e7ac..4931d9327 100644 --- a/netbox/users/tables.py +++ b/netbox/users/tables.py @@ -5,6 +5,8 @@ from netbox.tables import NetBoxTable, columns from users.models import NetBoxUser __all__ = ( + 'GroupTable', + 'ObjectPermissionTable', 'TokenTable', 'UserTable', ) @@ -68,3 +70,31 @@ class UserTable(NetBoxTable): 'pk', 'id', 'username', 'email', 'first_name', 'last_name', 'is_superuser', 'is_staff', 'is_active' ) default_columns = ('pk', 'username', 'email', 'first_name', 'last_name', 'is_superuser') + + +class GroupTable(NetBoxTable): + username = tables.LinkColumn('users:netboxuser', args=[A('pk')]) + actions = columns.ActionsColumn( + actions=('edit', 'delete'), + ) + + class Meta(NetBoxTable.Meta): + model = NetBoxUser + fields = ( + 'pk', 'id', 'username', 'email', 'first_name', 'last_name', 'is_superuser', 'is_staff', 'is_active' + ) + default_columns = ('pk', 'username', 'email', 'first_name', 'last_name', 'is_superuser') + + +class ObjectPermissionTable(NetBoxTable): + username = tables.LinkColumn('users:netboxuser', args=[A('pk')]) + actions = columns.ActionsColumn( + actions=('edit', 'delete'), + ) + + class Meta(NetBoxTable.Meta): + model = NetBoxUser + fields = ( + 'pk', 'id', 'username', 'email', 'first_name', 'last_name', 'is_superuser', 'is_staff', 'is_active' + ) + default_columns = ('pk', 'username', 'email', 'first_name', 'last_name', 'is_superuser') diff --git a/netbox/users/urls.py b/netbox/users/urls.py index 12c4e6e66..17e9b9685 100644 --- a/netbox/users/urls.py +++ b/netbox/users/urls.py @@ -12,14 +12,29 @@ urlpatterns = [ path('password/', views.ChangePasswordView.as_view(), name='change_password'), # Users - path('users/', views.NetBoxUserListView.as_view(), name='user_list'), - path('users/add/', views.NetBoxUserEditView.as_view(), name='user_add'), + path('users/', views.NetBoxUserListView.as_view(), name='netboxuser_list'), path('users/add/', views.NetBoxUserEditView.as_view(), name='netboxuser_add'), path('users/import/', views.NetBoxUserBulkImportView.as_view(), name='netboxuser_import'), path('users/edit/', views.NetBoxUserBulkEditView.as_view(), name='netboxuser_bulk_edit'), path('users/delete/', views.NetBoxUserBulkDeleteView.as_view(), name='netboxuser_bulk_delete'), path('users//', include(get_model_urls('users', 'netboxuser'))), + # Groups + path('groups/', views.NetBoxUserListView.as_view(), name='netboxgroup_list'), + path('groups/add/', views.NetBoxUserEditView.as_view(), name='netboxgroup_add'), + path('groups/import/', views.NetBoxUserBulkImportView.as_view(), name='netboxgroup_import'), + path('groups/edit/', views.NetBoxUserBulkEditView.as_view(), name='netboxgroup_bulk_edit'), + path('groups/delete/', views.NetBoxUserBulkDeleteView.as_view(), name='netboxgroup_bulk_delete'), + path('groups//', include(get_model_urls('users', 'netboxgroup'))), + + # Permissions + path('permissions/', views.NetBoxUserListView.as_view(), name='objectpermission_list'), + path('permissions/add/', views.NetBoxUserEditView.as_view(), name='objectpermission_add'), + path('permissions/import/', views.NetBoxUserBulkImportView.as_view(), name='objectpermission_import'), + path('permissions/edit/', views.NetBoxUserBulkEditView.as_view(), name='objectpermission_bulk_edit'), + path('permissions/delete/', views.NetBoxUserBulkDeleteView.as_view(), name='objectpermission_bulk_delete'), + path('permissions//', include(get_model_urls('users', 'objectpermission'))), + # API tokens path('api-tokens/', views.TokenListView.as_view(), name='token_list'), path('api-tokens/add/', views.TokenEditView.as_view(), name='token_add'), diff --git a/netbox/users/views.py b/netbox/users/views.py index d114deb7a..3bf6d5e1c 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -23,7 +23,7 @@ from netbox.views import generic from utilities.forms import ConfirmationForm from utilities.views import register_model_view from . import filtersets, forms, tables -from .models import Token, UserConfig, NetBoxUser +from .models import Token, UserConfig, NetBoxGroup, NetBoxUser, ObjectPermission # @@ -348,7 +348,7 @@ class NetBoxUserListView(generic.ObjectListView): @register_model_view(NetBoxUser) class NetBoxUserView(generic.ObjectView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() template_name = 'users/user.html' def get_extra_context(self, request, instance): @@ -366,28 +366,120 @@ class NetBoxUserView(generic.ObjectView): @register_model_view(NetBoxUser, 'edit') class NetBoxUserEditView(generic.ObjectEditView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() form = forms.UserForm @register_model_view(NetBoxUser, 'delete') class NetBoxUserDeleteView(generic.ObjectDeleteView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() class NetBoxUserBulkImportView(generic.BulkImportView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() model_form = forms.UserImportForm class NetBoxUserBulkEditView(generic.BulkEditView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() filterset = filtersets.UserFilterSet table = tables.UserTable form = forms.UserBulkEditForm class NetBoxUserBulkDeleteView(generic.BulkDeleteView): - queryset = get_user_model().objects.all() + queryset = NetBoxUser.objects.all() filterset = filtersets.UserFilterSet table = tables.UserTable + +# +# Groups +# + + +class NetBoxGroupListView(generic.ObjectListView): + queryset = NetBoxGroup.objects.all() + filterset = filtersets.GroupFilterSet + filterset_form = forms.GroupFilterForm + table = tables.GroupTable + + +@register_model_view(NetBoxGroup) +class NetBoxGroupView(generic.ObjectView): + queryset = NetBoxGroup.objects.all() + template_name = 'users/group.html' + + +@register_model_view(NetBoxGroup, 'edit') +class NetBoxGroupEditView(generic.ObjectEditView): + queryset = NetBoxGroup.objects.all() + form = forms.GroupForm + + +@register_model_view(NetBoxGroup, 'delete') +class NetBoxGroupDeleteView(generic.ObjectDeleteView): + queryset = NetBoxGroup.objects.all() + + +class NetBoxGroupBulkImportView(generic.BulkImportView): + queryset = NetBoxGroup.objects.all() + model_form = forms.GroupImportForm + + +class NetBoxGroupBulkEditView(generic.BulkEditView): + queryset = NetBoxGroup.objects.all() + filterset = filtersets.GroupFilterSet + table = tables.GroupTable + form = forms.GroupBulkEditForm + + +class NetBoxGroupBulkDeleteView(generic.BulkDeleteView): + queryset = NetBoxGroup.objects.all() + filterset = filtersets.GroupFilterSet + table = tables.GroupTable + +# +# ObjectPermissions +# + + +class ObjectPermissionListView(generic.ObjectListView): + queryset = NetBoxGroup.objects.all() + filterset = filtersets.ObjectPermissionFilterSet + filterset_form = forms.ObjectPermissionFilterForm + table = tables.ObjectPermissionTable + + +@register_model_view(ObjectPermission) +class ObjectPermissionView(generic.ObjectView): + queryset = NetBoxGroup.objects.all() + template_name = 'users/objectpermission.html' + + +@register_model_view(ObjectPermission, 'edit') +class ObjectPermissionEditView(generic.ObjectEditView): + queryset = ObjectPermission.objects.all() + form = forms.ObjectPermissionForm + + +@register_model_view(ObjectPermission, 'delete') +class ObjectPermissionDeleteView(generic.ObjectDeleteView): + queryset = ObjectPermission.objects.all() + + +class ObjectPermissionBulkImportView(generic.BulkImportView): + queryset = ObjectPermission.objects.all() + model_form = forms.ObjectPermissionImportForm + + +class ObjectPermissionBulkEditView(generic.BulkEditView): + queryset = ObjectPermission.objects.all() + filterset = filtersets.ObjectPermissionFilterSet + table = tables.ObjectPermissionTable + form = forms.ObjectPermissionBulkEditForm + + +class ObjectPermissionBulkDeleteView(generic.BulkDeleteView): + queryset = ObjectPermission.objects.all() + filterset = filtersets.ObjectPermissionFilterSet + table = tables.ObjectPermissionTable