From b92f4cc0e021027b6b4f958d9acf89d27f3cf0c7 Mon Sep 17 00:00:00 2001 From: Vincent Simonin <vincent.simonin@ext.ec.europa.eu> Date: Mon, 11 Sep 2023 18:13:07 +0200 Subject: [PATCH] Make alt_name an array of string --- .../netbox_cert_plugin/filtersets.py | 8 ++-- .../netbox_cert_plugin/forms.py | 1 + .../migrations/0001_initial.py | 1 + .../migrations/0002_new_alt_name_array.py | 27 ++++++++++++++ .../netbox_cert_plugin/models.py | 20 +++++----- .../tests/e2e/test_certificate_creation.py | 37 +++++++++++++++++++ 6 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0002_new_alt_name_array.py diff --git a/plugins/netbox-cert-plugin/netbox_cert_plugin/filtersets.py b/plugins/netbox-cert-plugin/netbox_cert_plugin/filtersets.py index 51bf487..e64bec8 100644 --- a/plugins/netbox-cert-plugin/netbox_cert_plugin/filtersets.py +++ b/plugins/netbox-cert-plugin/netbox_cert_plugin/filtersets.py @@ -1,5 +1,6 @@ """Filtersets definitions""" +from django_filters import filters from netbox.filtersets import NetBoxModelFilterSet from django.db.models import Q from .models import Certificate @@ -8,6 +9,8 @@ from .models import Certificate class CertificateFilterSet(NetBoxModelFilterSet): """Certificate filterset definition class""" + alt_name = filters.CharFilter(lookup_expr="icontains") + class Meta: model = Certificate fields = ( @@ -25,7 +28,4 @@ class CertificateFilterSet(NetBoxModelFilterSet): """override""" if not value.strip(): return queryset - return queryset.filter( - Q(cn__icontains=value) - | Q(alt_name__icontains=value) - ) + return queryset.filter(Q(cn__icontains=value) | Q(alt_name__icontains=value)) diff --git a/plugins/netbox-cert-plugin/netbox_cert_plugin/forms.py b/plugins/netbox-cert-plugin/netbox_cert_plugin/forms.py index 736821f..e458a92 100644 --- a/plugins/netbox-cert-plugin/netbox_cert_plugin/forms.py +++ b/plugins/netbox-cert-plugin/netbox_cert_plugin/forms.py @@ -83,4 +83,5 @@ class CertificateImportForm(NetBoxModelImportForm): labels = { "ca": "Certificate Authority. Can be 'letsencrypt', 'comisign', 'globalsign'", "expiration_time": "Expiration time needed. Can be '1m', '3m', '6m', '1y', '3y'", + "alt_name": "Alt name separated by commas, encased with double quotes (e.g. \"alt1,alt2,alt3\")" } diff --git a/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0001_initial.py b/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0001_initial.py index 9379126..2f7476d 100644 --- a/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0001_initial.py +++ b/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0001_initial.py @@ -2,6 +2,7 @@ # pylint: disable=C0103 from django.db import migrations, models +from django.contrib.postgres.fields.array import ArrayField import taggit.managers import utilities.json diff --git a/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0002_new_alt_name_array.py b/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0002_new_alt_name_array.py new file mode 100644 index 0000000..eead2a3 --- /dev/null +++ b/plugins/netbox-cert-plugin/netbox_cert_plugin/migrations/0002_new_alt_name_array.py @@ -0,0 +1,27 @@ +"""Migration File""" +# pylint: disable=C0103 + +from django.db import migrations, models +from django.contrib.postgres.fields.array import ArrayField + + +class Migration(migrations.Migration): + """Migration Class""" + + initial = True + + dependencies = [("netbox_cert_plugin", "0001_initial")] + + operations = [ + migrations.AddField( + model_name="Certificate", + name="new_alt_name", + field=ArrayField(models.CharField(max_length=256), blank=True, null=True), + ), + migrations.RunSQL( + sql="UPDATE netbox_cert_plugin_certificate AS t set new_alt_name=ARRAY[t.alt_name]", + reverse_sql="UPDATE netbox_cert_plugin_certificate AS t set new_alt_name=NULL", + ), + migrations.RemoveField(model_name="Certificate", name="alt_name"), + migrations.RenameField(model_name="Certificate", new_name="alt_name", old_name="new_alt_name") + ] diff --git a/plugins/netbox-cert-plugin/netbox_cert_plugin/models.py b/plugins/netbox-cert-plugin/netbox_cert_plugin/models.py index 7812802..a504910 100644 --- a/plugins/netbox-cert-plugin/netbox_cert_plugin/models.py +++ b/plugins/netbox-cert-plugin/netbox_cert_plugin/models.py @@ -2,6 +2,7 @@ from django.db import models from django.urls import reverse +from django.contrib.postgres.fields.array import ArrayField from netbox.models import NetBoxModel from utilities.choices import ChoiceSet @@ -19,6 +20,7 @@ class CaChoices(ChoiceSet): ("globalsign", "GlobalSign", "blue"), ] + class ExpirationTimeChoices(ChoiceSet): """Expiration time choices definition class""" @@ -43,12 +45,16 @@ class Certificate(NetBoxModel): blank=False, verbose_name="Common Name", unique=True, + help_text="Unique Common Name" ) - alt_name = models.CharField( - max_length=256, - blank=True, + alt_name = ArrayField( + base_field=models.CharField( + max_length=256 + ), null=True, + blank=True, verbose_name="Alt Name", + help_text="Alt Name is a list of host separated by commas (e.g. alt1,alt2,alt3)" ) ca = models.CharField( max_length=32, @@ -65,14 +71,10 @@ class Certificate(NetBoxModel): verbose_name="Expiration time needed", ) cert_created_at = models.DateField( - blank=True, - null=True, - verbose_name="Effective certificate's creation date" + blank=True, null=True, verbose_name="Effective certificate's creation date" ) cert_expired_at = models.DateField( - blank=True, - null=True, - verbose_name="Effective certificate's expiration date" + blank=True, null=True, verbose_name="Effective certificate's expiration date" ) class Meta: diff --git a/plugins/netbox-cert-plugin/tests/e2e/test_certificate_creation.py b/plugins/netbox-cert-plugin/tests/e2e/test_certificate_creation.py index 9378354..bf91abb 100644 --- a/plugins/netbox-cert-plugin/tests/e2e/test_certificate_creation.py +++ b/plugins/netbox-cert-plugin/tests/e2e/test_certificate_creation.py @@ -100,6 +100,43 @@ class TestCertificateCreation(unittest.TestCase): self.assertEqual(response.status_code, 400) self.assertEqual(response.content, b'{"expiration_time":["\\"10m\\" is not a valid choice."]}') + def test_that_alt_name_is_an_array(self) -> None: + """Test that Alt name is an array""" + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/", + json={ + "cn": "truc00.com", + "ca": "letsencrypt", + "expiration_time": "1m", + "alt_name": "truc01.com" + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 400) + self.assertEqual(response.content, b'{"alt_name":["Expected a list of items but got type \\"str\\"."]}') + + def test_that_certificate_is_created_with_alt_name(self) -> None: + """Test that certificate is created""" + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/", + json={ + "cn": "truc00.com", + "ca": "letsencrypt", + "expiration_time": "1m", + "alt_name": ["192.168.1.1", "truc01.com"] + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 201) + + self.mapping_id = json.loads(response.content)["id"] + def tearDown(self) -> None: """Teardown function""" -- GitLab