Skip to content

Commit

Permalink
Merge pull request #117 from nautobot/feature-applications
Browse files Browse the repository at this point in the history
Adding Support For Applications & Application Groups
  • Loading branch information
whitej6 committed Nov 22, 2022
2 parents c3052e3 + fedede3 commit 2fa5321
Show file tree
Hide file tree
Showing 49 changed files with 3,154 additions and 2,422 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog

## v1.2.0-alpha.0 - 2022-11-22

### Added

- #108 Application & ApplicationGroup support

### Fixed

- #117 Resolved `main` tab not loading on initial load of Policy & PolicyRule detail page

### Changed

- #117 Reorganized `models/` folder to improve developer experience

## v1.1.3 - 2022-11-16

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion docs/example.md
Expand Up @@ -245,4 +245,4 @@ Below is an example response to the REST API GET request for a Policy object at
"status": "35206353-47f4-4e71-9e2c-807092b6c439",
"tenant": "5fabe6c7-84a6-45af-95a0-384f9ebcbeb8"
}
```
```
Binary file modified docs/images/policy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions docs/models.md
Expand Up @@ -96,6 +96,16 @@ This plugin uses [custom models to model many-to-many](https://docs.djangoprojec
heading-offset=2
%}

{%
include-markdown "./models/applicationobject.md"
heading-offset=2
%}

{%
include-markdown "./models/applicationobjectgroup.md"
heading-offset=2
%}

