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

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

Add new plugin for certificate management

parent 8f8353d6
No related branches found
No related tags found
1 merge request!36Add new plugin for certificate management
Pipeline #90117 failed
Showing
with 571 additions and 4 deletions
......@@ -31,20 +31,22 @@ build-job: # This job runs in the build stage, which runs first.
script:
- ansible-playbook ansible/build.yml
run-test:
run-test-job:
stage: test
before_script:
- chmod 600 $ANSIBLE_PRIVATE_KEY_FILE
- env ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_PRIVATE_KEY_FILE="$ANSIBLE_PRIVATE_KEY_FILE" ansible-playbook -i "$TESTING_HOSTS" -u debian ansible/deploy_on_test.yml
- python3 -m venv "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/venv"
- source "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/venv/bin/activate"
- 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"
script:
- env HOST="$HOST" PORT="$PORT" API_KEY="$API_KEY" python3 -m unittest discover -b "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/tests/"
- env HOST="$HOST" PORT="$PORT" API_KEY="$API_KEY" python3 -m unittest discover -b "$CI_PROJECT_DIR/plugins/netbox-cert-plugin/tests/"
after_script:
- env ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_PRIVATE_KEY_FILE="$ANSIBLE_PRIVATE_KEY_FILE" ansible-playbook -i "$TESTING_HOSTS" -u debian ansible/halt_test.yml
- deactivate
- rm -rf "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/venv"
- rm -rf "$CI_PROJECT_DIR/plugins/venv"
deliver-latest-job:
stage: deliver
......
# 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.1"
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
from ..models import Certificate
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",
"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 netbox.filtersets import NetBoxModelFilterSet
from django.db.models import Q
from .models import Certificate
class CertificateFilterSet(NetBoxModelFilterSet):
"""Certificate filterset definition class"""
class Meta:
model = Certificate
fields = (
"id",
"cn",
"alt_name",
"ca",
"expiration_time",
"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
class CertificateForm(NetBoxModelForm):
"""Certificate form definition class"""
class Meta:
model = Certificate
fields = (
"cn",
"alt_name",
"ca",
"expiration_time",
"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",
"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
)
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",
"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'",
}
"""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"),
},
),
]
"""Models definitions"""
from django.db import models
from django.urls import reverse
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"),
("comisign", "Comisign", "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 Certificate(NetBoxModel):
"""Certificate definition class"""
cn = models.CharField(
max_length=256,
blank=False,
verbose_name="Common Name",
unique=True,
)
alt_name = models.CharField(
max_length=256,
blank=True,
null=True,
verbose_name="Alt Name",
)
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=CaChoices.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"
)
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),
)
"""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",
"expiration_time",
"cert_created_at",
"cert_expired_at",
"tags"
)
default_columns = (
"cn",
"alt_name",
"ca",
"expiration_time",
"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">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
from setuptools import find_packages, setup
setup(
name='netbox_cert_plugin',
version='0.0.1',
description='A Netbox plugin to manage certificates',
install_requires=[],
packages=find_packages(),
include_package_data=True,
zip_safe=False
)
# Testing the plugin
## End to end
Prepare a python environment to execute the E2E tests suite
```shell
python3 -m venv venv
source venv/bin/activate
pip install -r tests/requirements.e2e.txt
python -m unittest discover tests/e2e
```
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