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

Skip to content
Snippets Groups Projects
Commit 9f03a592 authored by Laurent VENIER's avatar Laurent VENIER :speech_balloon:
Browse files

Merge branch 'certificate_plugin' into 'main'

Add new plugin for certificate management

See merge request !36
parents 8f8353d6 cc004a72
No related branches found
No related tags found
1 merge request!36Add new plugin for certificate management
Pipeline #90422 passed
Showing
with 485 additions and 9 deletions
......@@ -17,34 +17,37 @@ include:
lint-job:
stage: lint
before_script:
- 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 pylint
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"
after_script:
- deactivate
- rm -rf "$CI_PROJECT_DIR/plugins/netbox-rps-plugin/venv"
- rm -rf "$CI_PROJECT_DIR/plugins/venv"
build-job: # This job runs in the build stage, which runs first.
stage: build
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
......
......@@ -35,4 +35,4 @@
- name: Pause for 10 seconds
ansible.builtin.pause:
seconds: 10
seconds: 60
......@@ -15,6 +15,7 @@ services:
syslog-format: "rfc5424"
tag: "netbox"
netbox-worker:
image: "code.europa.eu:4567/digit-c4/netbox-plugins:${TAG}"
env_file: env/netbox.env
logging:
driver: "syslog"
......@@ -23,6 +24,7 @@ services:
syslog-format: "rfc5424"
tag: "netbox-worker"
netbox-housekeeping:
image: "code.europa.eu:4567/digit-c4/netbox-plugins:${TAG}"
env_file: env/netbox.env
logging:
driver: "syslog"
......
PLUGINS = ['netbox_dns', 'netbox_rps_plugin', 'netbox_prometheus_sd']
PLUGINS = ['netbox_dns', 'netbox_rps_plugin', 'netbox_cert_plugin', 'netbox_prometheus_sd']
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.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 %}
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