Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Verified Commit e76e52e6 authored by Vincent SIMONIN's avatar Vincent SIMONIN
Browse files

:sparkles: Add new certificate property `state`

parent 8c2df14f
No related branches found
No related tags found
1 merge request!52✨ Add new certificate property `state`
Pipeline #112649 passed
Showing with 119 additions and 45 deletions
......@@ -23,6 +23,7 @@ class NestedCertificateSerializer(WritableNestedSerializer):
"expiration_time",
"cert_created_at",
"cert_expired_at",
"state",
"custom_fields",
"created",
"last_updated",
......@@ -48,6 +49,7 @@ class CertificateSerializer(NetBoxModelSerializer):
"expiration_time",
"cert_created_at",
"cert_expired_at",
"state",
"custom_fields",
"created",
"last_updated",
......
......@@ -19,6 +19,7 @@ class CertificateFilterSet(NetBoxModelFilterSet):
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
)
......
......@@ -8,7 +8,7 @@ from netbox.forms import (
)
from utilities.forms.widgets import DatePicker
from utilities.forms.fields import TagFilterField
from .models import Certificate, ExpirationTimeChoices, CaChoices
from .models import Certificate, ExpirationTimeChoices, CaChoices, StateChoices
class CertificateForm(NetBoxModelForm):
......@@ -21,6 +21,7 @@ class CertificateForm(NetBoxModelForm):
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
"tags",
......@@ -31,6 +32,7 @@ class CertificateForm(NetBoxModelForm):
"alt_name": "Alt name",
"ca": "CA",
"expiration_time": "Expiration time",
"state": "Certificate state",
"cert_created_at": "Effective certificate's creation date",
"cert_expired_at": "Effective certificate's expiration date",
}
......@@ -56,6 +58,9 @@ class CertificateFilterForm(NetBoxModelFilterSetForm):
expiration_time = forms.MultipleChoiceField(
label="Expiration time", choices=ExpirationTimeChoices, required=False
)
state = forms.MultipleChoiceField(
label="Certificate state", choices=StateChoices, required=False
)
cert_created_at = forms.DateField(
label="Effective certificate's creation date", required=False, widget=DatePicker
)
......@@ -77,11 +82,13 @@ class CertificateImportForm(NetBoxModelImportForm):
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
)
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\")"
"state": "Certificate status. Can be 'pending', 'valid', 'to_be_renewed'",
"alt_name": 'Alt name separated by commas, encased with double quotes (e.g. "alt1,alt2,alt3")',
}
"""Migration File"""
# pylint: disable=C0103
from django.db import migrations, models
class Migration(migrations.Migration):
"""Migration Class"""
dependencies = [
("netbox_cert_plugin", "0002_new_alt_name_array"),
]
operations = [
migrations.AlterModelOptions(
name="certificate",
options={"ordering": ["cn"]},
),
migrations.AddField(
model_name="certificate",
name="state",
field=models.CharField(default="valid", max_length=32),
),
migrations.AlterField(
model_name="certificate",
name="ca",
field=models.CharField(default="letsencrypt", max_length=32),
),
migrations.AlterField(
model_name="certificate",
name="expiration_time",
field=models.CharField(default="1m", max_length=2),
),
]
......@@ -37,6 +37,20 @@ class ExpirationTimeChoices(ChoiceSet):
]
class StateChoices(ChoiceSet):
"""CA choices definition class"""
key = "Certificate.state"
DEFAULT_VALUE = "pending"
CHOICES = [
("pending", "Pending", "yellow"),
("valid", "Valid", "red"),
("to_be_renewed", "To be renewed", "blue"),
]
class Certificate(NetBoxModel):
"""Certificate definition class"""
......@@ -45,16 +59,14 @@ class Certificate(NetBoxModel):
blank=False,
verbose_name="Common Name",
unique=True,
help_text="Unique Common Name"
help_text="Unique Common Name",
)
alt_name = ArrayField(
base_field=models.CharField(
max_length=256
),
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)"
help_text="Alt Name is a list of host separated by commas (e.g. alt1,alt2,alt3)",
)
ca = models.CharField(
max_length=32,
......@@ -66,7 +78,7 @@ class Certificate(NetBoxModel):
expiration_time = models.CharField(
max_length=2,
choices=ExpirationTimeChoices,
default=CaChoices.DEFAULT_VALUE,
default=ExpirationTimeChoices.DEFAULT_VALUE,
blank=False,
verbose_name="Expiration time needed",
)
......@@ -76,6 +88,13 @@ class Certificate(NetBoxModel):
cert_expired_at = models.DateField(
blank=True, null=True, verbose_name="Effective certificate's expiration date"
)
state = models.CharField(
max_length=32,
choices=StateChoices,
default=StateChoices.DEFAULT_VALUE,
blank=False,
verbose_name="Certificate State",
)
class Meta:
ordering = ["cn"]
......
......@@ -9,8 +9,4 @@ class CertificateIndex(SearchIndex):
"""Certificate search definition class"""
model = Certificate
fields = (
('cn', 256),
('alt_name', 256),
('ca', 32),
)
fields = (("cn", 256), ("alt_name", 256), ("ca", 32), ("state", 32))
......@@ -19,16 +19,18 @@ class CertificateTable(NetBoxTable):
"cn",
"alt_name",
"ca",
"state",
"expiration_time",
"cert_created_at",
"cert_expired_at",
"tags"
"tags",
)
default_columns = (
"cn",
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
)
......@@ -38,6 +38,10 @@
<th scope="row">Expiration time</th>
<td>{{ object.get_expiration_time_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">State</th>
<td>{{ object.get_state_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">Effective certificate's creation date</th>
<td>{{ object.cert_created_at|placeholder }}</td>
......
......@@ -22,11 +22,21 @@ class TestCertificateCreation(unittest.TestCase):
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={
"cn": "truc00.com",
"ca": "letsencrypt",
"expiration_time": "1m"
},
json={"cn": "truc00.com", "ca": "letsencrypt", "expiration_time": "1m"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
self.assertEqual(response.status_code, 201)
self.mapping_id = json.loads(response.content)["id"]
def test_that_default_certificate_properties_is_set(self) -> None:
"""Test that default certificate properties is set"""
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={"cn": "truc00.com", "ca": "letsencrypt"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
......@@ -35,16 +45,16 @@ class TestCertificateCreation(unittest.TestCase):
self.mapping_id = json.loads(response.content)["id"]
content = json.loads(response.content)
self.assertEqual(content["expiration_time"], "1m")
self.assertEqual(content["state"], "pending")
def test_that_cn_is_unique(self) -> None:
"""Test that CN is unique"""
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={
"cn": "truc00.com",
"ca": "letsencrypt",
"expiration_time": "1m"
},
json={"cn": "truc00.com", "ca": "letsencrypt", "expiration_time": "1m"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
......@@ -55,51 +65,47 @@ class TestCertificateCreation(unittest.TestCase):
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={
"cn": "truc00.com",
"ca": "commissign",
"expiration_time": "3m"
},
json={"cn": "truc00.com", "ca": "commissign", "expiration_time": "3m"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, b'{"cn":["certificate with this Common Name already exists."]}')
self.assertEqual(
response.content,
b'{"cn":["certificate with this Common Name already exists."]}',
)
def test_that_ca_is_valid(self) -> None:
"""Test that CA is valid"""
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={
"cn": "truc00.com",
"ca": "randomca",
"expiration_time": "3m"
},
json={"cn": "truc00.com", "ca": "randomca", "expiration_time": "3m"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, b'{"ca":["\\"randomca\\" is not a valid choice."]}')
self.assertEqual(
response.content, b'{"ca":["\\"randomca\\" is not a valid choice."]}'
)
def test_that_expiration_time_is_valid(self) -> None:
"""Test that expiration time is valid"""
response = requests.post(
url=f"http://{HOST}:{PORT}/api/plugins/cert/certificate/",
json={
"cn": "truc00.com",
"ca": "commissign",
"expiration_time": "10m"
},
json={"cn": "truc00.com", "ca": "commissign", "expiration_time": "10m"},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, b'{"expiration_time":["\\"10m\\" is not a valid choice."]}')
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"""
......@@ -110,14 +116,17 @@ class TestCertificateCreation(unittest.TestCase):
"cn": "truc00.com",
"ca": "letsencrypt",
"expiration_time": "1m",
"alt_name": "truc01.com"
"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\\"."]}')
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"""
......@@ -128,7 +137,7 @@ class TestCertificateCreation(unittest.TestCase):
"cn": "truc00.com",
"ca": "commissign",
"expiration_time": "1m",
"alt_name": ["192.168.1.1", "truc01.com"]
"alt_name": ["192.168.1.1", "truc01.com"],
},
headers={"Authorization": f"Token {API_KEY}"},
timeout=5,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment