From 49d7cda7cfeb79fbd5b56693dcdaa96a211425f0 Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Fri, 15 Jul 2022 19:42:43 +0000 Subject: [PATCH 01/38] rename extended rules --- .devcontainer/configuration/plugins.py | 2 +- netbox_access_lists/api/serializers.py | 14 +++---- netbox_access_lists/api/urls.py | 2 +- netbox_access_lists/api/views.py | 10 ++--- netbox_access_lists/filtersets.py | 6 +-- netbox_access_lists/forms.py | 10 ++--- netbox_access_lists/graphql.py | 10 ++--- .../{0001_initial.py => 0001_accesslist.py} | 39 ++++++++++++++----- netbox_access_lists/models.py | 6 +-- netbox_access_lists/navigation.py | 8 ++-- netbox_access_lists/tables.py | 8 ++-- .../netbox_access_lists/accesslist.html | 2 +- ...trule.html => accesslistextendedrule.html} | 0 netbox_access_lists/urls.py | 14 +++---- netbox_access_lists/views.py | 28 ++++++------- 15 files changed, 89 insertions(+), 70 deletions(-) rename netbox_access_lists/migrations/{0001_initial.py => 0001_accesslist.py} (71%) rename netbox_access_lists/templates/netbox_access_lists/{accesslistrule.html => accesslistextendedrule.html} (100%) diff --git a/.devcontainer/configuration/plugins.py b/.devcontainer/configuration/plugins.py index a92555c..67b1017 100644 --- a/.devcontainer/configuration/plugins.py +++ b/.devcontainer/configuration/plugins.py @@ -6,7 +6,7 @@ PLUGINS = [ "netbox_access_lists", - "netbox_secretstore" + #"netbox_secretstore" ] PLUGINS_CONFIG = { diff --git a/netbox_access_lists/api/serializers.py b/netbox_access_lists/api/serializers.py index a5b92db..f8262bf 100644 --- a/netbox_access_lists/api/serializers.py +++ b/netbox_access_lists/api/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from ipam.api.serializers import NestedPrefixSerializer from dcim.api.serializers import NestedDeviceSerializer from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer -from ..models import AccessList, AccessListRule +from ..models import AccessList, AccessListExtendedRule # @@ -20,13 +20,13 @@ class NestedAccessListSerializer(WritableNestedSerializer): fields = ('id', 'url', 'display', 'name', 'device') -class NestedAccessListRuleSerializer(WritableNestedSerializer): +class NestedAccessListExtendedRuleSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField( - view_name='plugins-api:netbox_access_lists-api:accesslistrule-detail' + view_name='plugins-api:netbox_access_lists-api:accesslistextendedrule-detail' ) class Meta: - model = AccessListRule + model = AccessListExtendedRule fields = ('id', 'url', 'display', 'index') @@ -49,16 +49,16 @@ class AccessListSerializer(NetBoxModelSerializer): ) -class AccessListRuleSerializer(NetBoxModelSerializer): +class AccessListExtendedRuleSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField( - view_name='plugins-api:netbox_access_lists-api:accesslistrule-detail' + view_name='plugins-api:netbox_access_lists-api:accesslistextendedrule-detail' ) access_list = NestedAccessListSerializer() source_prefix = NestedPrefixSerializer() destination_prefix = NestedPrefixSerializer() class Meta: - model = AccessListRule + model = AccessListExtendedRule fields = ( 'id', 'url', 'display', 'access_list', 'index', 'protocol', 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'action', 'tags', 'custom_fields', 'created', diff --git a/netbox_access_lists/api/urls.py b/netbox_access_lists/api/urls.py index 7afe0a2..358e620 100644 --- a/netbox_access_lists/api/urls.py +++ b/netbox_access_lists/api/urls.py @@ -6,6 +6,6 @@ app_name = 'netbox_access_list' router = NetBoxRouter() router.register('access-lists', views.AccessListViewSet) -router.register('access-list-rules', views.AccessListRuleViewSet) +router.register('access-list-rules', views.AccessListExtendedRuleViewSet) urlpatterns = router.urls diff --git a/netbox_access_lists/api/views.py b/netbox_access_lists/api/views.py index fa477dc..023ed12 100644 --- a/netbox_access_lists/api/views.py +++ b/netbox_access_lists/api/views.py @@ -3,7 +3,7 @@ from django.db.models import Count from netbox.api.viewsets import NetBoxModelViewSet from .. import filtersets, models -from .serializers import AccessListSerializer, AccessListRuleSerializer +from .serializers import AccessListSerializer, AccessListExtendedRuleSerializer class AccessListViewSet(NetBoxModelViewSet): @@ -16,9 +16,9 @@ class AccessListViewSet(NetBoxModelViewSet): filterset_class = filtersets.AccessListFilterSet -class AccessListRuleViewSet(NetBoxModelViewSet): - queryset = models.AccessListRule.objects.prefetch_related( +class AccessListExtendedRuleViewSet(NetBoxModelViewSet): + queryset = models.AccessListExtendedRule.objects.prefetch_related( 'access_list', 'source_prefix', 'destination_prefix', 'tags' ) - serializer_class = AccessListRuleSerializer - filterset_class = filtersets.AccessListRuleFilterSet + serializer_class = AccessListExtendedRuleSerializer + filterset_class = filtersets.AccessListExtendedRuleFilterSet diff --git a/netbox_access_lists/filtersets.py b/netbox_access_lists/filtersets.py index a588dbf..0c7480b 100644 --- a/netbox_access_lists/filtersets.py +++ b/netbox_access_lists/filtersets.py @@ -1,5 +1,5 @@ from netbox.filtersets import NetBoxModelFilterSet -from .models import AccessList, AccessListRule +from .models import AccessList, AccessListExtendedRule class AccessListFilterSet(NetBoxModelFilterSet): @@ -11,10 +11,10 @@ class AccessListFilterSet(NetBoxModelFilterSet): return queryset.filter(description__icontains=value) -class AccessListRuleFilterSet(NetBoxModelFilterSet): +class AccessListExtendedRuleFilterSet(NetBoxModelFilterSet): class Meta: - model = AccessListRule + model = AccessListExtendedRule fields = ('id', 'access_list', 'index', 'protocol', 'action', 'remark') def search(self, queryset, name, value): diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index 8c43f75..ffbfe67 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -4,7 +4,7 @@ from extras.models import Tag from ipam.models import Prefix from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm from utilities.forms import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField -from .models import AccessList, AccessListRule, AccessListActionChoices, AccessListProtocolChoices, AccessListTypeChoices +from .models import AccessList, AccessListExtendedRule, AccessListActionChoices, AccessListProtocolChoices, AccessListTypeChoices class AccessListForm(NetBoxModelForm): @@ -34,7 +34,7 @@ class AccessListFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class AccessListRuleForm(NetBoxModelForm): +class AccessListExtendedRuleForm(NetBoxModelForm): access_list = DynamicModelChoiceField( queryset=AccessList.objects.all() ) @@ -50,15 +50,15 @@ class AccessListRuleForm(NetBoxModelForm): ) class Meta: - model = AccessListRule + model = AccessListExtendedRule fields = ( 'access_list', 'index', 'remark', 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'protocol', 'action', 'tags', ) -class AccessListRuleFilterForm(NetBoxModelFilterSetForm): - model = AccessListRule +class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): + model = AccessListExtendedRule access_list = forms.ModelMultipleChoiceField( queryset=AccessList.objects.all(), required=False, diff --git a/netbox_access_lists/graphql.py b/netbox_access_lists/graphql.py index fe84a0b..85fbe91 100644 --- a/netbox_access_lists/graphql.py +++ b/netbox_access_lists/graphql.py @@ -16,12 +16,12 @@ class AccessListType(NetBoxObjectType): filterset_class = filtersets.AccessListFilterSet -class AccessListRuleType(NetBoxObjectType): +class AccessListExtendedRuleType(NetBoxObjectType): class Meta: - model = models.AccessListRule + model = models.AccessListExtendedRule fields = '__all__' - filterset_class = filtersets.AccessListRuleFilterSet + filterset_class = filtersets.AccessListExtendedRuleFilterSet # @@ -32,8 +32,8 @@ class Query(ObjectType): access_list = ObjectField(AccessListType) access_list_list = ObjectListField(AccessListType) - access_list_rule = ObjectField(AccessListRuleType) - access_list_rule_list = ObjectListField(AccessListRuleType) + access_list_rule = ObjectField(AccessListExtendedRuleType) + access_list_rule_list = ObjectListField(AccessListExtendedRuleType) schema = Query diff --git a/netbox_access_lists/migrations/0001_initial.py b/netbox_access_lists/migrations/0001_accesslist.py similarity index 71% rename from netbox_access_lists/migrations/0001_initial.py rename to netbox_access_lists/migrations/0001_accesslist.py index e1f7f4d..492c685 100644 --- a/netbox_access_lists/migrations/0001_initial.py +++ b/netbox_access_lists/migrations/0001_accesslist.py @@ -35,22 +35,41 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='AccessListRule', + name='AccessListStandardRule', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('created', models.DateTimeField(auto_now_add=True, null=True)), ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), - ('index', models.PositiveIntegerField()), - ('protocol', models.CharField(blank=True, max_length=30)), - ('source_ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(), blank=True, null=True, size=None)), - ('destination_ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(), blank=True, null=True, size=None)), - ('action', models.CharField(max_length=30)), - ('remark', models.CharField(blank=True, null=True, max_length=500)), - ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='netbox_access_lists.accesslist')), - ('destination_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), - ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ('index', models.PositiveIntegerField()), + ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='netbox_access_lists.accesslist')), + ('remark', models.CharField(blank=True, null=True, max_length=500)), + ('action', models.CharField(max_length=30)), + ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), + ], + options={ + 'ordering': ('access_list', 'index'), + 'unique_together': {('access_list', 'index')}, + }, + ), + migrations.CreateModel( + name='AccessListExtendedRule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ('index', models.PositiveIntegerField()), + ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='netbox_access_lists.accesslist')), + ('remark', models.CharField(blank=True, null=True, max_length=500)), + ('action', models.CharField(max_length=30)), + ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), + ('source_ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(), blank=True, null=True, size=None)), + ('destination_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), + ('destination_ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(), blank=True, null=True, size=None)), + ('protocol', models.CharField(blank=True, max_length=30)), ], options={ 'ordering': ('access_list', 'index'), diff --git a/netbox_access_lists/models.py b/netbox_access_lists/models.py index 0d0d6b6..6efcea3 100644 --- a/netbox_access_lists/models.py +++ b/netbox_access_lists/models.py @@ -7,7 +7,7 @@ from utilities.choices import ChoiceSet class AccessListActionChoices(ChoiceSet): - key = 'AccessListRule.action' + key = 'AccessListExtendedRule.action' ACTION_DENY = 'deny' ACTION_PERMIT = 'permit' ACTION_REJECT = 'reject' @@ -75,7 +75,7 @@ class AccessList(NetBoxModel): return AccessListTypeChoices.colors.get(self.type) -class AccessListRule(NetBoxModel): +class AccessListExtendedRule(NetBoxModel): access_list = models.ForeignKey( on_delete=models.CASCADE, related_name='rules', @@ -135,7 +135,7 @@ class AccessListRule(NetBoxModel): return f'{self.access_list}: Rule {self.index}' def get_absolute_url(self): - return reverse('plugins:netbox_access_lists:accesslistrule', args=[self.pk]) + return reverse('plugins:netbox_access_lists:accesslistextendedrule', args=[self.pk]) def get_protocol_color(self): return AccessListProtocolChoices.colors.get(self.protocol) diff --git a/netbox_access_lists/navigation.py b/netbox_access_lists/navigation.py index 5fbe6ec..abde06e 100644 --- a/netbox_access_lists/navigation.py +++ b/netbox_access_lists/navigation.py @@ -11,9 +11,9 @@ accesslist_buttons = [ ) ] -accesslistrule_butons = [ +accesslistextendedrule_butons = [ PluginMenuButton( - link='plugins:netbox_access_lists:accesslistrule_add', + link='plugins:netbox_access_lists:accesslistextendedrule_add', title='Add', icon_class='mdi mdi-plus-thick', color=ButtonColorChoices.GREEN @@ -28,8 +28,8 @@ menu_items = ( ), # # Comment out Access List Rule to force creation in the ACL view # PluginMenuItem( - # link='plugins:netbox_access_lists:accesslistrule_list', + # link='plugins:netbox_access_lists:accesslistextendedrule_list', # link_text='Access List Rules', - # buttons=accesslistrule_butons + # buttons=accesslistextendedrule_butons # ), ) diff --git a/netbox_access_lists/tables.py b/netbox_access_lists/tables.py index aa6b5e9..ae49355 100644 --- a/netbox_access_lists/tables.py +++ b/netbox_access_lists/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from netbox.tables import NetBoxTable, columns, ChoiceFieldColumn -from .models import AccessList, AccessListRule +from .models import AccessList, AccessListExtendedRule class AccessListTable(NetBoxTable): @@ -26,7 +26,7 @@ class AccessListTable(NetBoxTable): default_columns = ('name', 'device', 'type', 'rule_count', 'default_action', 'tags') -class AccessListRuleTable(NetBoxTable): +class AccessListExtendedRuleTable(NetBoxTable): access_list = tables.Column( linkify=True ) @@ -36,11 +36,11 @@ class AccessListRuleTable(NetBoxTable): protocol = ChoiceFieldColumn() action = ChoiceFieldColumn() tags = columns.TagColumn( - url_name='plugins:netbox_access_lists:accesslistrule_list' + url_name='plugins:netbox_access_lists:accesslistextendedrule_list' ) class Meta(NetBoxTable.Meta): - model = AccessListRule + model = AccessListExtendedRule fields = ( 'pk', 'id', 'access_list', 'index', 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'protocol', 'action', 'remark', 'actions', 'tags' diff --git a/netbox_access_lists/templates/netbox_access_lists/accesslist.html b/netbox_access_lists/templates/netbox_access_lists/accesslist.html index 43e6d23..7939e50 100644 --- a/netbox_access_lists/templates/netbox_access_lists/accesslist.html +++ b/netbox_access_lists/templates/netbox_access_lists/accesslist.html @@ -7,7 +7,7 @@ {% block controls %}
{% if perms.netbox_access_lists.change_policy %} - + Rule {% endif %} diff --git a/netbox_access_lists/templates/netbox_access_lists/accesslistrule.html b/netbox_access_lists/templates/netbox_access_lists/accesslistextendedrule.html similarity index 100% rename from netbox_access_lists/templates/netbox_access_lists/accesslistrule.html rename to netbox_access_lists/templates/netbox_access_lists/accesslistextendedrule.html diff --git a/netbox_access_lists/urls.py b/netbox_access_lists/urls.py index 6451342..c5cdc32 100644 --- a/netbox_access_lists/urls.py +++ b/netbox_access_lists/urls.py @@ -17,13 +17,13 @@ urlpatterns = ( }), # Access list rules - path('rules/', views.AccessListRuleListView.as_view(), name='accesslistrule_list'), - path('rules/add/', views.AccessListRuleEditView.as_view(), name='accesslistrule_add'), - path('rules//', views.AccessListRuleView.as_view(), name='accesslistrule'), - path('rules//edit/', views.AccessListRuleEditView.as_view(), name='accesslistrule_edit'), - path('rules//delete/', views.AccessListRuleDeleteView.as_view(), name='accesslistrule_delete'), - path('rules//changelog/', ObjectChangeLogView.as_view(), name='accesslistrule_changelog', kwargs={ - 'model': models.AccessListRule + path('rules/', views.AccessListExtendedRuleListView.as_view(), name='accesslistextendedrule_list'), + path('rules/add/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_add'), + path('rules//', views.AccessListExtendedRuleView.as_view(), name='accesslistextendedrule'), + path('rules//edit/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_edit'), + path('rules//delete/', views.AccessListExtendedRuleDeleteView.as_view(), name='accesslistextendedrule_delete'), + path('rules//changelog/', ObjectChangeLogView.as_view(), name='accesslistextendedrule_changelog', kwargs={ + 'model': models.AccessListExtendedRule }), ) diff --git a/netbox_access_lists/views.py b/netbox_access_lists/views.py index 39db83c..66bb406 100644 --- a/netbox_access_lists/views.py +++ b/netbox_access_lists/views.py @@ -12,7 +12,7 @@ class AccessListView(generic.ObjectView): queryset = models.AccessList.objects.all() def get_extra_context(self, request, instance): - table = tables.AccessListRuleTable(instance.rules.all()) + table = tables.AccessListExtendedRuleTable(instance.rules.all()) table.configure(request) return { @@ -39,24 +39,24 @@ class AccessListDeleteView(generic.ObjectDeleteView): # -# AccessListRule views +# AccessListExtendedRule views # -class AccessListRuleView(generic.ObjectView): - queryset = models.AccessListRule.objects.all() +class AccessListExtendedRuleView(generic.ObjectView): + queryset = models.AccessListExtendedRule.objects.all() -class AccessListRuleListView(generic.ObjectListView): - queryset = models.AccessListRule.objects.all() - table = tables.AccessListRuleTable - filterset = filtersets.AccessListRuleFilterSet - filterset_form = forms.AccessListRuleFilterForm +class AccessListExtendedRuleListView(generic.ObjectListView): + queryset = models.AccessListExtendedRule.objects.all() + table = tables.AccessListExtendedRuleTable + filterset = filtersets.AccessListExtendedRuleFilterSet + filterset_form = forms.AccessListExtendedRuleFilterForm -class AccessListRuleEditView(generic.ObjectEditView): - queryset = models.AccessListRule.objects.all() - form = forms.AccessListRuleForm +class AccessListExtendedRuleEditView(generic.ObjectEditView): + queryset = models.AccessListExtendedRule.objects.all() + form = forms.AccessListExtendedRuleForm -class AccessListRuleDeleteView(generic.ObjectDeleteView): - queryset = models.AccessListRule.objects.all() +class AccessListExtendedRuleDeleteView(generic.ObjectDeleteView): + queryset = models.AccessListExtendedRule.objects.all() From 76fa4ab77712d69b56ea61d0bed30a39e23ef0d2 Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Fri, 15 Jul 2022 22:05:00 +0000 Subject: [PATCH 02/38] draft --- netbox_access_lists/api/serializers.py | 31 +++++++- netbox_access_lists/api/views.py | 10 ++- netbox_access_lists/filtersets.py | 12 +++- netbox_access_lists/forms.py | 49 ++++++++++++- netbox_access_lists/graphql.py | 14 ++++ netbox_access_lists/models.py | 70 +++++++++++++++---- netbox_access_lists/navigation.py | 17 ++++- netbox_access_lists/tables.py | 22 +++++- .../netbox_access_lists/accesslist.html | 6 +- netbox_access_lists/urls.py | 24 +++++-- netbox_access_lists/views.py | 33 ++++++++- 11 files changed, 254 insertions(+), 34 deletions(-) diff --git a/netbox_access_lists/api/serializers.py b/netbox_access_lists/api/serializers.py index f8262bf..3717793 100644 --- a/netbox_access_lists/api/serializers.py +++ b/netbox_access_lists/api/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from ipam.api.serializers import NestedPrefixSerializer from dcim.api.serializers import NestedDeviceSerializer from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer -from ..models import AccessList, AccessListExtendedRule +from ..models import AccessList, AccessListExtendedRule, AccessListStandardRule # @@ -20,6 +20,16 @@ class NestedAccessListSerializer(WritableNestedSerializer): fields = ('id', 'url', 'display', 'name', 'device') +class NestedAccessListStandardRuleSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='plugins-api:netbox_access_lists-api:accessliststandardrule-detail' + ) + + class Meta: + model = AccessListStandardRule + fields = ('id', 'url', 'display', 'index') + + class NestedAccessListExtendedRuleSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField( view_name='plugins-api:netbox_access_lists-api:accesslistextendedrule-detail' @@ -45,7 +55,22 @@ class AccessListSerializer(NetBoxModelSerializer): model = AccessList fields = ( 'id', 'url', 'display', 'name', 'device', 'type', 'default_action', 'comments', 'tags', 'custom_fields', 'created', - 'last_updated', 'rule_count', + 'last_updated', 'rule_count' + ) + + +class AccessListStandardRuleSerializer(NetBoxModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='plugins-api:netbox_access_lists-api:accessliststandardrule-detail' + ) + access_list = NestedAccessListSerializer() + source_prefix = NestedPrefixSerializer() + + class Meta: + model = AccessListStandardRule + fields = ( + 'id', 'url', 'display', 'access_list', 'index', 'source_prefix', 'action', + 'tags', 'custom_fields', 'created', 'last_updated' ) @@ -62,5 +87,5 @@ class AccessListExtendedRuleSerializer(NetBoxModelSerializer): fields = ( 'id', 'url', 'display', 'access_list', 'index', 'protocol', 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'action', 'tags', 'custom_fields', 'created', - 'last_updated', + 'last_updated' ) diff --git a/netbox_access_lists/api/views.py b/netbox_access_lists/api/views.py index 023ed12..4272871 100644 --- a/netbox_access_lists/api/views.py +++ b/netbox_access_lists/api/views.py @@ -3,7 +3,7 @@ from django.db.models import Count from netbox.api.viewsets import NetBoxModelViewSet from .. import filtersets, models -from .serializers import AccessListSerializer, AccessListExtendedRuleSerializer +from .serializers import AccessListSerializer, AccessListExtendedRuleSerializer, AccessListStandardRuleSerializer class AccessListViewSet(NetBoxModelViewSet): @@ -16,6 +16,14 @@ class AccessListViewSet(NetBoxModelViewSet): filterset_class = filtersets.AccessListFilterSet +class AccessListStandardRuleViewSet(NetBoxModelViewSet): + queryset = models.AccessListStandardRule.objects.prefetch_related( + 'access_list', 'source_prefix', 'tags' + ) + serializer_class = AccessListStandardRuleSerializer + filterset_class = filtersets.AccessListStandardRuleFilterSet + + class AccessListExtendedRuleViewSet(NetBoxModelViewSet): queryset = models.AccessListExtendedRule.objects.prefetch_related( 'access_list', 'source_prefix', 'destination_prefix', 'tags' diff --git a/netbox_access_lists/filtersets.py b/netbox_access_lists/filtersets.py index 0c7480b..5f76d81 100644 --- a/netbox_access_lists/filtersets.py +++ b/netbox_access_lists/filtersets.py @@ -1,5 +1,5 @@ from netbox.filtersets import NetBoxModelFilterSet -from .models import AccessList, AccessListExtendedRule +from .models import AccessList, AccessListExtendedRule, AccessListStandardRule class AccessListFilterSet(NetBoxModelFilterSet): @@ -19,3 +19,13 @@ class AccessListExtendedRuleFilterSet(NetBoxModelFilterSet): def search(self, queryset, name, value): return queryset.filter(description__icontains=value) + + +class AccessListStandardRuleFilterSet(NetBoxModelFilterSet): + + class Meta: + model = AccessListStandardRule + fields = ('id', 'access_list', 'index', 'action', 'remark') + + def search(self, queryset, name, value): + return queryset.filter(description__icontains=value) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index ffbfe67..363cbd8 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -4,7 +4,7 @@ from extras.models import Tag from ipam.models import Prefix from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm from utilities.forms import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField -from .models import AccessList, AccessListExtendedRule, AccessListActionChoices, AccessListProtocolChoices, AccessListTypeChoices +from .models import AccessList, AccessListExtendedRule, AccessListActionChoices, AccessListProtocolChoices, AccessListTypeChoices, AccessListStandardRule class AccessListForm(NetBoxModelForm): @@ -36,7 +36,10 @@ class AccessListFilterForm(NetBoxModelFilterSetForm): class AccessListExtendedRuleForm(NetBoxModelForm): access_list = DynamicModelChoiceField( - queryset=AccessList.objects.all() + queryset=AccessList.objects.all(), + query_params={ + 'type': 'extended' + } ) source_prefix = DynamicModelChoiceField( queryset=Prefix.objects.all() @@ -53,7 +56,7 @@ class AccessListExtendedRuleForm(NetBoxModelForm): model = AccessListExtendedRule fields = ( 'access_list', 'index', 'remark', 'source_prefix', 'source_ports', 'destination_prefix', - 'destination_ports', 'protocol', 'action', 'tags', + 'destination_ports', 'protocol', 'action', 'tags' ) @@ -78,3 +81,43 @@ class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): widget=StaticSelectMultiple() ) tag = TagFilterField(model) + + +class AccessListStandardRuleForm(NetBoxModelForm): + access_list = DynamicModelChoiceField( + queryset=AccessList.objects.all(), + query_params={ + 'type': 'standard' + } + ) + source_prefix = DynamicModelChoiceField( + queryset=Prefix.objects.all() + ) + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False + ) + + class Meta: + model = AccessListStandardRule + fields = ( + 'access_list', 'index', 'remark', 'source_prefix', 'action', 'tags' + ) + + +class AccessListStandardRuleFilterForm(NetBoxModelFilterSetForm): + model = AccessListStandardRule + access_list = forms.ModelMultipleChoiceField( + queryset=AccessList.objects.all(), + required=False, + widget=StaticSelectMultiple() + ) + index = forms.IntegerField( + required=False + ) + action = forms.MultipleChoiceField( + choices=AccessListActionChoices, + required=False, + widget=StaticSelectMultiple() + ) + tag = TagFilterField(model) diff --git a/netbox_access_lists/graphql.py b/netbox_access_lists/graphql.py index 85fbe91..1a7795f 100644 --- a/netbox_access_lists/graphql.py +++ b/netbox_access_lists/graphql.py @@ -24,6 +24,13 @@ class AccessListExtendedRuleType(NetBoxObjectType): filterset_class = filtersets.AccessListExtendedRuleFilterSet +#class AccessListStandardRuleType(NetBoxObjectType): +# +# class Meta: +# model = models.AccessListStandardRule +# fields = '__all__' +# filterset_class = filtersets.AccessListStandardRuleFilterSet + # # Queries # @@ -36,4 +43,11 @@ class Query(ObjectType): access_list_rule_list = ObjectListField(AccessListExtendedRuleType) +#class Query(ObjectType): +# access_list = ObjectField(AccessListType) +# access_list_list = ObjectListField(AccessListType) +# +# access_list_rule = ObjectField(AccessListStandardRuleType) +# access_list_rule_list = ObjectListField(AccessListStandardRuleType) + schema = Query diff --git a/netbox_access_lists/models.py b/netbox_access_lists/models.py index 6efcea3..a300808 100644 --- a/netbox_access_lists/models.py +++ b/netbox_access_lists/models.py @@ -75,6 +75,50 @@ class AccessList(NetBoxModel): return AccessListTypeChoices.colors.get(self.type) +#class AccessListRule(NetBoxModel): + + +class AccessListStandardRule(NetBoxModel): + access_list = models.ForeignKey( + on_delete=models.CASCADE, + related_name='standard_acl_rules', + to=AccessList, + verbose_name='Access List' + ) + index = models.PositiveIntegerField() + remark = models.CharField( + max_length=200, + blank=True, + null=True + ) + action = models.CharField( + choices=AccessListActionChoices, + default=AccessListActionChoices.ACTION_PERMIT, + max_length=30, + ) + source_prefix = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name='+', + to='ipam.Prefix', + verbose_name='Source Prefix' + ) + + class Meta: + ordering = ('access_list', 'index') + unique_together = ('access_list', 'index') + + def __str__(self): + return f'{self.access_list}: Rule {self.index}' + + def get_action_color(self): + return AccessListActionChoices.colors.get(self.action) + + def get_absolute_url(self): + return reverse('plugins:netbox_access_lists:accessliststandardrule', args=[self.pk]) + + class AccessListExtendedRule(NetBoxModel): access_list = models.ForeignKey( on_delete=models.CASCADE, @@ -83,9 +127,14 @@ class AccessListExtendedRule(NetBoxModel): verbose_name='Access List' ) index = models.PositiveIntegerField() - protocol = models.CharField( + remark = models.CharField( + max_length=200, blank=True, - choices=AccessListProtocolChoices, + null=True + ) + action = models.CharField( + choices=AccessListActionChoices, + default=AccessListActionChoices.ACTION_PERMIT, max_length=30, ) source_prefix = models.ForeignKey( @@ -116,15 +165,10 @@ class AccessListExtendedRule(NetBoxModel): null=True, verbose_name='Destination Ports' ) - action = models.CharField( - choices=AccessListActionChoices, - default=AccessListActionChoices.ACTION_PERMIT, - max_length=30, - ) - remark = models.CharField( - max_length=200, + protocol = models.CharField( blank=True, - null=True + choices=AccessListProtocolChoices, + max_length=30, ) class Meta: @@ -134,11 +178,11 @@ class AccessListExtendedRule(NetBoxModel): def __str__(self): return f'{self.access_list}: Rule {self.index}' + def get_action_color(self): + return AccessListActionChoices.colors.get(self.action) + def get_absolute_url(self): return reverse('plugins:netbox_access_lists:accesslistextendedrule', args=[self.pk]) def get_protocol_color(self): return AccessListProtocolChoices.colors.get(self.protocol) - - def get_action_color(self): - return AccessListActionChoices.colors.get(self.action) diff --git a/netbox_access_lists/navigation.py b/netbox_access_lists/navigation.py index abde06e..9265dd6 100644 --- a/netbox_access_lists/navigation.py +++ b/netbox_access_lists/navigation.py @@ -11,6 +11,15 @@ accesslist_buttons = [ ) ] +#accessliststandardrule_butons = [ +# PluginMenuButton( +# link='plugins:netbox_access_lists:accessliststandardrule_add', +# title='Add', +# icon_class='mdi mdi-plus-thick', +# color=ButtonColorChoices.GREEN +# ) +#] + accesslistextendedrule_butons = [ PluginMenuButton( link='plugins:netbox_access_lists:accesslistextendedrule_add', @@ -26,7 +35,13 @@ menu_items = ( link_text='Access Lists', buttons=accesslist_buttons ), - # # Comment out Access List Rule to force creation in the ACL view + # # Comment out Standard Access List Rule to force creation in the ACL view + # PluginMenuItem( + # link='plugins:netbox_access_lists:accessliststandardrule_list', + # link_text='Access List Rules', + # buttons=accessliststandardrule_butons + # ), + # # Comment out Extended Access List Rule to force creation in the ACL view # PluginMenuItem( # link='plugins:netbox_access_lists:accesslistextendedrule_list', # link_text='Access List Rules', diff --git a/netbox_access_lists/tables.py b/netbox_access_lists/tables.py index ae49355..a89d5b4 100644 --- a/netbox_access_lists/tables.py +++ b/netbox_access_lists/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from netbox.tables import NetBoxTable, columns, ChoiceFieldColumn -from .models import AccessList, AccessListExtendedRule +from .models import AccessList, AccessListExtendedRule, AccessListStandardRule class AccessListTable(NetBoxTable): @@ -26,6 +26,26 @@ class AccessListTable(NetBoxTable): default_columns = ('name', 'device', 'type', 'rule_count', 'default_action', 'tags') +class AccessListStandardRuleTable(NetBoxTable): + access_list = tables.Column( + linkify=True + ) + index = tables.Column( + linkify=True + ) + action = ChoiceFieldColumn() + tags = columns.TagColumn( + url_name='plugins:netbox_access_lists:accessliststandardrule_list' + ) + class Meta(NetBoxTable.Meta): + model = AccessListStandardRule + fields = ( + 'pk', 'id', 'access_list', 'index', 'source_prefix', 'action', 'remark', 'tags' + ) + default_columns = ( + 'access_list', 'index', 'source_prefix', 'action', 'remark', 'tags' + ) + class AccessListExtendedRuleTable(NetBoxTable): access_list = tables.Column( linkify=True diff --git a/netbox_access_lists/templates/netbox_access_lists/accesslist.html b/netbox_access_lists/templates/netbox_access_lists/accesslist.html index 7939e50..9870749 100644 --- a/netbox_access_lists/templates/netbox_access_lists/accesslist.html +++ b/netbox_access_lists/templates/netbox_access_lists/accesslist.html @@ -7,7 +7,11 @@ {% block controls %}
{% if perms.netbox_access_lists.change_policy %} + {% if object.type == 'extended' %} + {% elif object.type == 'standard' %} + + {% endif %} Rule {% endif %} @@ -74,7 +78,7 @@
-
Rules
+
{{ object.get_type_display }} Rules
{% render_table rules_table %}
diff --git a/netbox_access_lists/urls.py b/netbox_access_lists/urls.py index c5cdc32..0ade3db 100644 --- a/netbox_access_lists/urls.py +++ b/netbox_access_lists/urls.py @@ -16,13 +16,23 @@ urlpatterns = ( 'model': models.AccessList }), - # Access list rules - path('rules/', views.AccessListExtendedRuleListView.as_view(), name='accesslistextendedrule_list'), - path('rules/add/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_add'), - path('rules//', views.AccessListExtendedRuleView.as_view(), name='accesslistextendedrule'), - path('rules//edit/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_edit'), - path('rules//delete/', views.AccessListExtendedRuleDeleteView.as_view(), name='accesslistextendedrule_delete'), - path('rules//changelog/', ObjectChangeLogView.as_view(), name='accesslistextendedrule_changelog', kwargs={ + # Standard Access list rules + path('standard-rules/', views.AccessListStandardRuleListView.as_view(), name='accessliststandardrule_list'), + path('standard-rules/add/', views.AccessListStandardRuleEditView.as_view(), name='accessliststandardrule_add'), + path('standard-rules//', views.AccessListStandardRuleView.as_view(), name='accessliststandardrule'), + path('standard-rules//edit/', views.AccessListStandardRuleEditView.as_view(), name='accessliststandardrule_edit'), + path('standard-rules//delete/', views.AccessListStandardRuleDeleteView.as_view(), name='accessliststandardrule_delete'), + path('standard-rules//changelog/', ObjectChangeLogView.as_view(), name='accessliststandardrule_changelog', kwargs={ + 'model': models.AccessListStandardRule + }), + + # Extended Access list rules + path('extended-rules/', views.AccessListExtendedRuleListView.as_view(), name='accesslistextendedrule_list'), + path('extended-rules/add/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_add'), + path('extended-rules//', views.AccessListExtendedRuleView.as_view(), name='accesslistextendedrule'), + path('extended-rules//edit/', views.AccessListExtendedRuleEditView.as_view(), name='accesslistextendedrule_edit'), + path('extended-rules//delete/', views.AccessListExtendedRuleDeleteView.as_view(), name='accesslistextendedrule_delete'), + path('extended-rules//changelog/', ObjectChangeLogView.as_view(), name='accesslistextendedrule_changelog', kwargs={ 'model': models.AccessListExtendedRule }), diff --git a/netbox_access_lists/views.py b/netbox_access_lists/views.py index 66bb406..fb30e85 100644 --- a/netbox_access_lists/views.py +++ b/netbox_access_lists/views.py @@ -12,17 +12,20 @@ class AccessListView(generic.ObjectView): queryset = models.AccessList.objects.all() def get_extra_context(self, request, instance): - table = tables.AccessListExtendedRuleTable(instance.rules.all()) + if instance.type == 'extended': + table = tables.AccessListExtendedRuleTable(instance.rules.all()) + elif instance.type == 'standard': + table = tables.AccessListStandardRuleTable(instance.standard_acl_rules.all()) table.configure(request) return { - 'rules_table': table, + 'rules_table': table } class AccessListListView(generic.ObjectListView): queryset = models.AccessList.objects.annotate( - rule_count=Count('rules') + rule_count=Count('rules') + Count('standard_acl_rules') ) table = tables.AccessListTable filterset = filtersets.AccessListFilterSet @@ -38,6 +41,30 @@ class AccessListDeleteView(generic.ObjectDeleteView): queryset = models.AccessList.objects.all() +# +# AccessListStandardRule views +# + +class AccessListStandardRuleView(generic.ObjectView): + queryset = models.AccessListStandardRule.objects.all() + + +class AccessListStandardRuleListView(generic.ObjectListView): + queryset = models.AccessListStandardRule.objects.all() + table = tables.AccessListStandardRuleTable + filterset = filtersets.AccessListStandardRuleFilterSet + filterset_form = forms.AccessListStandardRuleFilterForm + + +class AccessListStandardRuleEditView(generic.ObjectEditView): + queryset = models.AccessListStandardRule.objects.all() + form = forms.AccessListStandardRuleForm + + +class AccessListStandardRuleDeleteView(generic.ObjectDeleteView): + queryset = models.AccessListStandardRule.objects.all() + + # # AccessListExtendedRule views # From 67d40d1e183ebf8489528eeb6c80fc6f1eb2b55d Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Fri, 15 Jul 2022 22:45:44 +0000 Subject: [PATCH 03/38] add navigation --- netbox_access_lists/navigation.py | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/netbox_access_lists/navigation.py b/netbox_access_lists/navigation.py index 9265dd6..fc4ed97 100644 --- a/netbox_access_lists/navigation.py +++ b/netbox_access_lists/navigation.py @@ -11,14 +11,14 @@ accesslist_buttons = [ ) ] -#accessliststandardrule_butons = [ -# PluginMenuButton( -# link='plugins:netbox_access_lists:accessliststandardrule_add', -# title='Add', -# icon_class='mdi mdi-plus-thick', -# color=ButtonColorChoices.GREEN -# ) -#] +accessliststandardrule_butons = [ + PluginMenuButton( + link='plugins:netbox_access_lists:accessliststandardrule_add', + title='Add', + icon_class='mdi mdi-plus-thick', + color=ButtonColorChoices.GREEN + ) +] accesslistextendedrule_butons = [ PluginMenuButton( @@ -35,16 +35,16 @@ menu_items = ( link_text='Access Lists', buttons=accesslist_buttons ), - # # Comment out Standard Access List Rule to force creation in the ACL view - # PluginMenuItem( - # link='plugins:netbox_access_lists:accessliststandardrule_list', - # link_text='Access List Rules', - # buttons=accessliststandardrule_butons - # ), - # # Comment out Extended Access List Rule to force creation in the ACL view - # PluginMenuItem( - # link='plugins:netbox_access_lists:accesslistextendedrule_list', - # link_text='Access List Rules', - # buttons=accesslistextendedrule_butons - # ), + # Comment out Standard Access List Rule to force creation in the ACL view + PluginMenuItem( + link='plugins:netbox_access_lists:accessliststandardrule_list', + link_text='Standard Access List Rules', + buttons=accessliststandardrule_butons + ), + # Comment out Extended Access List Rule to force creation in the ACL view + PluginMenuItem( + link='plugins:netbox_access_lists:accesslistextendedrule_list', + link_text='Extended Access List Rules', + buttons=accesslistextendedrule_butons + ), ) From cb0117ae944417733893fe0ee08ea794d15fc8cc Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Fri, 15 Jul 2022 22:46:07 +0000 Subject: [PATCH 04/38] add standardacl template --- .../accessliststandardrule.html | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 netbox_access_lists/templates/netbox_access_lists/accessliststandardrule.html diff --git a/netbox_access_lists/templates/netbox_access_lists/accessliststandardrule.html b/netbox_access_lists/templates/netbox_access_lists/accessliststandardrule.html new file mode 100644 index 0000000..ea5e41e --- /dev/null +++ b/netbox_access_lists/templates/netbox_access_lists/accessliststandardrule.html @@ -0,0 +1,76 @@ +{% extends 'generic/object.html' %} + +{% block content %} +
+
+
+
Access List Rule
+
+ + + + + + + + + +
Access List + {{ object.access_list }} +
Index{{ object.index }}
+
+
+ {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} +
+
+
+
Details
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Remark{{ object.get_remark_display|placeholder }}
Protocol{{ object.get_protocol_display|placeholder }}
Source Prefix + {% if object.source_prefix %} + {{ object.source_prefix }} + {% else %} + {{ ''|placeholder }} + {% endif %} +
Source Ports{{ object.source_ports|join:", "|placeholder }}
Destination Prefix + {% if object.destination_prefix %} + {{ object.destination_prefix }} + {% else %} + {{ ''|placeholder }} + {% endif %} +
Destination Ports{{ object.destination_ports|join:", "|placeholder }}
Action{{ object.get_action_display }}
+
+
+
+
+{% endblock content %} From 00e707f27d8942560922e00fb1be0f31eec059ea Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Fri, 15 Jul 2022 23:00:53 +0000 Subject: [PATCH 05/38] remove required fields --- netbox_access_lists/forms.py | 9 ++++++--- netbox_access_lists/models.py | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index 363cbd8..a7f469f 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -42,10 +42,12 @@ class AccessListExtendedRuleForm(NetBoxModelForm): } ) source_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all() + queryset=Prefix.objects.all(), + required=False ) destination_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all() + queryset=Prefix.objects.all(), + required=False ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), @@ -91,7 +93,8 @@ class AccessListStandardRuleForm(NetBoxModelForm): } ) source_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all() + queryset=Prefix.objects.all(), + required=False ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), diff --git a/netbox_access_lists/models.py b/netbox_access_lists/models.py index a300808..c430248 100644 --- a/netbox_access_lists/models.py +++ b/netbox_access_lists/models.py @@ -92,6 +92,8 @@ class AccessListStandardRule(NetBoxModel): null=True ) action = models.CharField( + blank=True, + null=True, choices=AccessListActionChoices, default=AccessListActionChoices.ACTION_PERMIT, max_length=30, @@ -133,6 +135,8 @@ class AccessListExtendedRule(NetBoxModel): null=True ) action = models.CharField( + blank=True, + null=True, choices=AccessListActionChoices, default=AccessListActionChoices.ACTION_PERMIT, max_length=30, From 588b5ee9511fd335bdaaaada16e7d9c0bcae3ea8 Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sat, 16 Jul 2022 13:01:11 +0000 Subject: [PATCH 06/38] update devcontainer plugins --- .devcontainer/devcontainer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a56c043..a49e525 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -65,7 +65,9 @@ "aaron-bond.better-comments", "GitHub.codespaces", "codezombiech.gitignore", - "Tyriar.sort-lines" + "Tyriar.sort-lines", + "GitHub.vscode-pull-request-github", + "sourcery.sourcery" ] } }, From 6dee870249763542cd7956e0251e8611f69ca02e Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sat, 16 Jul 2022 13:29:22 +0000 Subject: [PATCH 07/38] update extendedACL related_name --- netbox_access_lists/api/views.py | 2 +- netbox_access_lists/migrations/0001_accesslist.py | 4 ++-- netbox_access_lists/models.py | 2 +- netbox_access_lists/views.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/netbox_access_lists/api/views.py b/netbox_access_lists/api/views.py index 4272871..e0d4dd4 100644 --- a/netbox_access_lists/api/views.py +++ b/netbox_access_lists/api/views.py @@ -10,7 +10,7 @@ class AccessListViewSet(NetBoxModelViewSet): queryset = models.AccessList.objects.prefetch_related( 'device', 'tags' ).annotate( - rule_count=Count('rules') + rule_count=Count('extended_acl_rules') + Count('standard_acl_rules') ) serializer_class = AccessListSerializer filterset_class = filtersets.AccessListFilterSet diff --git a/netbox_access_lists/migrations/0001_accesslist.py b/netbox_access_lists/migrations/0001_accesslist.py index 492c685..561d27e 100644 --- a/netbox_access_lists/migrations/0001_accesslist.py +++ b/netbox_access_lists/migrations/0001_accesslist.py @@ -43,7 +43,7 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ('index', models.PositiveIntegerField()), - ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='netbox_access_lists.accesslist')), + ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='standard_acl_rules', to='netbox_access_lists.accesslist')), ('remark', models.CharField(blank=True, null=True, max_length=500)), ('action', models.CharField(max_length=30)), ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), @@ -62,7 +62,7 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ('index', models.PositiveIntegerField()), - ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='netbox_access_lists.accesslist')), + ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='extended_acl_rules', to='netbox_access_lists.accesslist')), ('remark', models.CharField(blank=True, null=True, max_length=500)), ('action', models.CharField(max_length=30)), ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), diff --git a/netbox_access_lists/models.py b/netbox_access_lists/models.py index c430248..4ec2647 100644 --- a/netbox_access_lists/models.py +++ b/netbox_access_lists/models.py @@ -124,7 +124,7 @@ class AccessListStandardRule(NetBoxModel): class AccessListExtendedRule(NetBoxModel): access_list = models.ForeignKey( on_delete=models.CASCADE, - related_name='rules', + related_name='extended_acl_rules', to=AccessList, verbose_name='Access List' ) diff --git a/netbox_access_lists/views.py b/netbox_access_lists/views.py index fb30e85..829760c 100644 --- a/netbox_access_lists/views.py +++ b/netbox_access_lists/views.py @@ -13,7 +13,7 @@ class AccessListView(generic.ObjectView): def get_extra_context(self, request, instance): if instance.type == 'extended': - table = tables.AccessListExtendedRuleTable(instance.rules.all()) + table = tables.AccessListExtendedRuleTable(instance.extended_acl_rules.all()) elif instance.type == 'standard': table = tables.AccessListStandardRuleTable(instance.standard_acl_rules.all()) table.configure(request) @@ -25,7 +25,7 @@ class AccessListView(generic.ObjectView): class AccessListListView(generic.ObjectListView): queryset = models.AccessList.objects.annotate( - rule_count=Count('rules') + Count('standard_acl_rules') + rule_count=Count('extended_acl_rules') + Count('standard_acl_rules') ) table = tables.AccessListTable filterset = filtersets.AccessListFilterSet From f46141361966967c1a6f029799d51e33deecb10d Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sat, 16 Jul 2022 14:41:56 +0000 Subject: [PATCH 08/38] correct api issues --- netbox_access_lists/api/serializers.py | 10 +-- netbox_access_lists/api/urls.py | 3 +- netbox_access_lists/api/views.py | 4 +- netbox_access_lists/filtersets.py | 20 ++--- netbox_access_lists/forms.py | 114 +++++++++++++------------ netbox_access_lists/tables.py | 15 ++-- 6 files changed, 85 insertions(+), 81 deletions(-) diff --git a/netbox_access_lists/api/serializers.py b/netbox_access_lists/api/serializers.py index 3717793..2b885e3 100644 --- a/netbox_access_lists/api/serializers.py +++ b/netbox_access_lists/api/serializers.py @@ -69,8 +69,8 @@ class AccessListStandardRuleSerializer(NetBoxModelSerializer): class Meta: model = AccessListStandardRule fields = ( - 'id', 'url', 'display', 'access_list', 'index', 'source_prefix', 'action', - 'tags', 'custom_fields', 'created', 'last_updated' + 'id', 'url', 'display', 'access_list', 'index', 'action', 'tags', + 'created', 'custom_fields', 'last_updated', 'source_prefix' ) @@ -85,7 +85,7 @@ class AccessListExtendedRuleSerializer(NetBoxModelSerializer): class Meta: model = AccessListExtendedRule fields = ( - 'id', 'url', 'display', 'access_list', 'index', 'protocol', 'source_prefix', 'source_ports', - 'destination_prefix', 'destination_ports', 'action', 'tags', 'custom_fields', 'created', - 'last_updated' + 'id', 'url', 'display', 'access_list', 'index', 'action', 'tags', + 'created', 'custom_fields', 'last_updated', 'source_prefix', 'source_ports', + 'destination_prefix', 'destination_ports', 'protocol' ) diff --git a/netbox_access_lists/api/urls.py b/netbox_access_lists/api/urls.py index 358e620..411ee89 100644 --- a/netbox_access_lists/api/urls.py +++ b/netbox_access_lists/api/urls.py @@ -6,6 +6,7 @@ app_name = 'netbox_access_list' router = NetBoxRouter() router.register('access-lists', views.AccessListViewSet) -router.register('access-list-rules', views.AccessListExtendedRuleViewSet) +router.register('standard-acl-rules', views.AccessListStandardRuleViewSet) +router.register('extended-acl-rules', views.AccessListExtendedRuleViewSet) urlpatterns = router.urls diff --git a/netbox_access_lists/api/views.py b/netbox_access_lists/api/views.py index e0d4dd4..ead8156 100644 --- a/netbox_access_lists/api/views.py +++ b/netbox_access_lists/api/views.py @@ -18,7 +18,7 @@ class AccessListViewSet(NetBoxModelViewSet): class AccessListStandardRuleViewSet(NetBoxModelViewSet): queryset = models.AccessListStandardRule.objects.prefetch_related( - 'access_list', 'source_prefix', 'tags' + 'access_list', 'tags', 'source_prefix' ) serializer_class = AccessListStandardRuleSerializer filterset_class = filtersets.AccessListStandardRuleFilterSet @@ -26,7 +26,7 @@ class AccessListStandardRuleViewSet(NetBoxModelViewSet): class AccessListExtendedRuleViewSet(NetBoxModelViewSet): queryset = models.AccessListExtendedRule.objects.prefetch_related( - 'access_list', 'source_prefix', 'destination_prefix', 'tags' + 'access_list', 'tags', 'source_prefix', 'destination_prefix', ) serializer_class = AccessListExtendedRuleSerializer filterset_class = filtersets.AccessListExtendedRuleFilterSet diff --git a/netbox_access_lists/filtersets.py b/netbox_access_lists/filtersets.py index 5f76d81..f4bc50d 100644 --- a/netbox_access_lists/filtersets.py +++ b/netbox_access_lists/filtersets.py @@ -11,16 +11,6 @@ class AccessListFilterSet(NetBoxModelFilterSet): return queryset.filter(description__icontains=value) -class AccessListExtendedRuleFilterSet(NetBoxModelFilterSet): - - class Meta: - model = AccessListExtendedRule - fields = ('id', 'access_list', 'index', 'protocol', 'action', 'remark') - - def search(self, queryset, name, value): - return queryset.filter(description__icontains=value) - - class AccessListStandardRuleFilterSet(NetBoxModelFilterSet): class Meta: @@ -29,3 +19,13 @@ class AccessListStandardRuleFilterSet(NetBoxModelFilterSet): def search(self, queryset, name, value): return queryset.filter(description__icontains=value) + + +class AccessListExtendedRuleFilterSet(NetBoxModelFilterSet): + + class Meta: + model = AccessListExtendedRule + fields = ('id', 'access_list', 'index', 'action', 'remark', 'protocol') + + def search(self, queryset, name, value): + return queryset.filter(description__icontains=value) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index a7f469f..fa42a56 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -34,57 +34,6 @@ class AccessListFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class AccessListExtendedRuleForm(NetBoxModelForm): - access_list = DynamicModelChoiceField( - queryset=AccessList.objects.all(), - query_params={ - 'type': 'extended' - } - ) - source_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all(), - required=False - ) - destination_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all(), - required=False - ) - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) - - class Meta: - model = AccessListExtendedRule - fields = ( - 'access_list', 'index', 'remark', 'source_prefix', 'source_ports', 'destination_prefix', - 'destination_ports', 'protocol', 'action', 'tags' - ) - - -class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): - model = AccessListExtendedRule - access_list = forms.ModelMultipleChoiceField( - queryset=AccessList.objects.all(), - required=False, - widget=StaticSelectMultiple() - ) - index = forms.IntegerField( - required=False - ) - protocol = forms.MultipleChoiceField( - choices=AccessListProtocolChoices, - required=False, - widget=StaticSelectMultiple() - ) - action = forms.MultipleChoiceField( - choices=AccessListActionChoices, - required=False, - widget=StaticSelectMultiple() - ) - tag = TagFilterField(model) - - class AccessListStandardRuleForm(NetBoxModelForm): access_list = DynamicModelChoiceField( queryset=AccessList.objects.all(), @@ -92,19 +41,20 @@ class AccessListStandardRuleForm(NetBoxModelForm): 'type': 'standard' } ) - source_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all(), - required=False - ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) + source_prefix = DynamicModelChoiceField( + queryset=Prefix.objects.all(), + required=False + ) + class Meta: model = AccessListStandardRule fields = ( - 'access_list', 'index', 'remark', 'source_prefix', 'action', 'tags' + 'access_list', 'index', 'remark', 'action', 'tags', 'source_prefix', ) @@ -118,9 +68,61 @@ class AccessListStandardRuleFilterForm(NetBoxModelFilterSetForm): index = forms.IntegerField( required=False ) + tag = TagFilterField(model) action = forms.MultipleChoiceField( choices=AccessListActionChoices, required=False, widget=StaticSelectMultiple() ) + + +class AccessListExtendedRuleForm(NetBoxModelForm): + access_list = DynamicModelChoiceField( + queryset=AccessList.objects.all(), + query_params={ + 'type': 'extended' + } + ) + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False + ) + source_prefix = DynamicModelChoiceField( + queryset=Prefix.objects.all(), + required=False + ) + destination_prefix = DynamicModelChoiceField( + queryset=Prefix.objects.all(), + required=False + ) + + + class Meta: + model = AccessListExtendedRule + fields = ( + 'access_list', 'index', 'remark', 'action', 'tags', 'source_prefix', + 'source_ports', 'destination_prefix', 'destination_ports', 'protocol' + ) + + +class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): + model = AccessListExtendedRule + access_list = forms.ModelMultipleChoiceField( + queryset=AccessList.objects.all(), + required=False, + widget=StaticSelectMultiple() + ) + index = forms.IntegerField( + required=False + ) tag = TagFilterField(model) + action = forms.MultipleChoiceField( + choices=AccessListActionChoices, + required=False, + widget=StaticSelectMultiple() + ) + protocol = forms.MultipleChoiceField( + choices=AccessListProtocolChoices, + required=False, + widget=StaticSelectMultiple() + ) diff --git a/netbox_access_lists/tables.py b/netbox_access_lists/tables.py index a89d5b4..5a5bf57 100644 --- a/netbox_access_lists/tables.py +++ b/netbox_access_lists/tables.py @@ -40,12 +40,13 @@ class AccessListStandardRuleTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = AccessListStandardRule fields = ( - 'pk', 'id', 'access_list', 'index', 'source_prefix', 'action', 'remark', 'tags' + 'pk', 'id', 'access_list', 'index', 'action', 'actions', 'remark', 'tags' ) default_columns = ( - 'access_list', 'index', 'source_prefix', 'action', 'remark', 'tags' + 'access_list', 'index', 'action', 'actions', 'remark', 'tags' ) + class AccessListExtendedRuleTable(NetBoxTable): access_list = tables.Column( linkify=True @@ -53,19 +54,19 @@ class AccessListExtendedRuleTable(NetBoxTable): index = tables.Column( linkify=True ) - protocol = ChoiceFieldColumn() action = ChoiceFieldColumn() tags = columns.TagColumn( url_name='plugins:netbox_access_lists:accesslistextendedrule_list' ) + protocol = ChoiceFieldColumn() class Meta(NetBoxTable.Meta): model = AccessListExtendedRule fields = ( - 'pk', 'id', 'access_list', 'index', 'source_prefix', 'source_ports', 'destination_prefix', - 'destination_ports', 'protocol', 'action', 'remark', 'actions', 'tags' + 'pk', 'id', 'access_list', 'index', 'action', 'actions', 'remark', 'tags', + 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'protocol' ) default_columns = ( - 'access_list', 'index', 'remark', 'source_prefix', 'source_ports', 'destination_prefix', - 'destination_ports', 'protocol', 'action', 'actions', 'tags' + 'access_list', 'index', 'action', 'actions', 'remark', 'tags', + 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'protocol' ) From 8186fbb6b541b75fc71dfe09da27a637e0cbe512 Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sat, 16 Jul 2022 17:25:54 +0000 Subject: [PATCH 09/38] from logic --- netbox_access_lists/forms.py | 47 +++++++++++++++++++ .../migrations/0001_accesslist.py | 4 +- netbox_access_lists/models.py | 2 - 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index fa42a56..8d0a552 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import ValidationError from extras.models import Tag from ipam.models import Prefix @@ -18,6 +19,22 @@ class AccessListForm(NetBoxModelForm): model = AccessList fields = ('name', 'device', 'type', 'default_action', 'comments', 'tags') + #def validate_unique(self, value): + # accesslists = AccessList.objects.exclude(pk=self.pk) + # if accesslists.filter(name=self.name, device=self.device).exists(): + # raise ValidationError({ + # "name": f"An Access List with this name on device {self.device} already exists." + # }) + + def clean(self): + cleaned_data = super().clean() + if self.errors.get('name'): + return cleaned_data + name = cleaned_data.get('name') + device = cleaned_data.get('device') + if ('name' in self.changed_data or 'device' in self.changed_data) and AccessList.objects.filter(name__iexact=name, device=device).exists(): + raise forms.ValidationError('An Access-List with this name on this device already exists.') + return cleaned_data class AccessListFilterForm(NetBoxModelFilterSetForm): model = AccessList @@ -57,6 +74,17 @@ class AccessListStandardRuleForm(NetBoxModelForm): 'access_list', 'index', 'remark', 'action', 'tags', 'source_prefix', ) + def clean(self): + cleaned_data = super().clean() + if cleaned_data.get('remark'): + if cleaned_data.get('action'): + raise forms.ValidationError('Cannot input a remark AND an action. Remove one.') + if cleaned_data.get('source_prefix'): + raise forms.ValidationError('Cannot input a remark AND a source prefix. Remove one.') + #if cleaned_data.get('access_list_type') == 'standard' and (source_ports or destination_prefix or destination_ports): + # raise forms.ValidationError('Standard Access-Lists only allow a source_prefix or remark') + return cleaned_data + class AccessListStandardRuleFilterForm(NetBoxModelFilterSetForm): model = AccessListStandardRule @@ -104,6 +132,25 @@ class AccessListExtendedRuleForm(NetBoxModelForm): 'source_ports', 'destination_prefix', 'destination_ports', 'protocol' ) + def clean(self): + cleaned_data = super().clean() + if cleaned_data.get('remark'): + if cleaned_data.get('action'): + raise forms.ValidationError('Cannot input a remark AND an action. Remove one.') + if cleaned_data.get('source_prefix'): + raise forms.ValidationError('Cannot input a remark AND a source prefix. Remove one.') + if cleaned_data.get('source_ports'): + raise forms.ValidationError('Cannot input a remark AND source ports. Remove one.') + if cleaned_data.get('destination_prefix'): + raise forms.ValidationError('Cannot input a remark AND a destination prefix. Remove one.') + if cleaned_data.get('destination_ports'): + raise forms.ValidationError('Cannot input a remark AND destination ports. Remove one.') + if cleaned_data.get('protocol'): + raise forms.ValidationError('Cannot input a remark AND a protocol. Remove one.') + #if cleaned_data.get('access_list_type') == 'standard' and (source_ports or destination_prefix or destination_ports): + # raise forms.ValidationError('Standard Access-Lists only allow a source_prefix or remark') + return cleaned_data + class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): model = AccessListExtendedRule diff --git a/netbox_access_lists/migrations/0001_accesslist.py b/netbox_access_lists/migrations/0001_accesslist.py index 561d27e..a2e84c9 100644 --- a/netbox_access_lists/migrations/0001_accesslist.py +++ b/netbox_access_lists/migrations/0001_accesslist.py @@ -45,7 +45,7 @@ class Migration(migrations.Migration): ('index', models.PositiveIntegerField()), ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='standard_acl_rules', to='netbox_access_lists.accesslist')), ('remark', models.CharField(blank=True, null=True, max_length=500)), - ('action', models.CharField(max_length=30)), + ('action', models.CharField(blank=True, null=True, max_length=30)), ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), ], options={ @@ -64,7 +64,7 @@ class Migration(migrations.Migration): ('index', models.PositiveIntegerField()), ('access_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='extended_acl_rules', to='netbox_access_lists.accesslist')), ('remark', models.CharField(blank=True, null=True, max_length=500)), - ('action', models.CharField(max_length=30)), + ('action', models.CharField(blank=True, null=True, max_length=30)), ('source_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), ('source_ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(), blank=True, null=True, size=None)), ('destination_prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.prefix')), diff --git a/netbox_access_lists/models.py b/netbox_access_lists/models.py index 4ec2647..92fe77f 100644 --- a/netbox_access_lists/models.py +++ b/netbox_access_lists/models.py @@ -95,7 +95,6 @@ class AccessListStandardRule(NetBoxModel): blank=True, null=True, choices=AccessListActionChoices, - default=AccessListActionChoices.ACTION_PERMIT, max_length=30, ) source_prefix = models.ForeignKey( @@ -138,7 +137,6 @@ class AccessListExtendedRule(NetBoxModel): blank=True, null=True, choices=AccessListActionChoices, - default=AccessListActionChoices.ACTION_PERMIT, max_length=30, ) source_prefix = models.ForeignKey( From 2b821c1e3f87a0db76ec3b86e065b165f5ab76c0 Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sun, 17 Jul 2022 05:55:31 +0000 Subject: [PATCH 10/38] update existing ACL error --- netbox_access_lists/forms.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index 8d0a552..cb18e37 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -19,13 +19,6 @@ class AccessListForm(NetBoxModelForm): model = AccessList fields = ('name', 'device', 'type', 'default_action', 'comments', 'tags') - #def validate_unique(self, value): - # accesslists = AccessList.objects.exclude(pk=self.pk) - # if accesslists.filter(name=self.name, device=self.device).exists(): - # raise ValidationError({ - # "name": f"An Access List with this name on device {self.device} already exists." - # }) - def clean(self): cleaned_data = super().clean() if self.errors.get('name'): @@ -33,7 +26,7 @@ class AccessListForm(NetBoxModelForm): name = cleaned_data.get('name') device = cleaned_data.get('device') if ('name' in self.changed_data or 'device' in self.changed_data) and AccessList.objects.filter(name__iexact=name, device=device).exists(): - raise forms.ValidationError('An Access-List with this name on this device already exists.') + raise forms.ValidationError('An Access-List with this name is already associated to this device.') return cleaned_data class AccessListFilterForm(NetBoxModelFilterSetForm): From 47c64ebd42ed13770381dae57dec69935511681c Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Sun, 17 Jul 2022 17:12:59 +0000 Subject: [PATCH 11/38] form fieldsets --- netbox_access_lists/forms.py | 100 ++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/netbox_access_lists/forms.py b/netbox_access_lists/forms.py index cb18e37..1c969f7 100644 --- a/netbox_access_lists/forms.py +++ b/netbox_access_lists/forms.py @@ -1,5 +1,6 @@ from django import forms from django.core.exceptions import ValidationError +from django.utils.safestring import mark_safe from extras.models import Tag from ipam.models import Prefix @@ -8,6 +9,8 @@ from utilities.forms import CommentField, DynamicModelChoiceField, DynamicModelM from .models import AccessList, AccessListExtendedRule, AccessListActionChoices, AccessListProtocolChoices, AccessListTypeChoices, AccessListStandardRule +acl_rule_logic_help = mark_safe('*Note: CANNOT be set if remark is set.') + class AccessListForm(NetBoxModelForm): comments = CommentField() tags = DynamicModelMultipleChoiceField( @@ -15,9 +18,18 @@ class AccessListForm(NetBoxModelForm): required=False ) + fieldsets = [ + ('Access-List Details', ('name', 'device', 'type', 'default_action', 'tags')), + ] + class Meta: model = AccessList fields = ('name', 'device', 'type', 'default_action', 'comments', 'tags') + help_texts = { + 'default_action': 'The default behavior of the ACL.', + 'name': 'The name uniqueness per device is case insensitive.', + 'type': 'Sets the type of the ACL & its rules.', + } def clean(self): cleaned_data = super().clean() @@ -26,7 +38,7 @@ class AccessListForm(NetBoxModelForm): name = cleaned_data.get('name') device = cleaned_data.get('device') if ('name' in self.changed_data or 'device' in self.changed_data) and AccessList.objects.filter(name__iexact=name, device=device).exists(): - raise forms.ValidationError('An Access-List with this name is already associated to this device.') + raise forms.ValidationError('An ACL with this name (case insensitive) is already associated to this device.') return cleaned_data class AccessListFilterForm(NetBoxModelFilterSetForm): @@ -34,7 +46,7 @@ class AccessListFilterForm(NetBoxModelFilterSetForm): type = forms.MultipleChoiceField( choices=AccessListTypeChoices, required=False, - widget=StaticSelectMultiple() + widget=StaticSelectMultiple(), ) default_action = forms.MultipleChoiceField( choices=AccessListActionChoices, @@ -43,29 +55,46 @@ class AccessListFilterForm(NetBoxModelFilterSetForm): ) tag = TagFilterField(model) + fieldsets = ( + (None, ('q', 'tag')), + ('ACL Details', ('type', 'default_action')), + ) + class AccessListStandardRuleForm(NetBoxModelForm): access_list = DynamicModelChoiceField( queryset=AccessList.objects.all(), query_params={ 'type': 'standard' - } + }, + help_text=mark_safe('*Note: This field will only display Standard ACLs.'), + ) + source_prefix = DynamicModelChoiceField( + queryset=Prefix.objects.all(), + required=False, + help_text=acl_rule_logic_help, ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), required=False ) - source_prefix = DynamicModelChoiceField( - queryset=Prefix.objects.all(), - required=False - ) + fieldsets = ( + ('Access-List Details', ('access_list', 'index', 'tags')), + ('Rule Logic', ('remark', 'action', 'source_prefix')), + ) class Meta: model = AccessListStandardRule fields = ( - 'access_list', 'index', 'remark', 'action', 'tags', 'source_prefix', + 'access_list', 'index', 'remark', 'action', 'source_prefix', + 'tags', ) + help_texts = { + 'action': acl_rule_logic_help, + 'index': 'Determines the order of the rule in the ACL processing.', + 'remark': mark_safe('*Note: CANNOT be set if source prefix OR action is set.'), + } def clean(self): cleaned_data = super().clean() @@ -86,23 +115,29 @@ class AccessListStandardRuleFilterForm(NetBoxModelFilterSetForm): required=False, widget=StaticSelectMultiple() ) - index = forms.IntegerField( - required=False - ) tag = TagFilterField(model) + source_prefix = forms.ModelMultipleChoiceField( + queryset=Prefix.objects.all(), + required=False, + widget=StaticSelectMultiple() + ) action = forms.MultipleChoiceField( choices=AccessListActionChoices, required=False, widget=StaticSelectMultiple() ) - + fieldsets = ( + (None, ('q', 'tag')), + ('Rule Details', ('access_list', 'action', 'source_prefix',)), + ) class AccessListExtendedRuleForm(NetBoxModelForm): access_list = DynamicModelChoiceField( queryset=AccessList.objects.all(), query_params={ 'type': 'extended' - } + }, + help_text=mark_safe('*Note: This field will only display Extended ACLs.'), ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), @@ -110,20 +145,34 @@ class AccessListExtendedRuleForm(NetBoxModelForm): ) source_prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), - required=False + required=False, + help_text=acl_rule_logic_help, ) destination_prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), - required=False + required=False, + help_text=acl_rule_logic_help, + ) + fieldsets = ( + ('Access-List Details', ('access_list', 'index', 'tags')), + ('Rule Details', ('remark', 'action', 'source_prefix', 'source_ports', 'destination_prefix', 'destination_ports', 'protocol',)), ) - class Meta: model = AccessListExtendedRule fields = ( - 'access_list', 'index', 'remark', 'action', 'tags', 'source_prefix', - 'source_ports', 'destination_prefix', 'destination_ports', 'protocol' + 'access_list', 'index', 'remark', 'action', 'source_prefix', + 'source_ports', 'destination_prefix', 'destination_ports', 'protocol', + 'tags' ) + help_texts = { + 'action': acl_rule_logic_help, + 'destination_ports': acl_rule_logic_help, + 'index': 'Determines the order of the rule in the ACL processing.', + 'protocol': acl_rule_logic_help, + 'remark': mark_safe('*Note: CANNOT be set if a prefix, port, OR action is set.'), + 'source_ports': acl_rule_logic_help, + } def clean(self): cleaned_data = super().clean() @@ -161,8 +210,23 @@ class AccessListExtendedRuleFilterForm(NetBoxModelFilterSetForm): required=False, widget=StaticSelectMultiple() ) + source_prefix = forms.ModelMultipleChoiceField( + queryset=Prefix.objects.all(), + required=False, + widget=StaticSelectMultiple() + ) + desintation_prefix = forms.ModelMultipleChoiceField( + queryset=Prefix.objects.all(), + required=False, + widget=StaticSelectMultiple() + ) protocol = forms.MultipleChoiceField( choices=AccessListProtocolChoices, required=False, widget=StaticSelectMultiple() ) + + fieldsets = ( + (None, ('q', 'tag')), + ('Rule Details', ('access_list', 'action', 'source_prefix', 'desintation_prefix', 'protocol')), + ) From 2828a355f26a30cfe60f74688bdfef04a24528db Mon Sep 17 00:00:00 2001 From: ryanmerolle Date: Mon, 18 Jul 2022 11:49:10 +0000 Subject: [PATCH 12/38] organize template directories --- netbox_access_lists/template_content.py | 2 +- .../access_lists.html} | 2 +- .../{ => device}/assigned_access_lists.html | 0 ...textendedrule.html => aclextendedrule.html} | 0 ...tstandardrule.html => aclstandardrule.html} | 18 ------------------ 5 files changed, 2 insertions(+), 20 deletions(-) rename netbox_access_lists/templates/inc/{device_access_lists.html => device/access_lists.html} (87%) rename netbox_access_lists/templates/inc/{ => device}/assigned_access_lists.html (100%) rename netbox_access_lists/templates/netbox_access_lists/{accesslistextendedrule.html => aclextendedrule.html} (100%) rename netbox_access_lists/templates/netbox_access_lists/{accessliststandardrule.html => aclstandardrule.html} (72%) diff --git a/netbox_access_lists/template_content.py b/netbox_access_lists/template_content.py index 3fb339c..1431bd4 100644 --- a/netbox_access_lists/template_content.py +++ b/netbox_access_lists/template_content.py @@ -17,7 +17,7 @@ class AccessLists(PluginTemplateExtension): #elif ctype.model == 'virtualmachine': # access_lists = AccessList.objects.filter(device=obj.pk) - return self.render('inc/device_access_lists.html', extra_context={ + return self.render('inc/device/access_lists.html', extra_context={ 'access_lists': access_lists, 'type': ctype.model if ctype.model == 'device' else ctype.name.replace(' ', '_'), }) diff --git a/netbox_access_lists/templates/inc/device_access_lists.html b/netbox_access_lists/templates/inc/device/access_lists.html similarity index 87% rename from netbox_access_lists/templates/inc/device_access_lists.html rename to netbox_access_lists/templates/inc/device/access_lists.html index 074d028..7bdb9b9 100644 --- a/netbox_access_lists/templates/inc/device_access_lists.html +++ b/netbox_access_lists/templates/inc/device/access_lists.html @@ -3,7 +3,7 @@ Access Lists
- {% include 'inc/assigned_access_lists.html' %} + {% include 'inc/device/assigned_access_lists.html' %}