Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit 8f48d229 authored by Diego MENDEZ's avatar Diego MENDEZ
Browse files

:twisted_rightwards_arrows: Merge branch 'develop' into 'main'

Develop

See merge request !125
parents 90a03a13 17652f48
Branches
Tags
4 merge requests!133Draft: Rebase v4 firewall,!131Draft: Rebase v4.1,!129Draft: Rebase v4,!125Develop
Pipeline #225853 passed
Showing
with 0 additions and 695 deletions
......@@ -30,8 +30,6 @@ lint-job:
- source "$CI_PROJECT_DIR/plugins/venv/bin/activate"
- pip install 'pylint==3.2'
script:
- pylint "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/netbox_rps_plugin" "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/tests"
- pylint "$CI_PROJECT_DIR/plugins/netbox-cert-plugin/netbox_cert_plugin" "$CI_PROJECT_DIR/plugins/netbox-cert-plugin/tests"
- pylint "$CI_PROJECT_DIR/tests"
after_script:
- deactivate
......@@ -81,12 +79,8 @@ run-test-job:
before_script:
- python3 -m venv "$CI_PROJECT_DIR/plugins/venv"
- source "$CI_PROJECT_DIR/plugins/venv/bin/activate"
- pip install -r "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/tests/requirements.e2e.txt"
- pip install -r "$CI_PROJECT_DIR/plugins/netbox-cert-plugin/tests/requirements.e2e.txt"
- pip install -r "$CI_PROJECT_DIR/tests/requirements.e2e.txt"
script:
- env HOST="${TEST_HOST}" PORT="${TEST_PORT}" API_KEY="$API_KEY" pytest -s --junit-xml=tests-report-netbox-rps-plugin.xml "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/tests/"
- env HOST="${TEST_HOST}" PORT="${TEST_PORT}" API_KEY="$API_KEY" pytest -s --junit-xml=tests-report-netbox-cert-plugin.xml "$CI_PROJECT_DIR/plugins/netbox-cert-plugin/tests/"
- env HOST="${TEST_HOST}" PORT="${TEST_PORT}" API_KEY="$API_KEY" pytest -s --junit-xml=tests-report-netbox-dist-plugin.xml "$CI_PROJECT_DIR/tests/e2e/"
after_script:
- deactivate
......
global-include *.html
# Certificate Plugin for Netbox
A Netbox plugin for certificate management
## Tests
see [tests directory](./tests/README.md)
"""Netbox Plugin Configuration"""
# pylint: disable=E0401
from extras.plugins import PluginConfig
class NetBoxCertConfig(PluginConfig):
"""Netbox Plugin Configuration class"""
name = "netbox_cert_plugin"
verbose_name = "Netbox Certificate"
description = "A Netbox plugin to manage certificates"
version = "0.0.4"
author = "Vincent Simonin"
author_email = "vincent.simonin@ext.ec.europa.eu"
base_url = "cert"
# pylint: disable=C0103
config = NetBoxCertConfig
"""API Serializer definitions"""
from rest_framework import serializers
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from ..models import Certificate
class NestedCertificateSerializer(WritableNestedSerializer):
"""Nested Certificate Serializer class"""
url = serializers.HyperlinkedIdentityField(
view_name="plugins-api:netbox_cert_plugin-api:certificate-detail"
)
class Meta:
model = Certificate
fields = (
"id",
"url",
"cn",
"alt_name",
"ca",
"expiration_time",
"cert_created_at",
"cert_expired_at",
"state",
"custom_fields",
"created",
"last_updated",
"tags",
)
class CertificateSerializer(NetBoxModelSerializer):
"""Certificate Serializer class"""
url = serializers.HyperlinkedIdentityField(
view_name="plugins-api:netbox_cert_plugin-api:certificate-detail"
)
class Meta:
model = Certificate
fields = (
"id",
"url",
"cn",
"alt_name",
"ca",
"expiration_time",
"cert_created_at",
"cert_expired_at",
"state",
"custom_fields",
"created",
"last_updated",
"tags",
)
"""API URLs definition"""
from netbox.api.routers import NetBoxRouter
from . import views
APP_NAME = 'netbox_cert_plugin'
router = NetBoxRouter()
router.register('certificate', views.CertificateViewSet)
urlpatterns = router.urls
"""API views definitions"""
from netbox.api.viewsets import NetBoxModelViewSet
from .. import filtersets, models
from .serializers import CertificateSerializer
class CertificateViewSet(NetBoxModelViewSet):
"""Mapping view set class"""
queryset = models.Certificate.objects.all()
serializer_class = CertificateSerializer
filterset_class = filtersets.CertificateFilterSet
http_method_names = ["get", "post", "patch", "delete"]
"""Filtersets definitions"""
from django_filters import filters
from netbox.filtersets import NetBoxModelFilterSet
from django.db.models import Q
from .models import Certificate
class CertificateFilterSet(NetBoxModelFilterSet):
"""Certificate filterset definition class"""
alt_name = filters.CharFilter(lookup_expr="icontains")
class Meta:
model = Certificate
fields = (
"id",
"cn",
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
)
# pylint: disable=W0613
def search(self, queryset, name, value):
"""override"""
if not value.strip():
return queryset
return queryset.filter(Q(cn__icontains=value) | Q(alt_name__icontains=value))
"""Forms definitions"""
from django import forms
from netbox.forms import (
NetBoxModelForm,
NetBoxModelImportForm,
NetBoxModelFilterSetForm,
)
from utilities.forms.widgets import DatePicker
from utilities.forms.fields import TagFilterField
from .models import Certificate, ExpirationTimeChoices, CaChoices, StateChoices
class CertificateForm(NetBoxModelForm):
"""Certificate form definition class"""
class Meta:
model = Certificate
fields = (
"cn",
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
"tags",
)
help_texts = {"CN": "Unique Common Name", "CA": "Valid Certificate Authority"}
labels = {
"cn": "CN",
"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",
}
widgets = {
"cert_created_at": DatePicker(),
"cert_expired_at": DatePicker(),
}
class CertificateFilterForm(NetBoxModelFilterSetForm):
"""Certificate filter form definition class"""
model = Certificate
cn = forms.CharField(
label="Common Name", max_length=256, min_length=1, required=False
)
alt_name = forms.CharField(
label="Alt name", max_length=256, min_length=1, required=False
)
ca = forms.MultipleChoiceField(
label="Certificate Authority", choices=CaChoices, required=False
)
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
)
cert_expired_at = forms.DateField(
label="Effective certificate's expirations date",
required=False,
widget=DatePicker,
)
tag = TagFilterField(model)
class CertificateImportForm(NetBoxModelImportForm):
"""Certificate importation form definition class"""
class Meta:
model = Certificate
fields = (
"cn",
"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'",
"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
import taggit.managers
import utilities.json
class Migration(migrations.Migration):
"""Migration Class"""
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Certificate",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
("created", models.DateTimeField(auto_now_add=True, null=True)),
("last_updated", models.DateTimeField(auto_now=True, null=True)),
(
"custom_field_data",
models.JSONField(
blank=True,
default=dict,
encoder=utilities.json.CustomFieldJSONEncoder,
),
),
("cn", models.CharField(blank=False, unique=True, max_length=256)),
("alt_name", models.CharField(blank=True, null=True, max_length=256)),
("ca", models.CharField(blank=False, max_length=32)),
("expiration_time", models.CharField(blank=False, max_length=2)),
("cert_created_at", models.DateField(blank=True, null=True)),
("cert_expired_at", models.DateField(blank=True, null=True)),
(
"tags",
taggit.managers.TaggableManager(
through="extras.TaggedItem", to="extras.Tag"
),
),
],
options={
"ordering": ("source"),
},
),
]
"""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=string_to_array(t.alt_name, ',')",
reverse_sql="UPDATE netbox_cert_plugin_certificate AS t set alt_name=array_to_string(new_alt_name, ',')",
),
migrations.RemoveField(model_name="Certificate", name="alt_name"),
migrations.RenameField(model_name="Certificate", new_name="alt_name", old_name="new_alt_name")
]
"""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),
),
]
"""Migration File"""
# pylint: disable=C0103
from django.db import migrations, models
class Migration(migrations.Migration):
"""Migration Class"""
dependencies = [
(
"netbox_cert_plugin",
"0003_alter_certificate_options_certificate_state_and_more",
),
]
operations = [
migrations.AlterField(
model_name="certificate",
name="state",
field=models.CharField(default="to_be_created", max_length=32),
),
]
"""Models definitions"""
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
class CaChoices(ChoiceSet):
"""CA choices definition class"""
key = "Certificate.ca"
DEFAULT_VALUE = "letsencrypt"
CHOICES = [
("letsencrypt", "Let's Encrypt", "yellow"),
("commissign", "CommisSign", "red"),
("globalsign", "GlobalSign", "blue"),
]
class ExpirationTimeChoices(ChoiceSet):
"""Expiration time choices definition class"""
key = "Certificate.expiration_time"
DEFAULT_VALUE = "1m"
CHOICES = [
("1m", "1 month", "blue"),
("3m", "3 months", "blue"),
("6m", "6 months", "blue"),
("1y", "1 year", "blue"),
("3y", "3 year", "blue"),
]
class StateChoices(ChoiceSet):
"""CA choices definition class"""
key = "Certificate.state"
DEFAULT_VALUE = "to_be_created"
CHOICES = [
("to_be_created", "To be created", "yellow"),
("creating", "Creating", "green"),
("errored", "Errored", "red"),
("valid", "Valid", "blue"),
("to_be_renewed", "To be renewed", "black"),
("renewing", "Renewing", "green"),
]
class Certificate(NetBoxModel):
"""Certificate definition class"""
cn = models.CharField(
max_length=256,
blank=False,
verbose_name="Common Name",
unique=True,
help_text="Unique Common Name",
)
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,
choices=CaChoices,
default=CaChoices.DEFAULT_VALUE,
blank=False,
verbose_name="Certificate Authority",
)
expiration_time = models.CharField(
max_length=2,
choices=ExpirationTimeChoices,
default=ExpirationTimeChoices.DEFAULT_VALUE,
blank=False,
verbose_name="Expiration time needed",
)
cert_created_at = models.DateField(
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"
)
state = models.CharField(
max_length=32,
choices=StateChoices,
default=StateChoices.DEFAULT_VALUE,
blank=False,
verbose_name="Certificate State",
)
class Meta:
ordering = ["cn"]
def __str__(self):
return f"{self.cn}"
def get_absolute_url(self):
"""override"""
return reverse("plugins:netbox_cert_plugin:certificate", args=[self.pk])
"""Navigation Menu definitions"""
from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu
from utilities.choices import ButtonColorChoices
cert_buttons = [
PluginMenuButton(
link="plugins:netbox_cert_plugin:certificate_add",
title="Add",
icon_class="mdi mdi-plus-thick",
color=ButtonColorChoices.GREEN,
),
PluginMenuButton(
link="plugins:netbox_cert_plugin:certificate_import",
title="Import",
icon_class="mdi mdi-upload",
color=ButtonColorChoices.CYAN,
),
]
certItem = [
PluginMenuItem(
link="plugins:netbox_cert_plugin:certificate_list",
link_text="Certificates",
buttons=cert_buttons,
permissions=["netbox_cert_plugin.view_certificate"],
),
]
menu = PluginMenu(
label="Certificates",
groups=(("CERTIFICATE", certItem),),
icon_class="mdi mdi-certificate",
)
"""Search definitions"""
from netbox.search import SearchIndex, register_search
from .models import Certificate
@register_search
class CertificateIndex(SearchIndex):
"""Certificate search definition class"""
model = Certificate
fields = (("cn", 256), ("alt_name", 256), ("ca", 32), ("state", 32))
"""Tables definitions"""
import django_tables2 as tables
from netbox.tables import NetBoxTable, columns
from .models import Certificate
class CertificateTable(NetBoxTable):
"""Certificate Table definition class"""
cn = tables.Column(linkify=True)
tags = columns.TagColumn()
class Meta(NetBoxTable.Meta):
model = Certificate
fields = (
"pk",
"id",
"cn",
"alt_name",
"ca",
"state",
"expiration_time",
"cert_created_at",
"cert_expired_at",
"tags",
)
default_columns = (
"cn",
"alt_name",
"ca",
"expiration_time",
"state",
"cert_created_at",
"cert_expired_at",
)
{% extends 'generic/object.html' %}
{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">CERTIFICATE</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Common Name</th>
<td>{{ object.cn }}</td>
</tr>
<tr>
<th scope="row">Certificate Authority</th>
<td>{{ object.get_ca_display }}</td>
</tr>
<tr>
<th scope="row">Effective certificate's expiration date</th>
<td>{{ object.cert_expired_at|placeholder }}</td>
</tr>
</table>
</div>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
</div>
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">Details</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Alt name</th>
<td>{{ object.alt_name|placeholder }}</td>
</tr>
<tr>
<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>
</tr>
</table>
</div>
</div>
</div>
</div>
{% endblock content %}
"""URL definitions"""
from django.urls import path
from netbox_cert_plugin import views, models
from netbox.views.generic import ObjectChangeLogView, ObjectJournalView
urlpatterns = (
# Cert
path('certificates/', views.CertificateListView.as_view(), name='certificate_list'),
path('certificates/add/', views.CertificateEditView.as_view(), name='certificate_add'),
path('certificates/import/', views.CertificateBulkImportView.as_view(), name='certificate_import'),
path('certificates/delete/', views.CertificateBulkDeleteView.as_view(), name='certificate_bulk_delete'),
path('certificates/<int:pk>/', views.CertificateView.as_view(), name='certificate'),
path('certificates/<int:pk>/edit/', views.CertificateEditView.as_view(), name='certificate_edit'),
path('certificates/<int:pk>/delete/', views.CertificateDeleteView.as_view(), name='certificate_delete'),
path('certificates/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='certificate_changelog', kwargs={
'model': models.Certificate
}),
path('certificates/<int:pk>/journal/', ObjectJournalView.as_view(), name='certificate_journal', kwargs={
'model': models.Certificate
}),
)
"""Model views definitions"""
from netbox.views import generic
from netbox_cert_plugin import models, tables, filtersets, forms
from django.utils.translation import gettext as _
class CertificateView(generic.ObjectView):
"""Certificate view definition"""
queryset = models.Certificate.objects.all()
class CertificateListView(generic.ObjectListView):
"""Certificate list view definition"""
queryset = models.Certificate.objects.all()
table = tables.CertificateTable
filterset = filtersets.CertificateFilterSet
filterset_form = forms.CertificateFilterForm
class CertificateEditView(generic.ObjectEditView):
"""Certificate edition view definition"""
queryset = models.Certificate.objects.all()
form = forms.CertificateForm
class CertificateBulkImportView(generic.BulkImportView):
"""Certificate bulk import view definition"""
queryset = models.Certificate.objects.all()
model_form = forms.CertificateImportForm
class CertificateDeleteView(generic.ObjectDeleteView):
"""Certificate delete view definition"""
queryset = models.Certificate.objects.all()
class CertificateBulkDeleteView(generic.BulkDeleteView):
"""Certificate bulk delete view definition"""
queryset = models.Certificate.objects.all()
filterset = filtersets.CertificateFilterSet
table = tables.CertificateTable
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment