diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index 7381ca685..6b58becf0 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -23,10 +23,10 @@ class ObjectPermissionBackend(ModelBackend): """ Return all permissions granted to the user by an ObjectPermission. """ - # Retrieve all assigned ObjectPermissions + # Retrieve all assigned and enabled ObjectPermissions object_permissions = ObjectPermission.objects.unrestricted().filter( - Q(users=user_obj) | - Q(groups__user=user_obj) + Q(users=user_obj) | Q(groups__user=user_obj), + enabled=True ).prefetch_related('object_types') # Create a dictionary mapping permissions to their constraints diff --git a/netbox/users/admin.py b/netbox/users/admin.py index 25703966c..baa4dee76 100644 --- a/netbox/users/admin.py +++ b/netbox/users/admin.py @@ -172,15 +172,18 @@ class ObjectPermissionForm(forms.ModelForm): @admin.register(ObjectPermission) class ObjectPermissionAdmin(admin.ModelAdmin): fieldsets = ( + (None, { + 'fields': ('name', 'enabled') + }), + ('Actions', { + 'fields': (('can_view', 'can_add', 'can_change', 'can_delete'), 'actions') + }), ('Objects', { 'fields': ('object_types',) }), ('Assignment', { 'fields': ('groups', 'users') }), - ('Actions', { - 'fields': (('can_view', 'can_add', 'can_change', 'can_delete'), 'actions') - }), ('Constraints', { 'fields': ('constraints',) }), @@ -188,7 +191,7 @@ class ObjectPermissionAdmin(admin.ModelAdmin): filter_horizontal = ('object_types', 'groups', 'users') form = ObjectPermissionForm list_display = [ - 'list_models', 'list_users', 'list_groups', 'actions', 'constraints', + 'get_name', 'enabled', 'list_models', 'list_users', 'list_groups', 'actions', 'constraints', ] list_filter = [ 'groups', 'users' @@ -197,6 +200,10 @@ class ObjectPermissionAdmin(admin.ModelAdmin): def get_queryset(self, request): return super().get_queryset(request).prefetch_related('object_types', 'users', 'groups') + def get_name(self, obj): + return obj.name or f'Permission #{obj.pk}' + get_name.short_description = 'Name' + def list_models(self, obj): return ', '.join([f"{ct}" for ct in obj.object_types.all()]) list_models.short_description = 'Models' diff --git a/netbox/users/api/nested_serializers.py b/netbox/users/api/nested_serializers.py index f6e5cefbf..6e89f1da0 100644 --- a/netbox/users/api/nested_serializers.py +++ b/netbox/users/api/nested_serializers.py @@ -36,7 +36,7 @@ class NestedObjectPermissionSerializer(WritableNestedSerializer): class Meta: model = ObjectPermission - fields = ['id', 'object_types', 'groups', 'users', 'actions'] + fields = ['id', 'name', 'enabled', 'object_types', 'groups', 'users', 'actions'] def get_groups(self, obj): return [g.name for g in obj.groups.all()] diff --git a/netbox/users/api/serializers.py b/netbox/users/api/serializers.py index 052567e47..c83232431 100644 --- a/netbox/users/api/serializers.py +++ b/netbox/users/api/serializers.py @@ -26,4 +26,4 @@ class ObjectPermissionSerializer(ValidatedModelSerializer): class Meta: model = ObjectPermission - fields = ('id', 'object_types', 'groups', 'users', 'actions', 'constraints') + fields = ('id', 'name', 'enabled', 'object_types', 'groups', 'users', 'actions', 'constraints') diff --git a/netbox/users/migrations/0008_objectpermission.py b/netbox/users/migrations/0008_objectpermission.py index 3f16e1ee8..63e173ef8 100644 --- a/netbox/users/migrations/0008_objectpermission.py +++ b/netbox/users/migrations/0008_objectpermission.py @@ -18,6 +18,8 @@ class Migration(migrations.Migration): name='ObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), + ('name', models.CharField(blank=True, max_length=100)), + ('enabled', models.BooleanField(default=True)), ('constraints', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), ('actions', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30), size=None)), ('object_types', models.ManyToManyField(limit_choices_to={'app_label__in': ['circuits', 'dcim', 'extras', 'ipam', 'secrets', 'tenancy', 'virtualization']}, related_name='object_permissions', to='contenttypes.ContentType')), diff --git a/netbox/users/models.py b/netbox/users/models.py index bce3bd704..f210cb4d0 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -235,6 +235,13 @@ class ObjectPermission(models.Model): A mapping of view, add, change, and/or delete permission for users and/or groups to an arbitrary set of objects identified by ORM query parameters. """ + name = models.CharField( + max_length=100, + blank=True + ) + enabled = models.BooleanField( + default=True + ) object_types = models.ManyToManyField( to=ContentType, limit_choices_to={ @@ -270,6 +277,8 @@ class ObjectPermission(models.Model): verbose_name = "Permission" def __str__(self): + if self.name: + return self.name return '{}: {}'.format( ', '.join(self.object_types.values_list('model', flat=True)), ', '.join(self.actions) diff --git a/netbox/users/tests/test_api.py b/netbox/users/tests/test_api.py index 166473710..a485fe9df 100644 --- a/netbox/users/tests/test_api.py +++ b/netbox/users/tests/test_api.py @@ -18,7 +18,7 @@ class AppTest(APITestCase): class ObjectPermissionTest(APIViewTestCases.APIViewTestCase): model = ObjectPermission - brief_fields = ['actions', 'groups', 'id', 'object_types', 'users'] + brief_fields = ['actions', 'enabled', 'groups', 'id', 'name', 'object_types', 'users'] @classmethod def setUpTestData(cls):