Merge branch 'main' into feature
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled

This commit is contained in:
Jeremy Stretch
2025-09-16 12:00:48 -04:00
110 changed files with 299711 additions and 37418 deletions

View File

@@ -1,3 +1,5 @@
import json
from django import forms
from django.conf import settings
from django.contrib.auth import password_validation
@@ -13,7 +15,11 @@ from netbox.preferences import PREFERENCES
from users.constants import *
from users.models import *
from utilities.data import flatten_dict
from utilities.forms.fields import ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.fields import (
ContentTypeMultipleChoiceField,
DynamicModelMultipleChoiceField,
JSONField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker, SplitMultiSelectWidget
from utilities.permissions import qs_filter_from_constraints
@@ -316,13 +322,22 @@ class ObjectPermissionForm(forms.ModelForm):
required=False,
queryset=Group.objects.all()
)
constraints = JSONField(
required=False,
label=_('Constraints'),
help_text=_(
'JSON expression of a queryset filter that will return only permitted objects. Leave null '
'to match all objects of this type. A list of multiple objects will result in a logical OR '
'operation.'
),
)
fieldsets = (
FieldSet('name', 'description', 'enabled'),
FieldSet('can_view', 'can_add', 'can_change', 'can_delete', 'actions', name=_('Actions')),
FieldSet('object_types', name=_('Objects')),
FieldSet('groups', 'users', name=_('Assignment')),
FieldSet('constraints', name=_('Constraints'))
FieldSet('constraints', name=_('Constraints')),
)
class Meta:
@@ -330,13 +345,6 @@ class ObjectPermissionForm(forms.ModelForm):
fields = [
'name', 'description', 'enabled', 'object_types', 'users', 'groups', 'constraints', 'actions',
]
help_texts = {
'constraints': _(
'JSON expression of a queryset filter that will return only permitted objects. Leave null '
'to match all objects of this type. A list of multiple objects will result in a logical OR '
'operation.'
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -344,18 +352,32 @@ class ObjectPermissionForm(forms.ModelForm):
# Make the actions field optional since the form uses it only for non-CRUD actions
self.fields['actions'].required = False
# Populate assigned users and groups
# Prepare the appropriate fields when editing an existing ObjectPermission
if self.instance.pk:
# Populate assigned users and groups
self.fields['groups'].initial = self.instance.groups.values_list('id', flat=True)
self.fields['users'].initial = self.instance.users.values_list('id', flat=True)
# Check the appropriate checkboxes when editing an existing ObjectPermission
if self.instance.pk:
# Check the appropriate checkboxes when editing an existing ObjectPermission
for action in ['view', 'add', 'change', 'delete']:
if action in self.instance.actions:
self.fields[f'can_{action}'].initial = True
self.instance.actions.remove(action)
# Populate initial data for a new ObjectPermission
elif self.initial:
# Handle cloned objects - actions come from initial data (URL parameters)
if 'actions' in self.initial:
if cloned_actions := self.initial['actions']:
for action in ['view', 'add', 'change', 'delete']:
if action in cloned_actions:
self.fields[f'can_{action}'].initial = True
self.initial['actions'].remove(action)
# Convert data delivered via initial data to JSON data
if 'constraints' in self.initial:
if type(self.initial['constraints']) is str:
self.initial['constraints'] = json.loads(self.initial['constraints'])
def clean(self):
super().clean()