netbox-plugin-dns 1.2.6__py3-none-any.whl → 1.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of netbox-plugin-dns might be problematic. Click here for more details.
- netbox_dns/__init__.py +16 -8
- netbox_dns/api/field_serializers.py +25 -0
- netbox_dns/api/nested_serializers.py +19 -1
- netbox_dns/api/serializers.py +3 -0
- netbox_dns/api/serializers_/dnssec_key_template.py +59 -0
- netbox_dns/api/serializers_/dnssec_policy.py +113 -0
- netbox_dns/api/serializers_/record.py +2 -0
- netbox_dns/api/serializers_/record_template.py +2 -0
- netbox_dns/api/serializers_/zone.py +20 -1
- netbox_dns/api/serializers_/zone_template.py +13 -4
- netbox_dns/api/urls.py +4 -0
- netbox_dns/api/views.py +18 -0
- netbox_dns/choices/__init__.py +2 -0
- netbox_dns/choices/dnssec_key_template.py +63 -0
- netbox_dns/choices/dnssec_policy.py +40 -0
- netbox_dns/choices/record.py +2 -25
- netbox_dns/choices/utilities.py +26 -0
- netbox_dns/choices/zone.py +96 -1
- netbox_dns/fields/__init__.py +1 -0
- netbox_dns/fields/choice_array.py +33 -0
- netbox_dns/fields/timeperiod.py +15 -13
- netbox_dns/filtersets/__init__.py +3 -0
- netbox_dns/filtersets/dnssec_key_template.py +51 -0
- netbox_dns/filtersets/dnssec_policy.py +97 -0
- netbox_dns/filtersets/zone.py +30 -6
- netbox_dns/filtersets/zone_template.py +13 -2
- netbox_dns/forms/__init__.py +2 -0
- netbox_dns/forms/dnssec_key_template.py +189 -0
- netbox_dns/forms/dnssec_policy.py +593 -0
- netbox_dns/forms/nameserver.py +2 -0
- netbox_dns/forms/record_template.py +1 -0
- netbox_dns/forms/zone.py +120 -9
- netbox_dns/forms/zone_template.py +38 -0
- netbox_dns/graphql/__init__.py +7 -3
- netbox_dns/graphql/filters.py +16 -0
- netbox_dns/graphql/schema.py +20 -0
- netbox_dns/graphql/types.py +67 -3
- netbox_dns/locale/de/LC_MESSAGES/django.mo +0 -0
- netbox_dns/locale/fr/LC_MESSAGES/django.mo +0 -0
- netbox_dns/migrations/0015_dnssec.py +168 -0
- netbox_dns/migrations/0016_dnssec_policy_status.py +18 -0
- netbox_dns/migrations/0017_dnssec_policy_zone_zone_template.py +41 -0
- netbox_dns/migrations/0018_zone_domain_status_zone_expiration_date.py +23 -0
- netbox_dns/models/__init__.py +2 -0
- netbox_dns/models/dnssec_key_template.py +114 -0
- netbox_dns/models/dnssec_policy.py +203 -0
- netbox_dns/models/record.py +1 -1
- netbox_dns/models/zone.py +74 -19
- netbox_dns/models/zone_template.py +17 -7
- netbox_dns/navigation.py +49 -0
- netbox_dns/signals/dnssec.py +32 -0
- netbox_dns/tables/__init__.py +2 -0
- netbox_dns/tables/dnssec_key_template.py +48 -0
- netbox_dns/tables/dnssec_policy.py +131 -0
- netbox_dns/tables/zone.py +23 -2
- netbox_dns/tables/zone_template.py +4 -0
- netbox_dns/template_content.py +2 -1
- netbox_dns/templates/netbox_dns/dnsseckeytemplate.html +70 -0
- netbox_dns/templates/netbox_dns/dnssecpolicy.html +155 -0
- netbox_dns/templates/netbox_dns/zone/registration.html +19 -0
- netbox_dns/templates/netbox_dns/zone.html +16 -0
- netbox_dns/templates/netbox_dns/zonetemplate/child.html +46 -0
- netbox_dns/templates/netbox_dns/zonetemplate.html +12 -0
- netbox_dns/urls.py +23 -0
- netbox_dns/utilities/conversions.py +13 -0
- netbox_dns/validators/__init__.py +1 -0
- netbox_dns/validators/dnssec.py +148 -0
- netbox_dns/views/__init__.py +2 -0
- netbox_dns/views/dnssec_key_template.py +87 -0
- netbox_dns/views/dnssec_policy.py +155 -0
- netbox_dns/views/zone.py +11 -1
- {netbox_plugin_dns-1.2.6.dist-info → netbox_plugin_dns-1.2.7.dist-info}/METADATA +3 -2
- {netbox_plugin_dns-1.2.6.dist-info → netbox_plugin_dns-1.2.7.dist-info}/RECORD +76 -50
- {netbox_plugin_dns-1.2.6.dist-info → netbox_plugin_dns-1.2.7.dist-info}/WHEEL +1 -1
- {netbox_plugin_dns-1.2.6.dist-info → netbox_plugin_dns-1.2.7.dist-info/licenses}/LICENSE +0 -0
- {netbox_plugin_dns-1.2.6.dist-info → netbox_plugin_dns-1.2.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{% extends 'generic/object.html' %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block content %}
|
|
5
|
+
<div class="row">
|
|
6
|
+
<div class="col col-md-6">
|
|
7
|
+
<div class="card">
|
|
8
|
+
<h5 class="card-header">{% trans "DNSSEC Policy" %}</h5>
|
|
9
|
+
<table class="table table-hover attr-table">
|
|
10
|
+
{% if object.description %}
|
|
11
|
+
<tr>
|
|
12
|
+
<th scope="row">{% trans "Description" %}</th>
|
|
13
|
+
<td style="word-break:break-all;">{{ object.description }}</td>
|
|
14
|
+
</tr>
|
|
15
|
+
{% endif %}
|
|
16
|
+
<tr>
|
|
17
|
+
<th scope="row">{% trans "Status" %}</th>
|
|
18
|
+
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
|
19
|
+
</tr>
|
|
20
|
+
<tr>
|
|
21
|
+
<th scope="row">{% trans "Key Templates" %}</th>
|
|
22
|
+
<td>
|
|
23
|
+
<table>
|
|
24
|
+
{% for key_template in object.key_templates.all %}
|
|
25
|
+
<tr><td>{{ key_template|linkify }}</td></tr>
|
|
26
|
+
{% for template_id, errors in key_template_errors.items %}
|
|
27
|
+
{% if template_id == key_template.pk %}
|
|
28
|
+
{% for error in errors %}
|
|
29
|
+
<tr><td class="text-danger">{{ error }}</td></tr>
|
|
30
|
+
{% endfor %}
|
|
31
|
+
{% endif %}
|
|
32
|
+
{% endfor %}
|
|
33
|
+
{% endfor %}
|
|
34
|
+
</table>
|
|
35
|
+
</td>
|
|
36
|
+
</tr>
|
|
37
|
+
{% if policy_warning %}
|
|
38
|
+
<tr><td class="text-danger">{{ policy_warning }}</td></tr>
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% if object.tenant %}
|
|
41
|
+
<tr>
|
|
42
|
+
<th scope="row">{% trans "Tenant" %}</th>
|
|
43
|
+
<td>
|
|
44
|
+
{% if object.tenant.group %}
|
|
45
|
+
{{ object.tenant.group|linkify }} /
|
|
46
|
+
{% endif %}
|
|
47
|
+
{{ object.tenant|linkify|placeholder }}
|
|
48
|
+
</td>
|
|
49
|
+
</tr>
|
|
50
|
+
{% endif %}
|
|
51
|
+
</table>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="card">
|
|
54
|
+
<h5 class="card-header">{% trans "Timing" %}</h5>
|
|
55
|
+
<table class="table table-hover attr-table">
|
|
56
|
+
<tr>
|
|
57
|
+
<th scope="row">{% trans "DNSKEY TTL" %}</th>
|
|
58
|
+
<td>{{ object.dnskey_ttl|placeholder }}</td>
|
|
59
|
+
</tr>
|
|
60
|
+
<tr>
|
|
61
|
+
<th scope="row">{% trans "Publish Safety" %}</th>
|
|
62
|
+
<td>{{ object.publish_safety|placeholder }}</td>
|
|
63
|
+
</tr>
|
|
64
|
+
<tr>
|
|
65
|
+
<th scope="row">{% trans "Purge Keys" %}</th>
|
|
66
|
+
<td>{{ object.purge_keys|placeholder }}</td>
|
|
67
|
+
</tr>
|
|
68
|
+
<tr>
|
|
69
|
+
<th scope="row">{% trans "Retire Safety" %}</th>
|
|
70
|
+
<td>{{ object.retire_safety|placeholder }}</td>
|
|
71
|
+
</tr>
|
|
72
|
+
<tr>
|
|
73
|
+
<th scope="row">{% trans "Signatures Jitter" %}</th>
|
|
74
|
+
<td>{{ object.signatures_jitter|placeholder }}</td>
|
|
75
|
+
</tr>
|
|
76
|
+
<tr>
|
|
77
|
+
<th scope="row">{% trans "Signatures Refresh" %}</th>
|
|
78
|
+
<td>{{ object.signatures_refresh|placeholder }}</td>
|
|
79
|
+
</tr>
|
|
80
|
+
<tr>
|
|
81
|
+
<th scope="row">{% trans "Signatures Validity" %}</th>
|
|
82
|
+
<td>{{ object.signatures_validity|placeholder }}</td>
|
|
83
|
+
</tr>
|
|
84
|
+
<tr>
|
|
85
|
+
<th scope="row">{% trans "Signatures Validity (DNSKEY)" %}</th>
|
|
86
|
+
<td>{{ object.signatures_validity_dnskey|placeholder }}</td>
|
|
87
|
+
</tr>
|
|
88
|
+
<tr>
|
|
89
|
+
<th scope="row">{% trans "Max Zone TTL" %}</th>
|
|
90
|
+
<td>{{ object.max_zone_ttl|placeholder }}</td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr>
|
|
93
|
+
<th scope="row">{% trans "Zone Propagation Delay" %}</th>
|
|
94
|
+
<td>{{ object.zone_propagation_delay|placeholder }}</td>
|
|
95
|
+
</tr>
|
|
96
|
+
</table>
|
|
97
|
+
</div>
|
|
98
|
+
{% include 'inc/panels/custom_fields.html' %}
|
|
99
|
+
</div>
|
|
100
|
+
<div class="col col-md-6">
|
|
101
|
+
{% include 'inc/panels/tags.html' %}
|
|
102
|
+
<div class="card">
|
|
103
|
+
<h5 class="card-header">{% trans "Parent Delegation" %}</h5>
|
|
104
|
+
<table class="table table-hover attr-table">
|
|
105
|
+
<tr>
|
|
106
|
+
<th scope="row">{% trans "Create CDNSKEY" %}</th>
|
|
107
|
+
<td>{% checkmark object.create_cdnskey %}</td>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<th scope="row">{% trans "CDS Digest Types" %}</th>
|
|
111
|
+
<td>{% if object.cds_digest_types %}{% for digest_type in object.cds_digest_types %}{% badge digest_type %}{% endfor %}{% else %}{{ object_cds_digest_types|placeholder }}{% endif %}</td>
|
|
112
|
+
</tr>
|
|
113
|
+
<tr>
|
|
114
|
+
<th scope="row">{% trans "Parent DS TTL" %}</th>
|
|
115
|
+
<td>{{ object.parent_ds_ttl|placeholder }}</td>
|
|
116
|
+
</tr>
|
|
117
|
+
<tr>
|
|
118
|
+
<th scope="row">{% trans "Parent Propagation Delay" %}</th>
|
|
119
|
+
<td>{{ object.parent_propagation_delay|placeholder }}</td>
|
|
120
|
+
</tr>
|
|
121
|
+
</table>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card">
|
|
124
|
+
<h5 class="card-header">{% trans "Proof of Non-Existence" %}</h5>
|
|
125
|
+
<table class="table table-hover attr-table">
|
|
126
|
+
<tr>
|
|
127
|
+
<th scope="row">{% trans "Use NSEC3" %}</th>
|
|
128
|
+
<td>{% checkmark object.use_nsec3 %}</td>
|
|
129
|
+
</tr>
|
|
130
|
+
{% if object.use_nsec3 %}
|
|
131
|
+
<tr>
|
|
132
|
+
<th scope="row">{% trans "NSEC3 Iterations" %}</th>
|
|
133
|
+
<td>{{ object.nsec3_iterations|placeholder }}</td>
|
|
134
|
+
</tr>
|
|
135
|
+
<tr>
|
|
136
|
+
<th scope="row">{% trans "NSEC3 Opt Out" %}</th>
|
|
137
|
+
<td>{% checkmark object.nsec3_opt_out %}</td>
|
|
138
|
+
</tr>
|
|
139
|
+
<tr>
|
|
140
|
+
<th scope="row">{% trans "NSEC3 Salt Size" %}</th>
|
|
141
|
+
<td>{{ object.nsec3_salt_size|placeholder }}</td>
|
|
142
|
+
</tr>
|
|
143
|
+
{% if object.nsec3_iterations or object.nsec3_opt_out or object.nsec3_salt_size %}
|
|
144
|
+
<tr>
|
|
145
|
+
<th class="text-warning">
|
|
146
|
+
{% blocktrans with link='<a href="https://datatracker.ietf.org/doc/html/rfc9276.html#section-3.1">RFC 9276, Section 3.1</a>' %}Using NSEC3 options is not recommended (see {{ link }}){% endblocktrans %}
|
|
147
|
+
</th>
|
|
148
|
+
</tr>
|
|
149
|
+
{% endif %}
|
|
150
|
+
{% endif %}
|
|
151
|
+
</table>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
{% endblock %}
|
|
@@ -14,6 +14,25 @@
|
|
|
14
14
|
<th scope="row">{% trans "Registry Domain ID" %}</th>
|
|
15
15
|
<td>{{ object.registry_domain_id|placeholder }}</td>
|
|
16
16
|
</tr>
|
|
17
|
+
<tr>
|
|
18
|
+
<th scope="row">{% trans "Expiration Date" %}</th>
|
|
19
|
+
<td>{{ object.expiration_date|placeholder }}</td>
|
|
20
|
+
</tr>
|
|
21
|
+
{% if expiration_warning %}
|
|
22
|
+
<tr>
|
|
23
|
+
<th class="text-warning" scope="row">{% trans "Warning" %}</th>
|
|
24
|
+
<td class="text-warning">{{ expiration_warning }}</td>
|
|
25
|
+
</tr>
|
|
26
|
+
{% elif expiration_error %}
|
|
27
|
+
<tr>
|
|
28
|
+
<th class="text-danger" scope="row">{% trans "Error" %}</th>
|
|
29
|
+
<td class="text-danger">{{ expiration_error }}</td>
|
|
30
|
+
</tr>
|
|
31
|
+
{% endif %}
|
|
32
|
+
<tr>
|
|
33
|
+
<th scope="row">{% trans "Domain Status" %}</th>
|
|
34
|
+
<td>{% badge object.get_domain_status_display bg_color=object.get_domain_status_color %}</td>
|
|
35
|
+
</tr>
|
|
17
36
|
<tr>
|
|
18
37
|
<th scope="row">{% trans "Registrant" %}</th>
|
|
19
38
|
<td>{{ object.registrant|linkify|placeholder }}</td>
|
|
@@ -96,6 +96,22 @@
|
|
|
96
96
|
</table>
|
|
97
97
|
</div>
|
|
98
98
|
|
|
99
|
+
{% if object.dnssec_policy %}
|
|
100
|
+
<div class="card">
|
|
101
|
+
<h5 class="card-header">{% trans "DNSSEC" %}</h5>
|
|
102
|
+
<table class="table table-hover attr-table">
|
|
103
|
+
<tr>
|
|
104
|
+
<th scope="row">{% trans "Policy" %}</th>
|
|
105
|
+
<td>{{ object.dnssec_policy|linkify }}</td>
|
|
106
|
+
</tr>
|
|
107
|
+
<tr>
|
|
108
|
+
<th scope="row">{% trans "Use Inline Signing" %}</th>
|
|
109
|
+
<td>{% checkmark object.inline_signing %}</td>
|
|
110
|
+
</tr>
|
|
111
|
+
</table>
|
|
112
|
+
</div>
|
|
113
|
+
{% endif %}
|
|
114
|
+
|
|
99
115
|
{% include 'inc/panels/tags.html' %}
|
|
100
116
|
{% include 'inc/panels/custom_fields.html' %}
|
|
101
117
|
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% extends 'generic/object.html' %}
|
|
2
|
+
{% load helpers %}
|
|
3
|
+
{% load render_table from django_tables2 %}
|
|
4
|
+
{% load perms %}
|
|
5
|
+
{% load i18n %}
|
|
6
|
+
|
|
7
|
+
{% block content %}
|
|
8
|
+
{% include 'inc/table_controls_htmx.html' with table_modal="ZoneTemplateTable_config" %}
|
|
9
|
+
|
|
10
|
+
<form method="post">
|
|
11
|
+
{% csrf_token %}
|
|
12
|
+
<input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
|
|
13
|
+
|
|
14
|
+
<div class="card">
|
|
15
|
+
<div class="htmx-container table-responsive" id="object_list">
|
|
16
|
+
{% include 'htmx/table.html' %}
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
{% if perms.netbox_dns.change_zonetemplate or perms.netbox_dns.delete_zonetemplate %}
|
|
21
|
+
{% with bulk_edit_url="plugins:netbox_dns:zonetemplate_bulk_edit" bulk_delete_url="plugins:netbox_dns:zonetemplate_bulk_delete" %}
|
|
22
|
+
<div class="noprint bulk-buttons">
|
|
23
|
+
<div class="bulk-button-group">
|
|
24
|
+
{% block bulk_buttons %}{% endblock %}
|
|
25
|
+
{% if bulk_edit_url and perms.netbox_dns.change_zonetemplate %}
|
|
26
|
+
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning">
|
|
27
|
+
<i class="mdi mdi-pencil" aria-hidden="true"></i> {% trans "Edit Selected" %}
|
|
28
|
+
</button>
|
|
29
|
+
{% endif %}
|
|
30
|
+
{% if bulk_delete_url and perms.netbox_dns.delete_zonetemplate %}
|
|
31
|
+
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger">
|
|
32
|
+
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> {% trans "Delete Selected" %}
|
|
33
|
+
</button>
|
|
34
|
+
{% endif %}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
{% endwith %}
|
|
38
|
+
{% endif %}
|
|
39
|
+
</form>
|
|
40
|
+
|
|
41
|
+
{% endblock %}
|
|
42
|
+
|
|
43
|
+
{% block modals %}
|
|
44
|
+
{{ block.super }}
|
|
45
|
+
{% table_config_form table %}
|
|
46
|
+
{% endblock modals %}
|
|
@@ -53,6 +53,18 @@
|
|
|
53
53
|
</table>
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
|
+
{% if object.dnssec_policy %}
|
|
57
|
+
<div class="card">
|
|
58
|
+
<h5 class="card-header">{% trans "DNSSEC" %}</h5>
|
|
59
|
+
<table class="table table-hover attr-table">
|
|
60
|
+
<tr>
|
|
61
|
+
<th scope="row">{% trans "Policy" %}</th>
|
|
62
|
+
<td>{{ object.dnssec_policy|linkify }}</td>
|
|
63
|
+
</tr>
|
|
64
|
+
</table>
|
|
65
|
+
</div>
|
|
66
|
+
{% endif %}
|
|
67
|
+
|
|
56
68
|
{% include 'inc/panels/tags.html' %}
|
|
57
69
|
{% include 'inc/panels/custom_fields.html' %}
|
|
58
70
|
</div>
|
netbox_dns/urls.py
CHANGED
|
@@ -2,6 +2,13 @@ from django.urls import include, path
|
|
|
2
2
|
|
|
3
3
|
from utilities.urls import get_model_urls
|
|
4
4
|
|
|
5
|
+
# +
|
|
6
|
+
# Import views so the register_model_view is run. This is required for the
|
|
7
|
+
# URLs to be set up properly with get_model_urls().
|
|
8
|
+
# -
|
|
9
|
+
from .views import * # noqa: F401
|
|
10
|
+
|
|
11
|
+
|
|
5
12
|
app_name = "netbox_dns"
|
|
6
13
|
|
|
7
14
|
urlpatterns = (
|
|
@@ -69,4 +76,20 @@ urlpatterns = (
|
|
|
69
76
|
"zones/<int:pk>/",
|
|
70
77
|
include(get_model_urls("netbox_dns", "zone")),
|
|
71
78
|
),
|
|
79
|
+
path(
|
|
80
|
+
"dnsseckeytemplates/",
|
|
81
|
+
include(get_model_urls("netbox_dns", "dnsseckeytemplate", detail=False)),
|
|
82
|
+
),
|
|
83
|
+
path(
|
|
84
|
+
"dnsseckeytemplates/<int:pk>/",
|
|
85
|
+
include(get_model_urls("netbox_dns", "dnsseckeytemplate")),
|
|
86
|
+
),
|
|
87
|
+
path(
|
|
88
|
+
"dnssecpolicies/",
|
|
89
|
+
include(get_model_urls("netbox_dns", "dnssecpolicy", detail=False)),
|
|
90
|
+
),
|
|
91
|
+
path(
|
|
92
|
+
"dnssecpolicies/<int:pk>/",
|
|
93
|
+
include(get_model_urls("netbox_dns", "dnssecpolicy")),
|
|
94
|
+
),
|
|
72
95
|
)
|
|
@@ -4,6 +4,8 @@ from dns import name as dns_name
|
|
|
4
4
|
from dns.exception import DNSException
|
|
5
5
|
from netaddr import IPNetwork, AddrFormatError
|
|
6
6
|
|
|
7
|
+
from django.utils.dateparse import parse_duration
|
|
8
|
+
|
|
7
9
|
from netbox.plugins.utils import get_plugin_config
|
|
8
10
|
|
|
9
11
|
|
|
@@ -15,6 +17,7 @@ __all__ = (
|
|
|
15
17
|
"normalize_name",
|
|
16
18
|
"network_to_reverse",
|
|
17
19
|
"regex_from_list",
|
|
20
|
+
"iso8601_to_int",
|
|
18
21
|
)
|
|
19
22
|
|
|
20
23
|
|
|
@@ -111,3 +114,13 @@ def network_to_reverse(network):
|
|
|
111
114
|
|
|
112
115
|
def regex_from_list(names):
|
|
113
116
|
return f"^({'|'.join(re.escape(name) for name in names)})$"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def iso8601_to_int(value):
|
|
120
|
+
try:
|
|
121
|
+
return int(value)
|
|
122
|
+
except ValueError:
|
|
123
|
+
duration = parse_duration(value)
|
|
124
|
+
if duration is None:
|
|
125
|
+
raise TypeError
|
|
126
|
+
return int(duration.total_seconds())
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from django.core.exceptions import ValidationError
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
from netbox_dns.choices import (
|
|
5
|
+
DNSSECKeyTemplateTypeChoices,
|
|
6
|
+
DNSSECKeyTemplateAlgorithmChoices,
|
|
7
|
+
DNSSECKeyTemplateKeySizeChoices,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"validate_key_template",
|
|
12
|
+
"validate_key_template_assignment",
|
|
13
|
+
"validate_key_template_lifetime",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def validate_key_template(key_template):
|
|
18
|
+
if key_template.key_size is None:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
if key_template.key_size not in DNSSECKeyTemplateKeySizeChoices.values():
|
|
22
|
+
raise ValidationError(
|
|
23
|
+
{
|
|
24
|
+
"key_size": _("{key_size} is not a supported key size.").format(
|
|
25
|
+
key_size=key_template.key_size
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if key_template.algorithm != DNSSECKeyTemplateAlgorithmChoices.RSASHA256:
|
|
31
|
+
raise ValidationError(
|
|
32
|
+
{
|
|
33
|
+
"key_size": _(
|
|
34
|
+
"Specifying the key size is not supported for algorithm {algorithm}."
|
|
35
|
+
).format(algorithm=key_template.algorithm)
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def validate_key_template_assignment(key_templates):
|
|
41
|
+
if key_templates is None:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
csk = key_templates.filter(type=DNSSECKeyTemplateTypeChoices.TYPE_CSK)
|
|
45
|
+
ksk = key_templates.filter(type=DNSSECKeyTemplateTypeChoices.TYPE_KSK)
|
|
46
|
+
zsk = key_templates.filter(type=DNSSECKeyTemplateTypeChoices.TYPE_ZSK)
|
|
47
|
+
|
|
48
|
+
if csk and (ksk or zsk):
|
|
49
|
+
raise ValidationError(
|
|
50
|
+
{
|
|
51
|
+
"key_templates": _(
|
|
52
|
+
"Specifying a CSK together with any other key template type is not allowed."
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if any(
|
|
58
|
+
(
|
|
59
|
+
csk.count() > 1,
|
|
60
|
+
ksk.count() > 1,
|
|
61
|
+
zsk.count() > 1,
|
|
62
|
+
)
|
|
63
|
+
):
|
|
64
|
+
raise ValidationError(
|
|
65
|
+
{
|
|
66
|
+
"key_templates": _(
|
|
67
|
+
"At most one key template per type (CSK, KSK and ZSK) is allowed."
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if (ksk and zsk) and (ksk.first().algorithm != zsk.first().algorithm):
|
|
73
|
+
raise ValidationError(
|
|
74
|
+
{"key_templates": _("KSK and ZSK must use the same algorithm.")}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def validate_key_template_lifetime(key_template, policy, raise_exception=True):
|
|
79
|
+
key_lifetime = key_template.lifetime
|
|
80
|
+
|
|
81
|
+
if not key_lifetime:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
dnskey_ttl = policy.get_effective_value("dnskey_ttl")
|
|
85
|
+
publish_safety = policy.get_effective_value("publish_safety")
|
|
86
|
+
zone_propagation_delay = policy.get_effective_value("zone_propagation_delay")
|
|
87
|
+
max_zone_ttl = policy.get_effective_value("max_zone_ttl")
|
|
88
|
+
retire_safety = policy.get_effective_value("retire_safety")
|
|
89
|
+
|
|
90
|
+
validation_errors = []
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
dnskey_ttl is not None
|
|
94
|
+
and publish_safety is not None
|
|
95
|
+
and zone_propagation_delay is not None
|
|
96
|
+
and key_lifetime < (dnskey_ttl + publish_safety + zone_propagation_delay)
|
|
97
|
+
):
|
|
98
|
+
validation_errors.append(
|
|
99
|
+
_(
|
|
100
|
+
"Key Lifetime {lifetime} is less than DNSKEY TTL + Publish Safety + Zone Propagation Delay."
|
|
101
|
+
).format(lifetime=key_lifetime)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
max_zone_ttl is not None
|
|
106
|
+
and retire_safety is not None
|
|
107
|
+
and zone_propagation_delay is not None
|
|
108
|
+
and key_lifetime < (max_zone_ttl + retire_safety + zone_propagation_delay)
|
|
109
|
+
):
|
|
110
|
+
validation_errors.append(
|
|
111
|
+
_(
|
|
112
|
+
"Key Lifetime {lifetime} is less than Max Zone TTL + Retire Safety + Zone Propagation Delay."
|
|
113
|
+
).format(lifetime=key_lifetime)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if key_template.type == DNSSECKeyTemplateTypeChoices.TYPE_ZSK:
|
|
117
|
+
signatures_validity = policy.get_effective_value("signatures_validity")
|
|
118
|
+
signatures_refresh = policy.get_effective_value("signatures_refresh")
|
|
119
|
+
|
|
120
|
+
if (
|
|
121
|
+
signatures_validity is not None
|
|
122
|
+
and signatures_validity is not None
|
|
123
|
+
and key_lifetime < signatures_validity - signatures_refresh
|
|
124
|
+
):
|
|
125
|
+
validation_errors.append(
|
|
126
|
+
_(
|
|
127
|
+
"Key Lifetime {lifetime} is less than Signatures Validity - Signatures Refresh."
|
|
128
|
+
)
|
|
129
|
+
).format(lifetime=key_lifetime)
|
|
130
|
+
else:
|
|
131
|
+
parent_ds_ttl = policy.get_effective_value("parent_ds_ttl")
|
|
132
|
+
parent_propagation_delay = policy.get_effective_value(
|
|
133
|
+
"parent_propagation_delay"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if (
|
|
137
|
+
parent_ds_ttl is not None
|
|
138
|
+
and retire_safety is not None
|
|
139
|
+
and parent_propagation_delay is not None
|
|
140
|
+
and parent_ds_ttl < retire_safety + parent_propagation_delay
|
|
141
|
+
):
|
|
142
|
+
validation_errors.append(
|
|
143
|
+
_(
|
|
144
|
+
"Key Lifetime {lifetime} is less than Parent DS TTL + Retire Safety + Parent Propagation Delay."
|
|
145
|
+
).format(lifetime=key_lifetime)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return validation_errors
|
netbox_dns/views/__init__.py
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from netbox.views import generic
|
|
2
|
+
from utilities.views import register_model_view
|
|
3
|
+
from tenancy.views import ObjectContactsView
|
|
4
|
+
|
|
5
|
+
from netbox_dns.filtersets import DNSSECKeyTemplateFilterSet
|
|
6
|
+
from netbox_dns.forms import (
|
|
7
|
+
DNSSECKeyTemplateImportForm,
|
|
8
|
+
DNSSECKeyTemplateFilterForm,
|
|
9
|
+
DNSSECKeyTemplateForm,
|
|
10
|
+
DNSSECKeyTemplateBulkEditForm,
|
|
11
|
+
)
|
|
12
|
+
from netbox_dns.models import DNSSECKeyTemplate
|
|
13
|
+
from netbox_dns.tables import DNSSECKeyTemplateTable, DNSSECPolicyDisplayTable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = (
|
|
17
|
+
"DNSSECKeyTemplateView",
|
|
18
|
+
"DNSSECKeyTemplateListView",
|
|
19
|
+
"DNSSECKeyTemplateEditView",
|
|
20
|
+
"DNSSECKeyTemplateDeleteView",
|
|
21
|
+
"DNSSECKeyTemplateBulkEditView",
|
|
22
|
+
"DNSSECKeyTemplateBulkImportView",
|
|
23
|
+
"DNSSECKeyTemplateBulkDeleteView",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@register_model_view(DNSSECKeyTemplate, "list", path="", detail=False)
|
|
28
|
+
class DNSSECKeyTemplateListView(generic.ObjectListView):
|
|
29
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
30
|
+
filterset = DNSSECKeyTemplateFilterSet
|
|
31
|
+
filterset_form = DNSSECKeyTemplateFilterForm
|
|
32
|
+
table = DNSSECKeyTemplateTable
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@register_model_view(DNSSECKeyTemplate)
|
|
36
|
+
class DNSSECKeyTemplateView(generic.ObjectView):
|
|
37
|
+
queryset = DNSSECKeyTemplate.objects.prefetch_related("policies")
|
|
38
|
+
|
|
39
|
+
def get_extra_context(self, request, instance):
|
|
40
|
+
if instance.policies.exists():
|
|
41
|
+
return {
|
|
42
|
+
"policy_table": DNSSECPolicyDisplayTable(data=instance.policies.all())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register_model_view(DNSSECKeyTemplate, "add", detail=False)
|
|
49
|
+
@register_model_view(DNSSECKeyTemplate, "edit")
|
|
50
|
+
class DNSSECKeyTemplateEditView(generic.ObjectEditView):
|
|
51
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
52
|
+
form = DNSSECKeyTemplateForm
|
|
53
|
+
default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@register_model_view(DNSSECKeyTemplate, "delete")
|
|
57
|
+
class DNSSECKeyTemplateDeleteView(generic.ObjectDeleteView):
|
|
58
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
59
|
+
default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@register_model_view(DNSSECKeyTemplate, "bulk_import", detail=False)
|
|
63
|
+
class DNSSECKeyTemplateBulkImportView(generic.BulkImportView):
|
|
64
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
65
|
+
model_form = DNSSECKeyTemplateImportForm
|
|
66
|
+
table = DNSSECKeyTemplateTable
|
|
67
|
+
default_return_url = "plugins:netbox_dns:dnsseckeytemplate_list"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@register_model_view(DNSSECKeyTemplate, "bulk_edit", path="edit", detail=False)
|
|
71
|
+
class DNSSECKeyTemplateBulkEditView(generic.BulkEditView):
|
|
72
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
73
|
+
filterset = DNSSECKeyTemplateFilterSet
|
|
74
|
+
table = DNSSECKeyTemplateTable
|
|
75
|
+
form = DNSSECKeyTemplateBulkEditForm
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@register_model_view(DNSSECKeyTemplate, "bulk_delete", path="delete", detail=False)
|
|
79
|
+
class DNSSECKeyTemplateBulkDeleteView(generic.BulkDeleteView):
|
|
80
|
+
queryset = DNSSECKeyTemplate.objects.all()
|
|
81
|
+
filterset = DNSSECKeyTemplateFilterSet
|
|
82
|
+
table = DNSSECKeyTemplateTable
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@register_model_view(DNSSECKeyTemplate, "contacts")
|
|
86
|
+
class DNSSECKeyTemplateContactsView(ObjectContactsView):
|
|
87
|
+
queryset = DNSSECKeyTemplate.objects.all()
|