{%
include-markdown "./models/fqdn.md"
heading-offset=2
Expand Down
15 changes: 15 additions & 0 deletions docs/models/applicationobject.md
@@ -0,0 +1,15 @@
# ApplicationObject

Defines an Applications that are typically referenced in NextGen Firewalls.

## Attributes

* Name (string)
* Description (optional, string)
* Category (optional, string)
* Subategory (optional, string)
* Technology (optional, string)
* Risk (optional, positive integer)
* Default Type (optional, int OR int range OR comma delimited list of int or int range)
* Default IP Protocol (optional, string)
* Status (FK to Status)
10 changes: 10 additions & 0 deletions docs/models/applicationobjectgroup.md
@@ -0,0 +1,10 @@
# ApplicationObjectGroup

Represents a group of Application Objects.

## Attributes

* Name (string)
* Description (optional, string)
* Application Objects (M2M to ApplicationObject)
* Status (FK to Status)
2 changes: 1 addition & 1 deletion docs/models/serviceobject.md
Expand Up @@ -8,7 +8,7 @@ For well-known ports, it is best to use the port name as the name of the object.

* Name (string)
* Description (optional, string)
* Port (optional, int OR int range)
* Port (optional, int OR int range OR comma delimited list of int or int range)
* Must be specified as a valid layer 4 port OR port range (e.g. 80 OR 8080-8088).
* IP Protocol (string, choice field)
* IANA protocols (e.g. TCP UDP ICMP)
Expand Down
12 changes: 12 additions & 0 deletions nautobot_firewall_models/api/nested_serializers.py
Expand Up @@ -5,6 +5,18 @@
from nautobot_firewall_models import models


class NestedApplicationSerializer(WritableNestedSerializer):
"""Nested serializer for FQDN."""

url = HyperlinkedIdentityField(view_name="plugins-api:nautobot_firewall_models-api:applicationobject-detail")

class Meta:
"""Meta attributes."""

model = models.FQDN
fields = ["id", "url", "name"]


class NestedFQDNSerializer(WritableNestedSerializer):
"""Nested serializer for FQDN."""

Expand Down
52 changes: 51 additions & 1 deletion nautobot_firewall_models/api/serializers.py
Expand Up @@ -18,7 +18,11 @@


from nautobot_firewall_models import models
from nautobot_firewall_models.api.nested_serializers import NestedFQDNSerializer, NestedIPRangeSerializer
from nautobot_firewall_models.api.nested_serializers import (
NestedFQDNSerializer,
NestedIPRangeSerializer,
# NestedApplicationSerializer,
)


class StatusModelSerializerMixin(_StatusModelSerializerMixin): # pylint: disable=abstract-method
Expand Down Expand Up @@ -95,6 +99,40 @@ class Meta:
fields = "__all__"


class ApplicationObjectSerializer(TaggedObjectSerializer, StatusModelSerializerMixin, NautobotModelSerializer):
"""ApplicationObject Serializer."""

url = serializers.HyperlinkedIdentityField(
view_name="plugins-api:nautobot_firewall_models-api:applicationobject-detail"
)

class Meta:
"""Meta attributes."""

model = models.ApplicationObject
fields = "__all__"


class ApplicationObjectGroupSerializer(TaggedObjectSerializer, StatusModelSerializerMixin, NautobotModelSerializer):
"""ApplicationObjectGroup Serializer."""

url = serializers.HyperlinkedIdentityField(
view_name="plugins-api:nautobot_firewall_models-api:applicationobject-detail"
)
application_objects = SerializedPKRelatedField(
queryset=models.ApplicationObject.objects.all(),
serializer=ApplicationObjectSerializer,
required=False,
many=True,
)

class Meta:
"""Meta attributes."""

model = models.ApplicationObjectGroup
fields = "__all__"


class ServiceObjectSerializer(TaggedObjectSerializer, StatusModelSerializerMixin, NautobotModelSerializer):
"""ServiceObject Serializer."""

Expand Down Expand Up @@ -209,6 +247,18 @@ class PolicyRuleSerializer(TaggedObjectSerializer, StatusModelSerializerMixin, N
required=False,
many=True,
)
applications = SerializedPKRelatedField(
queryset=models.ApplicationObject.objects.all(),
serializer=ApplicationObjectSerializer,
required=False,
many=True,
)
application_groups = SerializedPKRelatedField(
queryset=models.ApplicationObjectGroup.objects.all(),
serializer=ApplicationObjectGroupSerializer,
required=False,
many=True,
)

class Meta:
"""Meta attributes."""
Expand Down
2 changes: 2 additions & 0 deletions nautobot_firewall_models/api/urls.py
Expand Up @@ -8,6 +8,8 @@
router = OrderedDefaultRouter()
router.register("address-object", views.AddressObjectViewSet)
router.register("address-object-group", views.AddressObjectGroupViewSet)
router.register("application-object", views.ApplicationObjectViewSet)
router.register("application-object-group", views.ApplicationObjectGroupViewSet)
router.register("capirca-policy", views.CapircaPolicyViewSet)
router.register("fqdn", views.FQDNViewSet)
router.register("ip-range", views.IPRangeViewSet)
Expand Down
16 changes: 16 additions & 0 deletions nautobot_firewall_models/api/views.py
Expand Up @@ -40,6 +40,22 @@ class AddressObjectGroupViewSet(NautobotModelViewSet):
filterset_class = filters.AddressObjectGroupFilterSet


class ApplicationObjectViewSet(NautobotModelViewSet):
"""ApplicationObject viewset."""

queryset = models.ApplicationObject.objects.all()
serializer_class = serializers.ApplicationObjectSerializer
filterset_class = filters.ApplicationObjectFilterSet


class ApplicationObjectGroupViewSet(NautobotModelViewSet):
"""ApplicationObjectGroup viewset."""

queryset = models.ApplicationObjectGroup.objects.all()
serializer_class = serializers.ApplicationObjectGroupSerializer
filterset_class = filters.ApplicationObjectGroupFilterSet


class ServiceObjectViewSet(NautobotModelViewSet):
"""ServiceObject viewset."""

Expand Down
22 changes: 22 additions & 0 deletions nautobot_firewall_models/filters.py
Expand Up @@ -77,6 +77,28 @@ class Meta:
fields = ["id", "name", "address_objects", "description"]


class ApplicationObjectFilterSet(BaseFilterSet, NautobotFilterSet):
"""Filter for ApplicationObject."""

class Meta:
"""Meta attributes for filter."""

model = models.ApplicationObject

fields = ["id", "name", "description", "category", "subcategory", "risk", "description"]


class ApplicationObjectGroupFilterSet(BaseFilterSet, NautobotFilterSet):
"""Filter for ApplicationObjectGroup."""

class Meta:
"""Meta attributes for filter."""

model = models.ApplicationObjectGroup

fields = ["id", "name", "application_objects", "description"]


class ServiceObjectFilterSet(BaseFilterSet, NautobotFilterSet):
"""Filter for ServiceObject."""

Expand Down
114 changes: 114 additions & 0 deletions nautobot_firewall_models/forms.py
Expand Up @@ -197,6 +197,110 @@ class Meta:
]


