From e97b5af7a65f9a7c37b5fe3d93fb311eea5cf047 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:22:48 +0200 Subject: [PATCH 01/41] feat: Adding new Source-Fields to ACLStandard Rule --- netbox_acls/models/access_list_rules.py | 65 +++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index 56567204..c7f3e6d3 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -7,6 +7,7 @@ from django.db import models from django.urls import reverse from netbox.models import NetBoxModel +from ipam.models import Prefix, IPRange, IPAddress, Aggregate, Service from ..choices import ACLProtocolChoices, ACLRuleActionChoices, ACLTypeChoices from .access_lists import AccessList @@ -17,7 +18,6 @@ "ACLExtendedRule", ) - class ACLRule(NetBoxModel): """ Abstract model for ACL Rules. @@ -43,16 +43,57 @@ class ACLRule(NetBoxModel): choices=ACLRuleActionChoices, max_length=30, ) + source_prefix = models.ForeignKey( blank=True, null=True, on_delete=models.PROTECT, related_name="+", - to="ipam.Prefix", + to=Prefix, verbose_name="Source Prefix", ) + source_iprange = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=IPRange, + verbose_name="Source IP-Range", + ) + source_ipaddress = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=IPAddress, + verbose_name="Source IP-Address", + ) + source_aggregate = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=Aggregate, + verbose_name="Source Aggregate", + ) + source_service = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=Service, + verbose_name="Source Service", + ) - clone_fields = ("access_list", "action", "source_prefix") + clone_fields = ( + "access_list", + "action", + "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" + ) def __str__(self): return f"{self.access_list}: Rule {self.index}" @@ -62,7 +103,14 @@ def get_action_color(self): @classmethod def get_prerequisite_models(cls): - return [apps.get_model("ipam.Prefix"), AccessList] + return [ + Prefix, + IPRange, + IPAddress, + Aggregate, + Service, + AccessList + ] class Meta: """ @@ -99,7 +147,14 @@ def get_absolute_url(self): @classmethod def get_prerequisite_models(cls): - return [AccessList] + return [ + Prefix, + IPRange, + IPAddress, + Aggregate, + Service, + AccessList + ] class Meta(ACLRule.Meta): """ From 426ad54e3be7eefa2e21f8803932436ab54cbfd5 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:25:26 +0200 Subject: [PATCH 02/41] feat: Adding new Destination-Fields to ACLExtended Rule --- netbox_acls/models/access_list_rules.py | 43 +++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index c7f3e6d3..cb97e784 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -192,9 +192,41 @@ class ACLExtendedRule(ACLRule): null=True, on_delete=models.PROTECT, related_name="+", - to="ipam.Prefix", + to=Prefix, verbose_name="Destination Prefix", ) + destination_iprange = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=IPRange, + verbose_name="Destination IP-Range", + ) + destination_ipaddress = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=IPAddress, + verbose_name="Destination IP-Address", + ) + destination_aggregate = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=Aggregate, + verbose_name="Destination Aggregate", + ) + destination_service = models.ForeignKey( + blank=True, + null=True, + on_delete=models.PROTECT, + related_name="+", + to=Service, + verbose_name="Destination Service", + ) destination_ports = ArrayField( base_field=models.PositiveIntegerField(), blank=True, @@ -219,7 +251,14 @@ def get_protocol_color(self): @classmethod def get_prerequisite_models(cls): - return [apps.get_model("ipam.Prefix"), AccessList] + return [ + Prefix, + IPRange, + IPAddress, + Aggregate, + Service, + AccessList + ] class Meta(ACLRule.Meta): """ From 04351fbaf2f9c273c73a10813b1276ffc6fdfe24 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:27:54 +0200 Subject: [PATCH 03/41] feat: Adding Database Constraint for ACLStandard Rule, so that at max only one Source Field can be specified --- netbox_acls/models/access_list_rules.py | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index cb97e784..317112ec 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -5,6 +5,7 @@ from django.apps import apps from django.contrib.postgres.fields import ArrayField from django.db import models +from django.db.models import Q from django.urls import reverse from netbox.models import NetBoxModel from ipam.models import Prefix, IPRange, IPAddress, Aggregate, Service @@ -167,6 +168,32 @@ class Meta(ACLRule.Meta): verbose_name = "ACL Standard Rule" verbose_name_plural = "ACL Standard Rules" + constraints = [ + models.CheckConstraint( + check=( + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=False) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=False) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=False) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=False) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=False) + ) + ), + name='not_more_than_one_source_for_standard_rule' + ) + ] + class ACLExtendedRule(ACLRule): """ From a84cd18bd819de64dacd0b90dcbd1c12791aeec1 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:28:38 +0200 Subject: [PATCH 04/41] feat: Adding Database Constraint for ACLExtended Rule, so that at max only one Source Field can be specified --- netbox_acls/models/access_list_rules.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index 317112ec..d1bd6ed5 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -297,3 +297,28 @@ class Meta(ACLRule.Meta): verbose_name = "ACL Extended Rule" verbose_name_plural = "ACL Extended Rules" + + constraints = [ + models.CheckConstraint( + check=( + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=False) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=False) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=False) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=False) & Q(source_service__isnull=True) + ) | + ( + Q(source_prefix__isnull=True) & Q(source_iprange__isnull=True) & Q(source_ipaddress__isnull=True) & Q(source_aggregate__isnull=True) & Q(source_service__isnull=False) + ) + ), + name='not_more_than_one_source_for_extended_rule' + ), From 0bc9cf23721debd3afbf252bc43e9506c22acb51 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:29:17 +0200 Subject: [PATCH 05/41] feat: Adding Database Constraint for ACLExtended Rule, so that at max only one Destination Field can be specified --- netbox_acls/models/access_list_rules.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index d1bd6ed5..9349aaef 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -322,3 +322,27 @@ class Meta(ACLRule.Meta): ), name='not_more_than_one_source_for_extended_rule' ), + models.CheckConstraint( + check=( + ( + Q(destination_prefix__isnull=True) & Q(destination_iprange__isnull=True) & Q(destination_ipaddress__isnull=True) & Q(destination_aggregate__isnull=True) & Q(destination_service__isnull=True) + ) | + ( + Q(destination_prefix__isnull=False) & Q(destination_iprange__isnull=True) & Q(destination_ipaddress__isnull=True) & Q(destination_aggregate__isnull=True) & Q(destination_service__isnull=True) + ) | + ( + Q(destination_prefix__isnull=True) & Q(destination_iprange__isnull=False) & Q(destination_ipaddress__isnull=True) & Q(destination_aggregate__isnull=True) & Q(destination_service__isnull=True) + ) | + ( + Q(destination_prefix__isnull=True) & Q(destination_iprange__isnull=True) & Q(destination_ipaddress__isnull=False) & Q(destination_aggregate__isnull=True) & Q(destination_service__isnull=True) + ) | + ( + Q(destination_prefix__isnull=True) & Q(destination_iprange__isnull=True) & Q(destination_ipaddress__isnull=True) & Q(destination_aggregate__isnull=False) & Q(destination_service__isnull=True) + ) | + ( + Q(destination_prefix__isnull=True) & Q(destination_iprange__isnull=True) & Q(destination_ipaddress__isnull=True) & Q(destination_aggregate__isnull=True) & Q(destination_service__isnull=False) + ) + ), + name='not_more_than_one_destination_for_extended_rule' + ) + ] \ No newline at end of file From 675a72d78bb13ebbe3e4ff2609c505037bcc1ad6 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:30:32 +0200 Subject: [PATCH 06/41] refactor: Re-order; Adapt Comments; Use Constants --- netbox_acls/models/access_list_rules.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/netbox_acls/models/access_list_rules.py b/netbox_acls/models/access_list_rules.py index 9349aaef..47c335ef 100644 --- a/netbox_acls/models/access_list_rules.py +++ b/netbox_acls/models/access_list_rules.py @@ -198,22 +198,17 @@ class Meta(ACLRule.Meta): class ACLExtendedRule(ACLRule): """ Inherits ACLRule. - Add ACLExtendedRule specific fields: source_ports, desintation_prefix, destination_ports, and protocol + Add ACLExtendedRule specific fields: destination, source_ports, destination_ports and protocol """ access_list = models.ForeignKey( on_delete=models.CASCADE, to=AccessList, verbose_name="Extended Access List", - limit_choices_to={"type": "extended"}, + limit_choices_to={"type": ACLTypeChoices.TYPE_EXTENDED}, related_name="aclextendedrules", ) - source_ports = ArrayField( - base_field=models.PositiveIntegerField(), - blank=True, - null=True, - verbose_name="Soure Ports", - ) + destination_prefix = models.ForeignKey( blank=True, null=True, @@ -254,6 +249,13 @@ class ACLExtendedRule(ACLRule): to=Service, verbose_name="Destination Service", ) + + source_ports = ArrayField( + base_field=models.PositiveIntegerField(), + blank=True, + null=True, + verbose_name="Source Ports", + ) destination_ports = ArrayField( base_field=models.PositiveIntegerField(), blank=True, From d513e54c7ef205bbe8a30d2526b27f36728e4cba Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:33:01 +0200 Subject: [PATCH 07/41] feat(view): Adding new Source fields to Standard Rule View Set --- netbox_acls/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_acls/api/views.py b/netbox_acls/api/views.py index cdff48d1..6d97e342 100644 --- a/netbox_acls/api/views.py +++ b/netbox_acls/api/views.py @@ -60,7 +60,7 @@ class ACLStandardRuleViewSet(NetBoxModelViewSet): queryset = models.ACLStandardRule.objects.prefetch_related( "access_list", "tags", - "source_prefix", + "source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service" ) serializer_class = ACLStandardRuleSerializer filterset_class = filtersets.ACLStandardRuleFilterSet From 19f325e373a2819ef7aae5e694a8ea1b9e2a0ca2 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:33:30 +0200 Subject: [PATCH 08/41] feat(view): Adding new Source fields to Extended Rule View Set --- netbox_acls/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_acls/api/views.py b/netbox_acls/api/views.py index 6d97e342..3f31001b 100644 --- a/netbox_acls/api/views.py +++ b/netbox_acls/api/views.py @@ -74,7 +74,7 @@ class ACLExtendedRuleViewSet(NetBoxModelViewSet): queryset = models.ACLExtendedRule.objects.prefetch_related( "access_list", "tags", - "source_prefix", + "source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service", "destination_prefix", ) serializer_class = ACLExtendedRuleSerializer From baf9961e29dd150cf3634ce9c31a28f9b6ab5b38 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:34:04 +0200 Subject: [PATCH 09/41] feat(view): Adding new Destination fields to Extended Rule View Set --- netbox_acls/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_acls/api/views.py b/netbox_acls/api/views.py index 3f31001b..a6587786 100644 --- a/netbox_acls/api/views.py +++ b/netbox_acls/api/views.py @@ -75,7 +75,7 @@ class ACLExtendedRuleViewSet(NetBoxModelViewSet): "access_list", "tags", "source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service", - "destination_prefix", + "destination_prefix", "destination_iprange", "destination_ipaddress", "destination_aggregate", "destination_service", ) serializer_class = ACLExtendedRuleSerializer filterset_class = filtersets.ACLExtendedRuleFilterSet From ba1d4cf395068a139562d5580c95ae276d53ae9d Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:35:21 +0200 Subject: [PATCH 10/41] feat(view): Adding new Source Fields to ACL Standard Rule Template --- .../templates/netbox_acls/aclstandardrule.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/netbox_acls/templates/netbox_acls/aclstandardrule.html b/netbox_acls/templates/netbox_acls/aclstandardrule.html index 4211ba1e..87206624 100644 --- a/netbox_acls/templates/netbox_acls/aclstandardrule.html +++ b/netbox_acls/templates/netbox_acls/aclstandardrule.html @@ -38,10 +38,23 @@

Details

{{ object.get_protocol_display|placeholder }} - Source Prefix + Source {% if object.source_prefix %} + Prefix {{ object.source_prefix }} + {% elif object.source_iprange %} + IP-Range + {{ object.source_iprange }} + {% elif object.source_ipaddress %} + IP-Address + {{ object.source_ipaddress }} + {% elif object.source_aggregate %} + Aggregate + {{ object.source_aggregate }} + {% elif object.source_service %} + Service + {{ object.source_service }} {% else %} {{ ''|placeholder }} {% endif %} From 5bea6ad8cc5e3675b2ce9a63325e502a6cfa94cf Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:36:00 +0200 Subject: [PATCH 11/41] feat(view): Adding new Source fields to ACL Extended Rule Template --- .../templates/netbox_acls/aclextendedrule.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/netbox_acls/templates/netbox_acls/aclextendedrule.html b/netbox_acls/templates/netbox_acls/aclextendedrule.html index 29e14419..6aebf731 100644 --- a/netbox_acls/templates/netbox_acls/aclextendedrule.html +++ b/netbox_acls/templates/netbox_acls/aclextendedrule.html @@ -38,10 +38,23 @@

Details

{{ object.get_protocol_display|placeholder }} - Source Prefix + Source {% if object.source_prefix %} + Prefix {{ object.source_prefix }} + {% elif object.source_iprange %} + IP-Range + {{ object.source_iprange }} + {% elif object.source_ipaddress %} + IP-Address + {{ object.source_ipaddress }} + {% elif object.source_aggregate %} + Aggregate + {{ object.source_aggregate }} + {% elif object.source_service %} + Service + {{ object.source_service }} {% else %} {{ ''|placeholder }} {% endif %} From fa55c0e01c71cbc97d7c7dc55fac4195bcc93b3e Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:36:25 +0200 Subject: [PATCH 12/41] feat(view): Adding new Destination fields to ACL Extended Rule Template --- .../templates/netbox_acls/aclextendedrule.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/netbox_acls/templates/netbox_acls/aclextendedrule.html b/netbox_acls/templates/netbox_acls/aclextendedrule.html index 6aebf731..fc690a5c 100644 --- a/netbox_acls/templates/netbox_acls/aclextendedrule.html +++ b/netbox_acls/templates/netbox_acls/aclextendedrule.html @@ -65,10 +65,23 @@

Details

{{ object.source_ports|join:", "|placeholder }} - Destination Prefix + Destination {% if object.destination_prefix %} + Prefix {{ object.destination_prefix }} + {% elif object.destination_iprange %} + IP-Range + {{ object.destination_iprange }} + {% elif object.destination_ipaddress %} + IP-Address + {{ object.destination_ipaddress }} + {% elif object.destination_aggregate %} + Aggregate + {{ object.destination_aggregate }} + {% elif object.destination_service %} + Service + {{ object.destination_service }} {% else %} {{ ''|placeholder }} {% endif %} From d38a9c65303c06a045d8d62da8e28d2a3708b9ad Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:38:13 +0200 Subject: [PATCH 13/41] feat(view): Adding new Source Fields to the Views of ACLStandardRule --- netbox_acls/views.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/netbox_acls/views.py b/netbox_acls/views.py index 9f1fa81d..b70ca81d 100644 --- a/netbox_acls/views.py +++ b/netbox_acls/views.py @@ -323,6 +323,10 @@ class ACLStandardRuleView(generic.ObjectView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" ) @@ -335,6 +339,10 @@ class ACLStandardRuleListView(generic.ObjectListView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" ) table = tables.ACLStandardRuleTable filterset = filtersets.ACLStandardRuleFilterSet @@ -346,11 +354,14 @@ class ACLStandardRuleEditView(generic.ObjectEditView): """ Defines the edit view for the ACLStandardRule django model. """ - queryset = models.ACLStandardRule.objects.prefetch_related( "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" ) form = forms.ACLStandardRuleForm @@ -374,6 +385,10 @@ class ACLStandardRuleDeleteView(generic.ObjectDeleteView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" ) @@ -382,6 +397,10 @@ class ACLStandardRuleBulkDeleteView(generic.BulkDeleteView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service" ) filterset = filtersets.ACLStandardRuleFilterSet table = tables.ACLStandardRuleTable From 2b4a2373b381a589806a8591e2454a443df5c207 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:38:54 +0200 Subject: [PATCH 14/41] feat(view): Adding new Source Fields to the Views of ACLExtendedRule --- netbox_acls/views.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/netbox_acls/views.py b/netbox_acls/views.py index b70ca81d..bf7e0a66 100644 --- a/netbox_acls/views.py +++ b/netbox_acls/views.py @@ -421,6 +421,10 @@ class ACLExtendedRuleView(generic.ObjectView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", "destination_prefix", ) @@ -434,6 +438,10 @@ class ACLExtendedRuleListView(generic.ObjectListView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", "destination_prefix", ) table = tables.ACLExtendedRuleTable @@ -451,6 +459,10 @@ class ACLExtendedRuleEditView(generic.ObjectEditView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", "destination_prefix", ) form = forms.ACLExtendedRuleForm @@ -475,6 +487,10 @@ class ACLExtendedRuleDeleteView(generic.ObjectDeleteView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", "destination_prefix", ) @@ -484,6 +500,10 @@ class ACLExtendedRuleBulkDeleteView(generic.BulkDeleteView): "access_list", "tags", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", "destination_prefix", ) filterset = filtersets.ACLExtendedRuleFilterSet From 11912b5ddeacb938c2e9b5d59f8b9e2495000a74 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:39:43 +0200 Subject: [PATCH 15/41] feat(view): Adding new Destination Fields to the Views of ACLExtendedRule --- netbox_acls/views.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/netbox_acls/views.py b/netbox_acls/views.py index bf7e0a66..6c4076dd 100644 --- a/netbox_acls/views.py +++ b/netbox_acls/views.py @@ -374,7 +374,6 @@ def get_extra_addanother_params(self, request): "access_list": request.GET.get("access_list") or request.POST.get("access_list"), } - @register_model_view(models.ACLStandardRule, "delete") class ACLStandardRuleDeleteView(generic.ObjectDeleteView): """ @@ -426,6 +425,10 @@ class ACLExtendedRuleView(generic.ObjectView): "source_aggregate", "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" ) @@ -443,6 +446,10 @@ class ACLExtendedRuleListView(generic.ObjectListView): "source_aggregate", "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" ) table = tables.ACLExtendedRuleTable filterset = filtersets.ACLExtendedRuleFilterSet @@ -464,6 +471,10 @@ class ACLExtendedRuleEditView(generic.ObjectEditView): "source_aggregate", "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" ) form = forms.ACLExtendedRuleForm @@ -492,6 +503,10 @@ class ACLExtendedRuleDeleteView(generic.ObjectDeleteView): "source_aggregate", "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" ) @@ -505,6 +520,10 @@ class ACLExtendedRuleBulkDeleteView(generic.BulkDeleteView): "source_aggregate", "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" ) filterset = filtersets.ACLExtendedRuleFilterSet table = tables.ACLExtendedRuleTable From 7adb987cbe2ae9c1a692baedb16c96715c4769be Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:51:56 +0200 Subject: [PATCH 16/41] feat(form): Adding new Sources to the ACLStandardRule creation form --- netbox_acls/forms/models.py | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index d25bab2b..790aaaa0 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -6,7 +6,13 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe -from ipam.models import Prefix +from ipam.models import ( + Prefix, + IPRange, + IPAddress, + Aggregate, + Service, +) from netbox.forms import NetBoxModelForm from utilities.forms.rendering import FieldSet from utilities.forms.fields import CommentField, DynamicModelChoiceField @@ -445,12 +451,37 @@ class ACLStandardRuleForm(NetBoxModelForm): ), label="Access List", ) + source_prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), required=False, help_text=help_text_acl_rule_logic, label="Source Prefix", ) + source_iprange = DynamicModelChoiceField( + queryset=IPRange.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source IP-Range", + ) + source_ipaddress = DynamicModelChoiceField( + queryset=IPAddress.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source IP-Address", + ) + source_aggregate = DynamicModelChoiceField( + queryset=Aggregate.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source Aggregate", + ) + source_service = DynamicModelChoiceField( + queryset=Service.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source Service", + ) fieldsets = ( FieldSet("access_list", "description", "tags", name=_('Access List Details')), @@ -463,7 +494,13 @@ class Meta: "index", "action", "remark", + "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", + "tags", "description", ) From 87515f41ca9eadaeab08c031f659d7b6903408ad Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:52:39 +0200 Subject: [PATCH 17/41] feat(form): ACLStandardRule: Instruct new Sources to be displayed as Tabbed Group; Improve Fieldsets --- netbox_acls/forms/models.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 790aaaa0..0c218d07 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ from ipam.models import ( Prefix, IPRange, @@ -23,7 +24,7 @@ VirtualMachine, VMInterface, ) - +from utilities.forms.rendering import FieldSet, TabbedGroups from ..choices import ACLTypeChoices from ..models import ( AccessList, @@ -484,9 +485,31 @@ class ACLStandardRuleForm(NetBoxModelForm): ) fieldsets = ( - FieldSet("access_list", "description", "tags", name=_('Access List Details')), - FieldSet("index", "action", "remark", "source_prefix", name=_('Rule Definition')) + FieldSet( + "access_list", + "description", + "tags", + name=_('Access List Details') + ), + FieldSet( + "index", + "action", + "remark", + name=_('Rule Definition') + ), + FieldSet( + TabbedGroups( + FieldSet('source_prefix', name=_('Prefix')), + FieldSet('source_iprange', name=_('IP Range')), + FieldSet('source_ipaddress', name=_('IP Address')), + FieldSet('source_aggregate', name=_('Aggregate')), + FieldSet('source_service', name=_('Service')), + ) + ) ) + + + class Meta: model = ACLStandardRule fields = ( From d290ae167b7d165d122e581fcb572099eca585d0 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:54:06 +0200 Subject: [PATCH 18/41] feat(form): ACLStandardRule: Change validation to support multiple sources; Add validation to restrict to a single Source --- netbox_acls/forms/models.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 0c218d07..bf56f78a 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -51,11 +51,14 @@ # Sets a standard error message for ACL rules with an action of remark, but no remark set. error_message_no_remark = "Action is set to remark, you MUST add a remark." -# Sets a standard error message for ACL rules with an action of remark, but no source_prefix is set. -error_message_action_remark_source_prefix_set = "Action is set to remark, Source Prefix CANNOT be set." # Sets a standard error message for ACL rules with an action not set to remark, but no remark is set. error_message_remark_without_action_remark = "CANNOT set remark unless action is set to remark." +# Sets a standard error message for ACL rules with an action of remark, but no source is set. +error_message_action_remark_source_set = "Action is set to remark, Source CANNOT be set." + +# Sets a standard error message for ACL rules when more than one IP/Host sources are set. +error_message_sources_more_than_one = "Only one IP/Host related Source can be specified." class AccessListForm(NetBoxModelForm): """ @@ -535,7 +538,7 @@ class Meta: "index": help_text_acl_rule_index, "action": help_text_acl_action, "remark": mark_safe( - "*Note: CANNOT be set if source prefix OR action is set.", + "*Note: CANNOT be set if source OR action is set.", ), } @@ -543,8 +546,9 @@ def clean(self): """ Validates form inputs before submitting: - Check if action set to remark, but no remark set. - - Check if action set to remark, but source_prefix set. + - Check if action set to remark, but source set. - Check remark set, but action not set to remark. + - Check not more than one source is set. """ super().clean() cleaned_data = self.cleaned_data @@ -552,18 +556,23 @@ def clean(self): action = cleaned_data.get("action") remark = cleaned_data.get("remark") - source_prefix = cleaned_data.get("source_prefix") + sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] if action == "remark": # Check if action set to remark, but no remark set. if not remark: error_message["remark"] = [error_message_no_remark] - # Check if action set to remark, but source_prefix set. - if source_prefix: - error_message["source_prefix"] = [error_message_action_remark_source_prefix_set] + # Check if action set to remark, but source set. + if any(cleaned_data.get(source) for source in sources): + for source in sources: + error_message[source] = [error_message_action_remark_source_set] # Check remark set, but action not set to remark. elif remark: error_message["remark"] = [error_message_remark_without_action_remark] + # Check not more than one source is set. + elif sum(bool(cleaned_data.get(source)) for source in sources) > 1: + for source in sources: + error_message[source] = [error_message_sources_more_than_one] if error_message: raise ValidationError(error_message) From 8d35acb098e8a5679d41857289d16a0faab57a45 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 17:55:51 +0200 Subject: [PATCH 19/41] feat(form): Adding new Sources to the ACLExtendedRule creation form --- netbox_acls/forms/models.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index bf56f78a..feeeaa17 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -602,6 +602,31 @@ class ACLExtendedRuleForm(NetBoxModelForm): help_text=help_text_acl_rule_logic, label="Source Prefix", ) + source_iprange = DynamicModelChoiceField( + queryset=IPRange.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source IP-Range", + ) + source_ipaddress = DynamicModelChoiceField( + queryset=IPAddress.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source IP-Address", + ) + source_aggregate = DynamicModelChoiceField( + queryset=Aggregate.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source Aggregate", + ) + source_service = DynamicModelChoiceField( + queryset=Service.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Source Service", + ) + destination_prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), required=False, @@ -619,9 +644,16 @@ class Meta: "index", "action", "remark", + "source_prefix", - "source_ports", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", + "destination_prefix", + + "source_ports", "destination_ports", "protocol", "tags", From 00bc0acabda4514edba0c5f91df46dddfc1efe33 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:02:01 +0200 Subject: [PATCH 20/41] feat(form): ACLExtendedRule: Instruct new Sources to be displayed as Tabbed Group; Improve Fieldsets --- netbox_acls/forms/models.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index feeeaa17..00f66a5e 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -634,8 +634,31 @@ class ACLExtendedRuleForm(NetBoxModelForm): label="Destination Prefix", ) fieldsets = ( - FieldSet("access_list", "description", "tags", name=_('Access List Details')), - FieldSet("index", "action", "remark", "source_prefix", "source_ports", "destination_prefix", "destination_ports", "protocol", name=_('Rule Definition')) + FieldSet( + "access_list", + "description", + "tags", + name=_('Access List Details') + ), + FieldSet( + "index", + "action", + "remark", + "protocol", + name=_('Rule Definition') + ), + FieldSet( + TabbedGroups( + FieldSet('source_prefix', name=_('Prefix')), + FieldSet('source_iprange', name=_('IP Range')), + FieldSet('source_ipaddress', name=_('IP Address')), + FieldSet('source_aggregate', name=_('Aggregate')), + FieldSet('source_service', name=_('Service')), + ), + "source_ports", + ), + ), + ), ) class Meta: model = ACLExtendedRule From 1179ccdba86140e008f6c9c988da061ec788eae9 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:04:46 +0200 Subject: [PATCH 21/41] feat(form): ACLExtendedRule: Change validation to support multiple sources; Add validation to restrict to a single Source --- netbox_acls/forms/models.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 00f66a5e..4eb9f2e0 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -684,26 +684,24 @@ class Meta: ) help_texts = { - "action": help_text_acl_action, - "destination_ports": help_text_acl_rule_logic, "index": help_text_acl_rule_index, - "protocol": help_text_acl_rule_logic, + "action": help_text_acl_action, "remark": mark_safe( "*Note: CANNOT be set if action is not set to remark.", ), "source_ports": help_text_acl_rule_logic, + "destination_ports": help_text_acl_rule_logic, + "protocol": help_text_acl_rule_logic, } def clean(self): """ Validates form inputs before submitting: - Check if action set to remark, but no remark set. - - Check if action set to remark, but source_prefix set. - - Check if action set to remark, but source_ports set. - - Check if action set to remark, but destination_prefix set. - - Check if action set to remark, but destination_ports set. - - Check if action set to remark, but protocol set. + - Check if action set to remark, but source set. + - Check if action set to remark, but protocol set - Check remark set, but action not set to remark. + - Check not more than one source is set. """ super().clean() cleaned_data = self.cleaned_data @@ -711,7 +709,8 @@ def clean(self): action = cleaned_data.get("action") remark = cleaned_data.get("remark") - source_prefix = cleaned_data.get("source_prefix") + + sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] source_ports = cleaned_data.get("source_ports") destination_prefix = cleaned_data.get("destination_prefix") destination_ports = cleaned_data.get("destination_ports") @@ -720,8 +719,11 @@ def clean(self): if action == "remark": if not remark: error_message["remark"] = [error_message_no_remark] - if source_prefix: - error_message["source_prefix"] = [error_message_action_remark_source_prefix_set] + + # Check if action set to remark, but source set. + for source in sources: + error_message[source] = [error_message_action_remark_source_set] + if source_ports: error_message["source_ports"] = ["Action is set to remark, Source Ports CANNOT be set."] if destination_prefix: @@ -733,5 +735,10 @@ def clean(self): elif remark: error_message["remark"] = [error_message_remark_without_action_remark] + # Check not more than one source is set. + elif sum(bool(cleaned_data.get(source)) for source in sources) > 1: + for source in sources: + error_message[source] = [error_message_sources_more_than_one] + if error_message: raise ValidationError(error_message) From 72ef3074c7ce673ff650afdf32bc88ab971576a3 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:05:46 +0200 Subject: [PATCH 22/41] feat(form): Adding new Destinations to the ACLExtendedRule creation form --- netbox_acls/forms/models.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 4eb9f2e0..9cb0b489 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -633,6 +633,31 @@ class ACLExtendedRuleForm(NetBoxModelForm): help_text=help_text_acl_rule_logic, label="Destination Prefix", ) + destination_iprange = DynamicModelChoiceField( + queryset=IPRange.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Destination IP-Range", + ) + destination_ipaddress = DynamicModelChoiceField( + queryset=IPAddress.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Destination IP-Address", + ) + destination_aggregate = DynamicModelChoiceField( + queryset=Aggregate.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Destination Aggregate", + ) + destination_service = DynamicModelChoiceField( + queryset=Service.objects.all(), + required=False, + help_text=help_text_acl_rule_logic, + label="Destination Service", + ) + fieldsets = ( FieldSet( "access_list", @@ -675,6 +700,10 @@ class Meta: "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service", "source_ports", "destination_ports", From ccfb3fab7068e91dad248f8cff9bab9a42ed4903 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:06:22 +0200 Subject: [PATCH 23/41] feat(form): ACLExtendedRule: Instruct new Destinations to be displayed as Tabbed Group --- netbox_acls/forms/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 9cb0b489..495c05ee 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -682,7 +682,15 @@ class ACLExtendedRuleForm(NetBoxModelForm): ), "source_ports", ), + FieldSet( + TabbedGroups( + FieldSet('destination_prefix', name=_('Prefix')), + FieldSet('destination_iprange', name=_('IP Range')), + FieldSet('destination_ipaddress', name=_('IP Address')), + FieldSet('destination_aggregate', name=_('Aggregate')), + FieldSet('destination_service', name=_('Service')), ), + "destination_ports", ), ) class Meta: From d15398d402cdedfb5618e08c741199393b5f8cf5 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:07:07 +0200 Subject: [PATCH 24/41] feat(form): ACLExtendedRule: Change validation to support multiple destinations; Add validation to restrict to a single Destination --- netbox_acls/forms/models.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/netbox_acls/forms/models.py b/netbox_acls/forms/models.py index 495c05ee..2cde4cdb 100644 --- a/netbox_acls/forms/models.py +++ b/netbox_acls/forms/models.py @@ -56,9 +56,13 @@ # Sets a standard error message for ACL rules with an action of remark, but no source is set. error_message_action_remark_source_set = "Action is set to remark, Source CANNOT be set." +# Sets a standard error message for ACL rules with an action of remark, but no destination is set. +error_message_action_remark_destination_set = "Action is set to remark, Destination CANNOT be set." # Sets a standard error message for ACL rules when more than one IP/Host sources are set. error_message_sources_more_than_one = "Only one IP/Host related Source can be specified." +# Sets a standard error message for ACL rules when more than one IP/Host destinations are set. +error_message_destinations_more_than_one = "Only one IP/Host related Destination can be specified." class AccessListForm(NetBoxModelForm): """ @@ -736,9 +740,11 @@ def clean(self): Validates form inputs before submitting: - Check if action set to remark, but no remark set. - Check if action set to remark, but source set. + - Check if action set to remark, but destination set. - Check if action set to remark, but protocol set - Check remark set, but action not set to remark. - Check not more than one source is set. + - Check not more than one destination is set. """ super().clean() cleaned_data = self.cleaned_data @@ -748,8 +754,9 @@ def clean(self): remark = cleaned_data.get("remark") sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] + destinations = ["destination_prefix", "destination_iprange", "destination_ipaddress", "destination_aggregate", "destination_service"] + source_ports = cleaned_data.get("source_ports") - destination_prefix = cleaned_data.get("destination_prefix") destination_ports = cleaned_data.get("destination_ports") protocol = cleaned_data.get("protocol") @@ -760,11 +767,13 @@ def clean(self): # Check if action set to remark, but source set. for source in sources: error_message[source] = [error_message_action_remark_source_set] + + # Check if action set to remark, but destination set. + for destination in destinations: + error_message[destination] = [error_message_action_remark_destination_set] if source_ports: error_message["source_ports"] = ["Action is set to remark, Source Ports CANNOT be set."] - if destination_prefix: - error_message["destination_prefix"] = ["Action is set to remark, Destination Prefix CANNOT be set."] if destination_ports: error_message["destination_ports"] = ["Action is set to remark, Destination Ports CANNOT be set."] if protocol: @@ -777,5 +786,10 @@ def clean(self): for source in sources: error_message[source] = [error_message_sources_more_than_one] + # Check not more than one destination is set. + elif sum(bool(cleaned_data.get(destination)) for destination in destinations) > 1: + for destination in destinations: + error_message[destination] = [error_message_destinations_more_than_one] + if error_message: raise ValidationError(error_message) From fa63c801f81aaf64646cf6d4952dcc5b7a2dd2a1 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:09:43 +0200 Subject: [PATCH 25/41] fix(table): Fix error when sorting host in InterfaceAssignment --- netbox_acls/tables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox_acls/tables.py b/netbox_acls/tables.py index b5a045ff..bb12b46e 100644 --- a/netbox_acls/tables.py +++ b/netbox_acls/tables.py @@ -92,6 +92,7 @@ class ACLInterfaceAssignmentTable(NetBoxTable): direction = ChoiceFieldColumn() host = tables.TemplateColumn( template_code=COL_HOST_ASSIGNMENT, + orderable=False, ) assigned_object = tables.Column( linkify=True, @@ -208,3 +209,5 @@ class Meta(NetBoxTable.Meta): "destination_ports", "protocol", ) + + From 4c5b49b9370befeb006361c545d46948214871dd Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:12:27 +0200 Subject: [PATCH 26/41] feat(table): ACLStandardRule: Changing Source Prefix to a single Source column that will display any Source --- netbox_acls/tables.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/netbox_acls/tables.py b/netbox_acls/tables.py index bb12b46e..8fa4fd14 100644 --- a/netbox_acls/tables.py +++ b/netbox_acls/tables.py @@ -22,7 +22,26 @@ {{ record.assigned_object.virtual_machine|placeholder }} {% endif %} """ - +COL_SOURCE_AND_DESTINATION_ASSIGNMENT = """ + {% if record.#replaceme#_prefix %} + {{ record.#replaceme#_prefix|placeholder }} + Prefix + {% elif record.#replaceme#_iprange %} + {{ record.#replaceme#_iprange|placeholder }} + IP-Range + {% elif record.#replaceme#_ipaddress %} + {{ record.#replaceme#_ipaddress|placeholder }} + IP-Address + {% elif record.#replaceme#_aggregate %} + {{ record.#replaceme#_aggregate|placeholder }} + Aggregate + {% elif record.#replaceme#_service %} + {{ record.#replaceme#_service|placeholder }} + Service + {% else %} + {{ ''|placeholder }} + {% endif %} + """ class AccessListTable(NetBoxTable): """ @@ -139,6 +158,10 @@ class ACLStandardRuleTable(NetBoxTable): tags = columns.TagColumn( url_name="plugins:netbox_acls:aclstandardrule_list", ) + source = tables.TemplateColumn( + template_code=COL_SOURCE_AND_DESTINATION_ASSIGNMENT.replace('#replaceme#', 'source'), + order_by=('source_prefix', 'source_iprange', 'source_ipaddress', 'source_aggregate', 'source_service') + ) class Meta(NetBoxTable.Meta): model = ACLStandardRule @@ -151,14 +174,14 @@ class Meta(NetBoxTable.Meta): "remark", "tags", "description", - "source_prefix", + "source", ) default_columns = ( "access_list", "index", "action", "remark", - "source_prefix", + "source", "tags", ) From 91431daf3160a29dfb0e581044678eddf410c8c3 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:13:05 +0200 Subject: [PATCH 27/41] feat(table): ACLExtendedRule: Changing Source Prefix to a single Source column that will display any Source --- netbox_acls/tables.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/netbox_acls/tables.py b/netbox_acls/tables.py index 8fa4fd14..4896f412 100644 --- a/netbox_acls/tables.py +++ b/netbox_acls/tables.py @@ -201,6 +201,10 @@ class ACLExtendedRuleTable(NetBoxTable): tags = columns.TagColumn( url_name="plugins:netbox_acls:aclextendedrule_list", ) + source = tables.TemplateColumn( + template_code=COL_SOURCE_AND_DESTINATION_ASSIGNMENT.replace('#replaceme#', 'source'), + order_by=('source_prefix', 'source_iprange', 'source_ipaddress', 'source_aggregate', 'source_service') + ) protocol = ChoiceFieldColumn() class Meta(NetBoxTable.Meta): @@ -214,7 +218,7 @@ class Meta(NetBoxTable.Meta): "remark", "tags", "description", - "source_prefix", + "source", "source_ports", "destination_prefix", "destination_ports", @@ -226,7 +230,7 @@ class Meta(NetBoxTable.Meta): "action", "remark", "tags", - "source_prefix", + "source", "source_ports", "destination_prefix", "destination_ports", From 5c0f39f88db09cd9f5a53d3eb1de3d980a82abed Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:13:39 +0200 Subject: [PATCH 28/41] feat(table): ACLExtendedRule: Changing Destination Prefix to a single Destination column that will display any Destination --- netbox_acls/tables.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/netbox_acls/tables.py b/netbox_acls/tables.py index 4896f412..223cb781 100644 --- a/netbox_acls/tables.py +++ b/netbox_acls/tables.py @@ -205,6 +205,10 @@ class ACLExtendedRuleTable(NetBoxTable): template_code=COL_SOURCE_AND_DESTINATION_ASSIGNMENT.replace('#replaceme#', 'source'), order_by=('source_prefix', 'source_iprange', 'source_ipaddress', 'source_aggregate', 'source_service') ) + destination = tables.TemplateColumn( + template_code=COL_SOURCE_AND_DESTINATION_ASSIGNMENT.replace('#replaceme#', 'destination'), + order_by=('destination_prefix', 'destination_iprange', 'destination_ipaddress', 'destination_aggregate', 'destination_service') + ) protocol = ChoiceFieldColumn() class Meta(NetBoxTable.Meta): @@ -220,7 +224,7 @@ class Meta(NetBoxTable.Meta): "description", "source", "source_ports", - "destination_prefix", + "destination", "destination_ports", "protocol", ) @@ -232,7 +236,7 @@ class Meta(NetBoxTable.Meta): "tags", "source", "source_ports", - "destination_prefix", + "destination", "destination_ports", "protocol", ) From 668551e59e08621d7647d0a3a44098ce17e0ef12 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:16:32 +0200 Subject: [PATCH 29/41] feat(filter): ACLStandardRule: Adding new Sources to the filter --- netbox_acls/filtersets.py | 17 ++++++++++++++++- netbox_acls/forms/filtersets.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/netbox_acls/filtersets.py b/netbox_acls/filtersets.py index 7a8e6d04..b6c6cc3b 100644 --- a/netbox_acls/filtersets.py +++ b/netbox_acls/filtersets.py @@ -181,7 +181,17 @@ class Meta: """ model = ACLStandardRule - fields = ("id", "access_list", "index", "action") + fields = ( + "id", + "access_list", + "index", + "action", + "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", + ) def search(self, queryset, name, value): """ @@ -191,6 +201,11 @@ def search(self, queryset, name, value): Q(access_list__name__icontains=value) | Q(index__icontains=value) | Q(action__icontains=value) + | Q(source_prefix__icontains=value) + | Q(source_iprange__icontains=value) + | Q(source_ipaddress__icontains=value) + | Q(source_aggregate__icontains=value) + | Q(source_service__icontains=value) ) return queryset.filter(query) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index 41715ffd..352e930e 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -5,7 +5,13 @@ from dcim.models import Device, Interface, Region, Site, SiteGroup, VirtualChassis from django import forms from django.utils.translation import gettext as _ -from ipam.models import Prefix +from ipam.models import ( + Prefix, + IPRange, + IPAddress, + Aggregate, + Service, +) from netbox.forms import NetBoxModelFilterSetForm from utilities.forms.rendering import FieldSet from utilities.forms.fields import ( @@ -181,6 +187,31 @@ class ACLStandardRuleFilterForm(NetBoxModelFilterSetForm): required=False, label="Source Prefix", ) + source_prefix = DynamicModelMultipleChoiceField( + queryset=Prefix.objects.all(), + required=False, + label="Source Prefix", + ) + source_iprange = DynamicModelMultipleChoiceField( + queryset=IPRange.objects.all(), + required=False, + label="Source IP-Range", + ) + source_ipaddress = DynamicModelMultipleChoiceField( + queryset=IPAddress.objects.all(), + required=False, + label="Source IP-Address", + ) + source_aggregate = DynamicModelMultipleChoiceField( + queryset=Aggregate.objects.all(), + required=False, + label="Source Aggregate", + ) + source_service = DynamicModelMultipleChoiceField( + queryset=Service.objects.all(), + required=False, + label="Source Service", + ) action = forms.ChoiceField( choices=add_blank_choice(ACLRuleActionChoices), required=False, From c8a19b69a16223a1c9b2297638a04926bf29dea0 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:17:23 +0200 Subject: [PATCH 30/41] feat(filter): ACLStandardRule: Instruct new Sources to be displayed as Tabbed Group; Improve Fieldsets --- netbox_acls/forms/filtersets.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index 352e930e..2df18fe4 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -20,6 +20,7 @@ TagFilterField, ) from utilities.forms.utils import add_blank_choice +from utilities.forms.rendering import FieldSet, TabbedGroups from virtualization.models import VirtualMachine, VMInterface @@ -218,9 +219,25 @@ class ACLStandardRuleFilterForm(NetBoxModelFilterSetForm): ) fieldsets = ( - FieldSet("access_list", "action", "source_prefix", name=_('Rule Details')), - FieldSet("q", "tag",name=None) + FieldSet("q", "tag",name=None), + FieldSet( + "access_list", + "action", + name=_('Rule Details') + ), + FieldSet( + TabbedGroups( + FieldSet('source_prefix', name=_('Prefix')), + FieldSet('source_iprange', name=_('IP Range')), + FieldSet('source_ipaddress', name=_('IP Address')), + FieldSet('source_aggregate', name=_('Aggregate')), + FieldSet('source_service', name=_('Service')), + ) + ) ) + + + class ACLExtendedRuleFilterForm(NetBoxModelFilterSetForm): """ GUI filter form to search the django ACLExtendedRule model. From 8d1f698edff2593e9c8b2e65ccd3053402a641d0 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:20:30 +0200 Subject: [PATCH 31/41] feat(filter): ACLExtendedRule: Adding new Sources to the filter --- netbox_acls/filtersets.py | 17 ++++++++++++++++- netbox_acls/forms/filtersets.py | 26 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/netbox_acls/filtersets.py b/netbox_acls/filtersets.py index b6c6cc3b..02ff2499 100644 --- a/netbox_acls/filtersets.py +++ b/netbox_acls/filtersets.py @@ -221,7 +221,17 @@ class Meta: """ model = ACLExtendedRule - fields = ("id", "access_list", "index", "action", "protocol") + fields = ( + "id", + "access_list", + "index", + "action", + "protocol", + "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", def search(self, queryset, name, value): """ @@ -232,5 +242,10 @@ def search(self, queryset, name, value): | Q(index__icontains=value) | Q(action__icontains=value) | Q(protocol__icontains=value) + | Q(source_prefix__icontains=value) + | Q(source_iprange__icontains=value) + | Q(source_ipaddress__icontains=value) + | Q(source_aggregate__icontains=value) + | Q(source_service__icontains=value) ) return queryset.filter(query) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index 2df18fe4..dc2270f5 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -261,7 +261,31 @@ class ACLExtendedRuleFilterForm(NetBoxModelFilterSetForm): required=False, label="Source Prefix", ) - desintation_prefix = DynamicModelMultipleChoiceField( + source_prefix = DynamicModelMultipleChoiceField( + queryset=Prefix.objects.all(), + required=False, + label="Source Prefix", + ) + source_iprange = DynamicModelMultipleChoiceField( + queryset=IPRange.objects.all(), + required=False, + label="Source IP-Range", + ) + source_ipaddress = DynamicModelMultipleChoiceField( + queryset=IPAddress.objects.all(), + required=False, + label="Source IP-Address", + ) + source_aggregate = DynamicModelMultipleChoiceField( + queryset=Aggregate.objects.all(), + required=False, + label="Source Aggregate", + ) + source_service = DynamicModelMultipleChoiceField( + queryset=Service.objects.all(), + required=False, + label="Source Service", + ) queryset=Prefix.objects.all(), required=False, label="Destination Prefix", From ef91bdc92e6cd5d3eb34f6e6b63d798e2f25d62a Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:22:11 +0200 Subject: [PATCH 32/41] feat(filter): ACLExtendedRule: Instruct new Sources to be displayed as Tabbed Group; Improve Fieldsets --- netbox_acls/forms/filtersets.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index dc2270f5..7ac508f3 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -296,6 +296,21 @@ class ACLExtendedRuleFilterForm(NetBoxModelFilterSetForm): ) fieldsets = ( - FieldSet("access_list", "action", "source_prefix", "desintation_prefix", "protocol", name=_('Rule Details')), - FieldSet("q", "tag",name=None) + FieldSet("q", "tag",name=None), + FieldSet( + "access_list", + "action", + "protocol", + name=_('Rule Details') + ), + FieldSet( + TabbedGroups( + FieldSet('source_prefix', name=_('Prefix')), + FieldSet('source_iprange', name=_('IP Range')), + FieldSet('source_ipaddress', name=_('IP Address')), + FieldSet('source_aggregate', name=_('Aggregate')), + FieldSet('source_service', name=_('Service')), + ), + "source_ports", + ), ) From 834ab7daab6180eeb5164e76aa05418f5a604092 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:24:10 +0200 Subject: [PATCH 33/41] feat(filter): ACLExtendedRule: Adding new Destinations to the filter --- netbox_acls/filtersets.py | 11 +++++++++++ netbox_acls/forms/filtersets.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/netbox_acls/filtersets.py b/netbox_acls/filtersets.py index 02ff2499..97852335 100644 --- a/netbox_acls/filtersets.py +++ b/netbox_acls/filtersets.py @@ -232,6 +232,12 @@ class Meta: "source_ipaddress", "source_aggregate", "source_service", + "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service" + ) def search(self, queryset, name, value): """ @@ -247,5 +253,10 @@ def search(self, queryset, name, value): | Q(source_ipaddress__icontains=value) | Q(source_aggregate__icontains=value) | Q(source_service__icontains=value) + | Q(destination_prefix__icontains=value) + | Q(destination_iprange__icontains=value) + | Q(destination_ipaddress__icontains=value) + | Q(destination_aggregate__icontains=value) + | Q(destination_service__icontains=value) ) return queryset.filter(query) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index 7ac508f3..084c06c4 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -286,10 +286,32 @@ class ACLExtendedRuleFilterForm(NetBoxModelFilterSetForm): required=False, label="Source Service", ) + + destination_prefix = DynamicModelMultipleChoiceField( queryset=Prefix.objects.all(), required=False, label="Destination Prefix", ) + destination_iprange = DynamicModelMultipleChoiceField( + queryset=IPRange.objects.all(), + required=False, + label="Destination IP-Range", + ) + destination_ipaddress = DynamicModelMultipleChoiceField( + queryset=IPAddress.objects.all(), + required=False, + label="Destination IP-Address", + ) + destination_aggregate = DynamicModelMultipleChoiceField( + queryset=Aggregate.objects.all(), + required=False, + label="Destination Aggregate", + ) + destination_service = DynamicModelMultipleChoiceField( + queryset=Service.objects.all(), + required=False, + label="Destination Service", + ) protocol = forms.ChoiceField( choices=add_blank_choice(ACLProtocolChoices), required=False, From f8cd87755e382d1fd6a68aa9c7fdd732347e69eb Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:24:29 +0200 Subject: [PATCH 34/41] feat(filter): ACLExtendedRule: Instruct new Destinations to be displayed as Tabbed Group --- netbox_acls/forms/filtersets.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/netbox_acls/forms/filtersets.py b/netbox_acls/forms/filtersets.py index 084c06c4..c9eda7f7 100644 --- a/netbox_acls/forms/filtersets.py +++ b/netbox_acls/forms/filtersets.py @@ -335,4 +335,14 @@ class ACLExtendedRuleFilterForm(NetBoxModelFilterSetForm): ), "source_ports", ), + FieldSet( + TabbedGroups( + FieldSet('destination_prefix', name=_('Prefix')), + FieldSet('destination_iprange', name=_('IP Range')), + FieldSet('destination_ipaddress', name=_('IP Address')), + FieldSet('destination_aggregate', name=_('Aggregate')), + FieldSet('destination_service', name=_('Service')), + ), + "destination_ports", + ), ) From dca8e475ed28dfb171b1b64d8072a53a4c7a6061 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:26:44 +0200 Subject: [PATCH 35/41] feat(serializer): ACLStandardRule: Adding new Source Fields to serializer --- netbox_acls/api/serializers.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index e2e42531..a54180ae 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from drf_spectacular.utils import extend_schema_field -from ipam.api.serializers import PrefixSerializer +from ipam.api.serializers import PrefixSerializer, IPRangeSerializer, IPAddressSerializer, AggregateSerializer, ServiceSerializer from netbox.api.fields import ContentTypeField from netbox.api.serializers import NetBoxModelSerializer from rest_framework import serializers @@ -27,6 +27,7 @@ "ACLExtendedRuleSerializer", ] + # Sets a standard error message for ACL rules with an action of remark, but no remark set. error_message_no_remark = "Action is set to remark, you MUST add a remark." # Sets a standard error message for ACL rules with an action of remark, but no source_prefix is set. @@ -190,6 +191,30 @@ class ACLStandardRuleSerializer(NetBoxModelSerializer): default=None, nested=True ) + source_iprange = IPRangeSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_ipaddress = IPAddressSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_aggregate = AggregateSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_service = ServiceSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) class Meta: """ @@ -211,6 +236,10 @@ class Meta: "custom_fields", "last_updated", "source_prefix", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", ) brief_fields = ("id", "url", "display") From 659c2d65a2084c9234e2ca03a4cbb861a259b91e Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:39:30 +0200 Subject: [PATCH 36/41] feat(serializer): ACLStandardRule: Change validation to support multiple sources; Add validation to restrict to a single Source --- netbox_acls/api/serializers.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index a54180ae..1572cf7e 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -30,12 +30,14 @@ # Sets a standard error message for ACL rules with an action of remark, but no remark set. error_message_no_remark = "Action is set to remark, you MUST add a remark." -# Sets a standard error message for ACL rules with an action of remark, but no source_prefix is set. -error_message_action_remark_source_prefix_set = "Action is set to remark, Source Prefix CANNOT be set." +# Sets a standard error message for ACL rules with an action of remark, but no source/destination is set. +error_message_action_remark_source_set = "Action is set to remark, Source CANNOT be set." # Sets a standard error message for ACL rules with an action not set to remark, but no remark is set. error_message_remark_without_action_remark = "CANNOT set remark unless action is set to remark." # Sets a standard error message for ACL rules no associated to an ACL of the same type. error_message_acl_type = "Provided parent Access List is not of right type." +# Sets a standard error message for ACL rules when more than one IP/Host sources are set. +error_message_sources_more_than_one = "Only one IP/Host related Source can be specified." class AccessListSerializer(NetBoxModelSerializer): @@ -247,21 +249,28 @@ def validate(self, data): """ Validate the ACLStandardRule django model's inputs before allowing it to update the instance: - Check if action set to remark, but no remark set. - - Check if action set to remark, but source_prefix set. + - Check if action set to remark, but source set. + - Check not more than one source is set. """ error_message = {} + sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] + if data.get("action") == "remark": # Check if action set to remark, but no remark set. if data.get("remark") is None: error_message["remark"] = [ error_message_no_remark, ] - # Check if action set to remark, but source_prefix set. - if data.get("source_prefix"): - error_message["source_prefix"] = [ - error_message_action_remark_source_prefix_set, - ] + # Check if action set to remark, but source set. + if any(data.get(source) for source in sources): + for source in sources: + error_message[source] = [error_message_action_remark_source_set] + + # Check not more than one source is set. + if sum(bool(data.get(source)) for source in sources) > 1: + for source in sources: + error_message[source] = [error_message_sources_more_than_one] if error_message: raise serializers.ValidationError(error_message) From 70e7b9dac204c1a44ef412b607877cfd618cf936 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:42:12 +0200 Subject: [PATCH 37/41] feat(serializer): ACLExtendedRule: Adding new Source Fields to serializer --- netbox_acls/api/serializers.py | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index 1572cf7e..ddbb4b4d 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -287,12 +287,38 @@ class ACLExtendedRuleSerializer(NetBoxModelSerializer): view_name="plugins-api:netbox_acls-api:aclextendedrule-detail", ) access_list = NestedAccessListSerializer() + source_prefix = PrefixSerializer( required=False, allow_null=True, default=None, nested=True ) + source_iprange = IPRangeSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_ipaddress = IPAddressSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_aggregate = AggregateSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + source_service = ServiceSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + destination_prefix = PrefixSerializer( required=False, allow_null=True, @@ -318,9 +344,16 @@ class Meta: "created", "custom_fields", "last_updated", + "source_prefix", - "source_ports", + "source_iprange", + "source_ipaddress", + "source_aggregate", + "source_service", + "destination_prefix", + + "source_ports", "destination_ports", "protocol", "remark", From bcbd7f1781282bf419447f41656412258ebe0171 Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:43:26 +0200 Subject: [PATCH 38/41] feat(serializer): ACLExtendedRule: Change validation to support multiple sources; Add validation to restrict to a single Source --- netbox_acls/api/serializers.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index ddbb4b4d..ab8532e5 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -363,26 +363,26 @@ def validate(self, data): """ Validate the ACLExtendedRule django model's inputs before allowing it to update the instance: - Check if action set to remark, but no remark set. - - Check if action set to remark, but source_prefix set. + - Check if action set to remark, but source set. - Check if action set to remark, but source_ports set. - - Check if action set to remark, but destination_prefix set. - Check if action set to remark, but destination_ports set. - Check if action set to remark, but protocol set. - - Check if action set to remark, but protocol set. + - Check not more than one source is set. """ error_message = {} + sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] + if data.get("action") == "remark": # Check if action set to remark, but no remark set. if data.get("remark") is None: error_message["remark"] = [ error_message_no_remark, ] - # Check if action set to remark, but source_prefix set. - if data.get("source_prefix"): - error_message["source_prefix"] = [ - error_message_action_remark_source_prefix_set, - ] + # Check if action set to remark, but source set. + if any(data.get(source) for source in sources): + for source in sources: + error_message[source] = [error_message_action_remark_source_set] # Check if action set to remark, but source_ports set. if data.get("source_ports"): error_message["source_ports"] = [ @@ -403,6 +403,12 @@ def validate(self, data): error_message["protocol"] = [ "Action is set to remark, Protocol CANNOT be set.", ] + + # Check not more than one source is set. + if sum(bool(data.get(source)) for source in sources) > 1: + for source in sources: + error_message[source] = [error_message_sources_more_than_one] + if error_message: raise serializers.ValidationError(error_message) From ea1dd2cd6524c465ce9813b7cd573a9b6beb514b Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:44:14 +0200 Subject: [PATCH 39/41] feat(serializer): ACLExtendedRule: Adding new Destination Fields to serializer --- netbox_acls/api/serializers.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index ab8532e5..ccb928bd 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -325,6 +325,30 @@ class ACLExtendedRuleSerializer(NetBoxModelSerializer): default=None, nested=True ) + destination_iprange = IPRangeSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + destination_ipaddress = IPAddressSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + destination_aggregate = AggregateSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) + destination_service = ServiceSerializer( + required=False, + allow_null=True, + default=None, + nested=True + ) class Meta: """ @@ -352,6 +376,10 @@ class Meta: "source_service", "destination_prefix", + "destination_iprange", + "destination_ipaddress", + "destination_aggregate", + "destination_service", "source_ports", "destination_ports", From 18f87c7f720626d4995b4866eba2cee77aa7d6ce Mon Sep 17 00:00:00 2001 From: rvveber Date: Mon, 2 Sep 2024 18:44:46 +0200 Subject: [PATCH 40/41] feat(serializer): ACLExtendedRule: Change validation to support multiple destinatiions; Add validation to restrict to a single Destination --- netbox_acls/api/serializers.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/netbox_acls/api/serializers.py b/netbox_acls/api/serializers.py index ccb928bd..6fd85f99 100644 --- a/netbox_acls/api/serializers.py +++ b/netbox_acls/api/serializers.py @@ -32,12 +32,15 @@ error_message_no_remark = "Action is set to remark, you MUST add a remark." # Sets a standard error message for ACL rules with an action of remark, but no source/destination is set. error_message_action_remark_source_set = "Action is set to remark, Source CANNOT be set." +error_message_action_remark_destination_set = "Action is set to remark, Destination CANNOT be set." # Sets a standard error message for ACL rules with an action not set to remark, but no remark is set. error_message_remark_without_action_remark = "CANNOT set remark unless action is set to remark." # Sets a standard error message for ACL rules no associated to an ACL of the same type. error_message_acl_type = "Provided parent Access List is not of right type." # Sets a standard error message for ACL rules when more than one IP/Host sources are set. error_message_sources_more_than_one = "Only one IP/Host related Source can be specified." +# Sets a standard error message for ACL rules when more than one IP/Host destinations are set. +error_message_destinations_more_than_one = "Only one IP/Host related Destination can be specified." class AccessListSerializer(NetBoxModelSerializer): @@ -392,14 +395,17 @@ def validate(self, data): Validate the ACLExtendedRule django model's inputs before allowing it to update the instance: - Check if action set to remark, but no remark set. - Check if action set to remark, but source set. + - Check if action set to remark, but destination set. - Check if action set to remark, but source_ports set. - Check if action set to remark, but destination_ports set. - Check if action set to remark, but protocol set. - Check not more than one source is set. + - Check not more than one destination is set. """ error_message = {} sources = ["source_prefix", "source_iprange", "source_ipaddress", "source_aggregate", "source_service"] + destinations = ["destination_prefix", "destination_iprange", "destination_ipaddress", "destination_aggregate", "destination_service"] if data.get("action") == "remark": # Check if action set to remark, but no remark set. @@ -411,16 +417,15 @@ def validate(self, data): if any(data.get(source) for source in sources): for source in sources: error_message[source] = [error_message_action_remark_source_set] + # Check if action set to remark, but destination set. + if any(data.get(destination) for destination in destinations): + for destination in destinations: + error_message[destination] = [error_message_action_remark_destination_set] # Check if action set to remark, but source_ports set. if data.get("source_ports"): error_message["source_ports"] = [ "Action is set to remark, Source Ports CANNOT be set.", ] - # Check if action set to remark, but destination_prefix set. - if data.get("destination_prefix"): - error_message["destination_prefix"] = [ - "Action is set to remark, Destination Prefix CANNOT be set.", - ] # Check if action set to remark, but destination_ports set. if data.get("destination_ports"): error_message["destination_ports"] = [ @@ -437,6 +442,10 @@ def validate(self, data): for source in sources: error_message[source] = [error_message_sources_more_than_one] + # Check not more than one destination is set. + if sum(bool(data.get(destination)) for destination in destinations) > 1: + for destination in destinations: + error_message[destination] = [error_message_destinations_more_than_one] if error_message: raise serializers.ValidationError(error_message) From 7558f08e488eb4132880a32b13e67e564f304103 Mon Sep 17 00:00:00 2001 From: rvveber Date: Thu, 17 Oct 2024 11:06:31 +0200 Subject: [PATCH 41/41] feat(migration): Adding migration for the new sources and destinations --- ...equested_source_and_destination_objects.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 netbox_acls/migrations/0005_add_community_requested_source_and_destination_objects.py diff --git a/netbox_acls/migrations/0005_add_community_requested_source_and_destination_objects.py b/netbox_acls/migrations/0005_add_community_requested_source_and_destination_objects.py new file mode 100644 index 00000000..75ad4c34 --- /dev/null +++ b/netbox_acls/migrations/0005_add_community_requested_source_and_destination_objects.py @@ -0,0 +1,88 @@ +# Generated by Django 5.0.6 on 2024-09-02 15:10 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0115_convert_dashboard_widgets'), + ('ipam', '0069_gfk_indexes'), + ('netbox_acls', '0004_netbox_acls'), + ] + + operations = [ + migrations.AddField( + model_name='aclextendedrule', + name='destination_aggregate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.aggregate'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='destination_ipaddress', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.ipaddress'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='destination_iprange', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.iprange'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='destination_service', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.service'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='source_aggregate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.aggregate'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='source_ipaddress', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.ipaddress'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='source_iprange', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.iprange'), + ), + migrations.AddField( + model_name='aclextendedrule', + name='source_service', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.service'), + ), + migrations.AddField( + model_name='aclstandardrule', + name='source_aggregate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.aggregate'), + ), + migrations.AddField( + model_name='aclstandardrule', + name='source_ipaddress', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.ipaddress'), + ), + migrations.AddField( + model_name='aclstandardrule', + name='source_iprange', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.iprange'), + ), + migrations.AddField( + model_name='aclstandardrule', + name='source_service', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='ipam.service'), + ), + migrations.AddConstraint( + model_name='aclextendedrule', + constraint=models.CheckConstraint(check=models.Q(models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', False), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', False), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', False), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', False), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', False)), _connector='OR'), name='not_more_than_one_source_for_extended_rule'), + ), + migrations.AddConstraint( + model_name='aclextendedrule', + constraint=models.CheckConstraint(check=models.Q(models.Q(('destination_prefix__isnull', True), ('destination_iprange__isnull', True), ('destination_ipaddress__isnull', True), ('destination_aggregate__isnull', True), ('destination_service__isnull', True)), models.Q(('destination_prefix__isnull', False), ('destination_iprange__isnull', True), ('destination_ipaddress__isnull', True), ('destination_aggregate__isnull', True), ('destination_service__isnull', True)), models.Q(('destination_prefix__isnull', True), ('destination_iprange__isnull', False), ('destination_ipaddress__isnull', True), ('destination_aggregate__isnull', True), ('destination_service__isnull', True)), models.Q(('destination_prefix__isnull', True), ('destination_iprange__isnull', True), ('destination_ipaddress__isnull', False), ('destination_aggregate__isnull', True), ('destination_service__isnull', True)), models.Q(('destination_prefix__isnull', True), ('destination_iprange__isnull', True), ('destination_ipaddress__isnull', True), ('destination_aggregate__isnull', False), ('destination_service__isnull', True)), models.Q(('destination_prefix__isnull', True), ('destination_iprange__isnull', True), ('destination_ipaddress__isnull', True), ('destination_aggregate__isnull', True), ('destination_service__isnull', False)), _connector='OR'), name='not_more_than_one_destination_for_extended_rule'), + ), + migrations.AddConstraint( + model_name='aclstandardrule', + constraint=models.CheckConstraint(check=models.Q(models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', False), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', False), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', False), ('source_aggregate__isnull', True), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', False), ('source_service__isnull', True)), models.Q(('source_prefix__isnull', True), ('source_iprange__isnull', True), ('source_ipaddress__isnull', True), ('source_aggregate__isnull', True), ('source_service__isnull', False)), _connector='OR'), name='not_more_than_one_source_for_standard_rule'), + ), + ]