class ApplicationObjectFilterForm(BootstrapMixin, StatusModelFilterFormMixin, CustomFieldModelFilterFormMixin):
"""Filter form to filter searches."""

field_order = ["q", "name"]

model = models.ApplicationObject
q = forms.CharField(
required=False,
label="Search",
help_text="Search within Name or Description.",
)
name = forms.CharField(required=False, label="Name")
category = DynamicModelChoiceField(
queryset=models.ApplicationObject.objects.all(), required=False, label="Category"
)


class ApplicationObjectForm(BootstrapMixin, RelationshipModelFormMixin, forms.ModelForm):
"""ApplicationObject creation/edit form."""

class Meta:
"""Meta attributes."""

model = models.ApplicationObject
fields = [
"name",
"description",
"category",
"subcategory",
"technology",
"risk",
"default_type",
"default_ip_protocol",
"status",
]


class ApplicationObjectBulkEditForm(BootstrapMixin, StatusModelBulkEditFormMixin, BulkEditForm):
"""ApplicationObject bulk edit form."""

pk = DynamicModelMultipleChoiceField(
queryset=models.ApplicationObject.objects.all(), widget=forms.MultipleHiddenInput
)
description = forms.CharField(required=False)
risk = forms.IntegerField(required=False)
technology = forms.CharField(required=False)
category = forms.CharField(required=False)
subcategory = forms.CharField(required=False)

class Meta:
"""Meta attributes."""

nullable_fields = [
"description",
"default_ip_protocol",
"default_type",
"technology",
"category",
"subcategory",
]


class ApplicationObjectGroupFilterForm(BootstrapMixin, StatusModelFilterFormMixin, CustomFieldModelFilterFormMixin):
"""Filter form to filter searches."""

field_order = ["q", "name"]

model = models.ApplicationObjectGroup
q = forms.CharField(
required=False,
label="Search",
help_text="Search within Name or Description.",
)
name = forms.CharField(required=False, label="Name")


class ApplicationObjectGroupForm(BootstrapMixin, RelationshipModelFormMixin, forms.ModelForm):
"""ApplicationObjectGroup creation/edit form."""

application_objects = DynamicModelMultipleChoiceField(queryset=models.ApplicationObject.objects.all())

class Meta:
"""Meta attributes."""

model = models.ApplicationObjectGroup
fields = ["name", "description", "application_objects", "status", "tags"]


class ApplicationObjectGroupBulkEditForm(BootstrapMixin, StatusModelBulkEditFormMixin, BulkEditForm):
"""ApplicationObjectGroup bulk edit form."""

pk = DynamicModelMultipleChoiceField(
queryset=models.ApplicationObjectGroup.objects.all(), widget=forms.MultipleHiddenInput
)
description = forms.CharField(required=False)

class Meta:
"""Meta attributes."""

nullable_fields = [
"description",
]


class ServiceObjectFilterForm(BootstrapMixin, StatusModelFilterFormMixin, CustomFieldModelFilterFormMixin):
"""Filter form to filter searches."""

Expand Down Expand Up @@ -473,6 +577,14 @@ class PolicyRuleForm(BootstrapMixin, CustomFieldModelFormMixin, RelationshipMode
destination_service_groups = DynamicModelMultipleChoiceField(
queryset=models.ServiceObjectGroup.objects.all(), label="Destination Service Object Groups", required=False
)
applications = DynamicModelMultipleChoiceField(
queryset=models.ApplicationObject.objects.all(), label="Destination Application Objects", required=False
)
application_groups = DynamicModelMultipleChoiceField(
queryset=models.ApplicationObjectGroup.objects.all(),
label="Destination Application Object Groups",
required=False,
)
request_id = forms.CharField(required=False, label="Optional field for request ticket identifier.")

class Meta:
Expand All @@ -495,6 +607,8 @@ class Meta:
"destination_zone",
"destination_services",
"destination_service_groups",
"applications",
"application_groups",
"action",
"log",
"status",
Expand Down

0 comments on commit 2fa5321

Please sign in to comment.