netbox-authorized-keys 1.0.0__tar.gz

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.
Files changed (31) hide show
  1. netbox_authorized_keys-1.0.0/PKG-INFO +6 -0
  2. netbox_authorized_keys-1.0.0/README.md +42 -0
  3. netbox_authorized_keys-1.0.0/netbox_authorized_keys/__init__.py +19 -0
  4. netbox_authorized_keys-1.0.0/netbox_authorized_keys/api/__init__.py +2 -0
  5. netbox_authorized_keys-1.0.0/netbox_authorized_keys/api/serializers.py +59 -0
  6. netbox_authorized_keys-1.0.0/netbox_authorized_keys/api/urls.py +14 -0
  7. netbox_authorized_keys-1.0.0/netbox_authorized_keys/api/views.py +27 -0
  8. netbox_authorized_keys-1.0.0/netbox_authorized_keys/filtersets/__init__.py +1 -0
  9. netbox_authorized_keys-1.0.0/netbox_authorized_keys/filtersets/authorizedkey.py +63 -0
  10. netbox_authorized_keys-1.0.0/netbox_authorized_keys/forms/__init__.py +1 -0
  11. netbox_authorized_keys-1.0.0/netbox_authorized_keys/forms/authorizedkey.py +79 -0
  12. netbox_authorized_keys-1.0.0/netbox_authorized_keys/migrations/0001_initial.py +160 -0
  13. netbox_authorized_keys-1.0.0/netbox_authorized_keys/migrations/0002_authorizedkey_full_name_and_more.py +33 -0
  14. netbox_authorized_keys-1.0.0/netbox_authorized_keys/migrations/__init__.py +0 -0
  15. netbox_authorized_keys-1.0.0/netbox_authorized_keys/models/__init__.py +1 -0
  16. netbox_authorized_keys-1.0.0/netbox_authorized_keys/models/authorizedkey.py +116 -0
  17. netbox_authorized_keys-1.0.0/netbox_authorized_keys/navigation.py +28 -0
  18. netbox_authorized_keys-1.0.0/netbox_authorized_keys/tables/__init__.py +1 -0
  19. netbox_authorized_keys-1.0.0/netbox_authorized_keys/tables/authorizedkey.py +50 -0
  20. netbox_authorized_keys-1.0.0/netbox_authorized_keys/template_content.py +58 -0
  21. netbox_authorized_keys-1.0.0/netbox_authorized_keys/urls.py +27 -0
  22. netbox_authorized_keys-1.0.0/netbox_authorized_keys/version.py +5 -0
  23. netbox_authorized_keys-1.0.0/netbox_authorized_keys/views/__init__.py +1 -0
  24. netbox_authorized_keys-1.0.0/netbox_authorized_keys/views/authorizedkey.py +62 -0
  25. netbox_authorized_keys-1.0.0/netbox_authorized_keys.egg-info/PKG-INFO +6 -0
  26. netbox_authorized_keys-1.0.0/netbox_authorized_keys.egg-info/SOURCES.txt +29 -0
  27. netbox_authorized_keys-1.0.0/netbox_authorized_keys.egg-info/dependency_links.txt +1 -0
  28. netbox_authorized_keys-1.0.0/netbox_authorized_keys.egg-info/not-zip-safe +1 -0
  29. netbox_authorized_keys-1.0.0/netbox_authorized_keys.egg-info/top_level.txt +1 -0
  30. netbox_authorized_keys-1.0.0/setup.cfg +4 -0
  31. netbox_authorized_keys-1.0.0/setup.py +44 -0
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.1
2
+ Name: netbox_authorized_keys
3
+ Version: 1.0.0
4
+ Summary: NetBox plugin to store Authorized Keys
5
+ Author: Jan Krupa
6
+ License: MIT
@@ -0,0 +1,42 @@
1
+ # NetBox Authorized Keys Plugin
2
+
3
+ NetBox Authorized Keys is a plugin for NetBox that allows you to store and manage SSH authorized keys.
4
+
5
+ ## Features
6
+
7
+ - Store SSH authorized keys
8
+ - Assign keys to devices or virtual machines
9
+ - Manage keys through the NetBox UI
10
+ - API support for managing keys
11
+
12
+ ## Installation
13
+
14
+ 1. Clone the repository:
15
+ ```sh
16
+ git clone https://github.com/yourusername/netbox_authorized_keys.git
17
+ ```
18
+
19
+ 2. Navigate to the project directory:
20
+ ```sh
21
+ cd netbox_authorized_keys
22
+ ```
23
+
24
+ 3. Install the plugin:
25
+ ```sh
26
+ pip install .
27
+ ```
28
+
29
+ 4. Add the plugin to your NetBox configuration:
30
+ ```python
31
+ PLUGINS = ["netbox_authorized_keys"]
32
+ PLUGINS_CONFIG = {
33
+ "netbox_authorized_keys": {
34
+ # Add any plugin-specific configuration here
35
+ }
36
+ }
37
+ ```
38
+
39
+ 5. Run the migrations:
40
+ ```sh
41
+ python manage.py migrate
42
+ ```
@@ -0,0 +1,19 @@
1
+ """Top-level package for NetBox cesnet_services Plugin."""
2
+
3
+ __author__ = """Jan Krupa"""
4
+ __email__ = "jan.krupa@cesnet.cz"
5
+ __version__ = "0.0.1"
6
+
7
+
8
+ from netbox.plugins import PluginConfig
9
+
10
+
11
+ class NetBoxAuthorizeKeysConfig(PluginConfig):
12
+ name = "netbox_authorized_keys"
13
+ verbose_name = "NetBox Authorized key Plugin"
14
+ description = ""
15
+ version = __version__
16
+ base_url = "authorized-keys"
17
+
18
+
19
+ config = NetBoxAuthorizeKeysConfig
@@ -0,0 +1,2 @@
1
+
2
+ # This file can be left empty or used to include any necessary initialization code for the API module.
@@ -0,0 +1,59 @@
1
+ from dcim.api.serializers_.devices import DeviceSerializer
2
+ from dcim.models import Device
3
+ from netbox.api.fields import SerializedPKRelatedField
4
+ from netbox.api.serializers import NetBoxModelSerializer
5
+ from rest_framework import serializers
6
+ from virtualization.api.serializers_.virtualmachines import VirtualMachineSerializer
7
+ from virtualization.models import VirtualMachine
8
+
9
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
10
+
11
+
12
+ class AuthorizedKeySerializer(NetBoxModelSerializer):
13
+ url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_authorized_keys-api:authorizedkey-detail")
14
+ devices = SerializedPKRelatedField(
15
+ queryset=Device.objects.all(), serializer=DeviceSerializer, nested=True, required=False, many=True
16
+ )
17
+
18
+ virtual_machines = SerializedPKRelatedField(
19
+ queryset=VirtualMachine.objects.all(),
20
+ serializer=VirtualMachineSerializer,
21
+ nested=True,
22
+ required=False,
23
+ many=True,
24
+ )
25
+
26
+ class Meta:
27
+ model = AuthorizedKey
28
+ fields = [
29
+ "id",
30
+ "display",
31
+ "url",
32
+ "public_key",
33
+ "username",
34
+ "full_name",
35
+ "description",
36
+ "comments",
37
+ "devices",
38
+ "virtual_machines",
39
+ "tags",
40
+ ]
41
+ brief_fields = ("id", "url", "display")
42
+
43
+
44
+ class AuthorizedKeyDeviceSerializer(NetBoxModelSerializer):
45
+ authorized_key = AuthorizedKeySerializer(nested=True)
46
+ device = DeviceSerializer(nested=True)
47
+
48
+ class Meta:
49
+ model = AuthorizedKeyDevice
50
+ fields = ["id", "authorized_key", "device"]
51
+
52
+
53
+ class AuthorizedKeyVirtualMachineSerializer(NetBoxModelSerializer):
54
+ authorized_key = AuthorizedKeySerializer(nested=True)
55
+ virtual_machine = VirtualMachineSerializer(nested=True)
56
+
57
+ class Meta:
58
+ model = AuthorizedKeyVirtualMachine
59
+ fields = ["id", "authorized_key", "virtual_machine"]
@@ -0,0 +1,14 @@
1
+ from rest_framework.routers import DefaultRouter
2
+
3
+ from netbox_authorized_keys.api.views import (
4
+ AuthorizedKeyDeviceViewSet,
5
+ AuthorizedKeyViewSet,
6
+ AuthorizedKeyVirtualMachineViewSet,
7
+ )
8
+
9
+ router = DefaultRouter()
10
+ router.register(r"authorized-keys", AuthorizedKeyViewSet)
11
+ router.register(r"devices", AuthorizedKeyDeviceViewSet)
12
+ router.register(r"virtual-machines", AuthorizedKeyVirtualMachineViewSet)
13
+
14
+ urlpatterns = router.urls
@@ -0,0 +1,27 @@
1
+ from netbox.api.viewsets import NetBoxModelViewSet
2
+
3
+ from netbox_authorized_keys.api.serializers import (
4
+ AuthorizedKeyDeviceSerializer,
5
+ AuthorizedKeySerializer,
6
+ AuthorizedKeyVirtualMachineSerializer,
7
+ )
8
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
9
+ from netbox_authorized_keys.filtersets import AuthorizedKeyFilterSet, AuthorizedKeyDeviceFilterSet, AuthorizedKeyVirtualMachineFilterSet
10
+
11
+
12
+ class AuthorizedKeyViewSet(NetBoxModelViewSet):
13
+ queryset = AuthorizedKey.objects.all()
14
+ serializer_class = AuthorizedKeySerializer
15
+ filterset_class = AuthorizedKeyFilterSet
16
+
17
+
18
+ class AuthorizedKeyDeviceViewSet(NetBoxModelViewSet):
19
+ queryset = AuthorizedKeyDevice.objects.all()
20
+ serializer_class = AuthorizedKeyDeviceSerializer
21
+ filterset_class = AuthorizedKeyDeviceFilterSet
22
+
23
+
24
+ class AuthorizedKeyVirtualMachineViewSet(NetBoxModelViewSet):
25
+ queryset = AuthorizedKeyVirtualMachine.objects.all()
26
+ serializer_class = AuthorizedKeyVirtualMachineSerializer
27
+ filterset_class = AuthorizedKeyVirtualMachineFilterSet
@@ -0,0 +1 @@
1
+ from .authorizedkey import *
@@ -0,0 +1,63 @@
1
+ import django_filters
2
+ from dcim.models import Device
3
+ from django.db.models import Q
4
+ from extras.filters import TagFilter
5
+ from netbox.filtersets import BaseFilterSet, NetBoxModelFilterSet
6
+ from virtualization.models import VirtualMachine
7
+
8
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
9
+
10
+
11
+ class AuthorizedKeyFilterSet(NetBoxModelFilterSet):
12
+ q = django_filters.CharFilter(method="search", label="Search")
13
+ public_key = django_filters.CharFilter()
14
+ username = django_filters.CharFilter()
15
+ full_name = django_filters.CharFilter()
16
+ description = django_filters.CharFilter()
17
+ tag = TagFilter()
18
+
19
+ devices = django_filters.ModelMultipleChoiceFilter(
20
+ field_name="devices", queryset=Device.objects.all(), label="Devices"
21
+ )
22
+ virtual_machines = django_filters.ModelMultipleChoiceFilter(
23
+ field_name="virtual_machines", queryset=VirtualMachine.objects.all(), label="Virtual Machines"
24
+ )
25
+
26
+ class Meta:
27
+ model = AuthorizedKey
28
+ fields = ["id", "public_key", "username", "full_name", "description", "comments", "devices", "virtual_machines"]
29
+
30
+ def search(self, queryset, name, value):
31
+ if not value.strip():
32
+ return queryset
33
+
34
+ filters = Q(username__icontains=value) | Q(full_name__icontains=value) | Q(description__icontains=value) | Q(comments__icontains=value)
35
+ return queryset.filter(filters)
36
+
37
+
38
+ class AuthorizedKeyDeviceFilterSet(BaseFilterSet):
39
+ authorized_key = django_filters.ModelMultipleChoiceFilter(
40
+ field_name="authorized_key", queryset=AuthorizedKey.objects.all(), label="Authorized Key"
41
+ )
42
+
43
+ device = django_filters.ModelMultipleChoiceFilter(
44
+ field_name="device", queryset=Device.objects.all(), label="Device"
45
+ )
46
+
47
+ class Meta:
48
+ model = AuthorizedKeyDevice
49
+ fields = ["id", "authorized_key", "device"]
50
+
51
+
52
+ class AuthorizedKeyVirtualMachineFilterSet(BaseFilterSet):
53
+ authorized_key = django_filters.ModelMultipleChoiceFilter(
54
+ field_name="authorized_key", queryset=AuthorizedKey.objects.all(), label="Authorized Key"
55
+ )
56
+
57
+ virtual_machine = django_filters.ModelMultipleChoiceFilter(
58
+ field_name="virtual_machine", queryset=VirtualMachine.objects.all(), label="Virtual Machine"
59
+ )
60
+
61
+ class Meta:
62
+ model = AuthorizedKeyVirtualMachine
63
+ fields = ["id", "authorized_key", "virtual_machine"]
@@ -0,0 +1 @@
1
+ from .authorizedkey import *
@@ -0,0 +1,79 @@
1
+ from dcim.models import Device
2
+ from django import forms
3
+ from django.utils.translation import gettext_lazy as _
4
+ from netbox.forms import NetBoxModelFilterSetForm, NetBoxModelForm
5
+ from utilities.forms.fields import (
6
+ CommentField,
7
+ DynamicModelMultipleChoiceField,
8
+ TagFilterField,
9
+ )
10
+ from utilities.forms.rendering import FieldSet
11
+ from virtualization.models import VirtualMachine
12
+
13
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
14
+
15
+
16
+ class AuthorizedKeyForm(NetBoxModelForm):
17
+ public_key = forms.CharField(
18
+ label=_("Public Key"), widget=forms.Textarea, required=True, help_text=_("Enter the public key (unique)")
19
+ )
20
+ username = forms.CharField(label=_("Username"), required=True)
21
+ full_name = forms.CharField(label=_("Full Name"), required=False, help_text=_("Owner's Full Name"))
22
+
23
+ devices = DynamicModelMultipleChoiceField(queryset=Device.objects.all(), label=_("Devices"), required=False)
24
+ virtual_machines = DynamicModelMultipleChoiceField(
25
+ queryset=VirtualMachine.objects.all(), label=_("Virtual Machines"), required=False
26
+ )
27
+
28
+ fieldsets = (
29
+ FieldSet("public_key", "username", "full_name", "description", name=_("Base")),
30
+ FieldSet("devices", "virtual_machines", name=_("Host Assignment")),
31
+ FieldSet("tags", name=_("Misc")),
32
+ )
33
+
34
+ class Meta:
35
+ comments = CommentField()
36
+ model = AuthorizedKey
37
+ fields = [
38
+ "public_key",
39
+ "username",
40
+ "full_name",
41
+ "description",
42
+ "devices",
43
+ "virtual_machines",
44
+ "comments",
45
+ "tags",
46
+ ]
47
+
48
+
49
+ class AuthorizedKeyFilterForm(NetBoxModelFilterSetForm):
50
+ model = AuthorizedKey
51
+ public_key = forms.CharField(required=False, widget=forms.widgets.Textarea(attrs={"placeholder": _("Public Key")}))
52
+ username = forms.CharField(required=False)
53
+ full_name = forms.CharField(required=False)
54
+ description = forms.CharField(required=False)
55
+
56
+ devices = DynamicModelMultipleChoiceField(queryset=Device.objects.all(), label=_("Devices"), required=False)
57
+ virtual_machines = DynamicModelMultipleChoiceField(
58
+ queryset=VirtualMachine.objects.all(), label=_("Virtual Machines"), required=False
59
+ )
60
+
61
+ tag = TagFilterField(model)
62
+
63
+
64
+ class AuthorizedKeyDeviceFilterForm(NetBoxModelFilterSetForm):
65
+ model = AuthorizedKeyDevice
66
+ authorized_key = DynamicModelMultipleChoiceField(
67
+ queryset=AuthorizedKey.objects.all(), label=_("Authorized Key"), required=False
68
+ )
69
+ device = DynamicModelMultipleChoiceField(queryset=Device.objects.all(), label=_("Device"), required=False)
70
+
71
+
72
+ class AuthorizedKeyVirtualMachineFilterForm(NetBoxModelFilterSetForm):
73
+ model = AuthorizedKeyVirtualMachine
74
+ authorized_key = DynamicModelMultipleChoiceField(
75
+ queryset=AuthorizedKey.objects.all(), label=_("Authorized Key"), required=False
76
+ )
77
+ virtual_machine = DynamicModelMultipleChoiceField(
78
+ queryset=VirtualMachine.objects.all(), label=_("Virtual Machine"), required=False
79
+ )
@@ -0,0 +1,160 @@
1
+ # Generated by Django 5.0.9 on 2024-11-25 13:28
2
+
3
+ import django.db.models.deletion
4
+ import taggit.managers
5
+ import utilities.json
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ initial = True
12
+
13
+ dependencies = [
14
+ ("dcim", "0191_module_bay_rebuild"),
15
+ ("extras", "0121_customfield_related_object_filter"),
16
+ ("virtualization", "0040_convert_disk_size"),
17
+ ]
18
+
19
+ operations = [
20
+ migrations.CreateModel(
21
+ name="AuthorizedKey",
22
+ fields=[
23
+ (
24
+ "id",
25
+ models.BigAutoField(
26
+ auto_created=True, primary_key=True, serialize=False
27
+ ),
28
+ ),
29
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
30
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
31
+ (
32
+ "custom_field_data",
33
+ models.JSONField(
34
+ blank=True,
35
+ default=dict,
36
+ encoder=utilities.json.CustomFieldJSONEncoder,
37
+ ),
38
+ ),
39
+ ("public_key", models.TextField()),
40
+ ("username", models.CharField(blank=True, default="", max_length=200)),
41
+ (
42
+ "description",
43
+ models.CharField(blank=True, default="", max_length=200),
44
+ ),
45
+ ("comments", models.TextField(blank=True, default="")),
46
+ (
47
+ "tags",
48
+ taggit.managers.TaggableManager(
49
+ through="extras.TaggedItem", to="extras.Tag"
50
+ ),
51
+ ),
52
+ ],
53
+ options={
54
+ "abstract": False,
55
+ },
56
+ ),
57
+ migrations.CreateModel(
58
+ name="AuthorizedKeyDevice",
59
+ fields=[
60
+ (
61
+ "id",
62
+ models.BigAutoField(
63
+ auto_created=True, primary_key=True, serialize=False
64
+ ),
65
+ ),
66
+ (
67
+ "custom_field_data",
68
+ models.JSONField(
69
+ blank=True,
70
+ default=dict,
71
+ encoder=utilities.json.CustomFieldJSONEncoder,
72
+ ),
73
+ ),
74
+ (
75
+ "authorized_key",
76
+ models.ForeignKey(
77
+ on_delete=django.db.models.deletion.CASCADE,
78
+ to="netbox_authorized_keys.authorizedkey",
79
+ ),
80
+ ),
81
+ (
82
+ "device",
83
+ models.ForeignKey(
84
+ on_delete=django.db.models.deletion.CASCADE, to="dcim.device"
85
+ ),
86
+ ),
87
+ (
88
+ "tags",
89
+ taggit.managers.TaggableManager(
90
+ through="extras.TaggedItem", to="extras.Tag"
91
+ ),
92
+ ),
93
+ ],
94
+ options={
95
+ "unique_together": {("authorized_key", "device")},
96
+ },
97
+ ),
98
+ migrations.AddField(
99
+ model_name="authorizedkey",
100
+ name="devices",
101
+ field=models.ManyToManyField(
102
+ blank=True,
103
+ related_name="authorized_keys",
104
+ through="netbox_authorized_keys.AuthorizedKeyDevice",
105
+ to="dcim.device",
106
+ ),
107
+ ),
108
+ migrations.CreateModel(
109
+ name="AuthorizedKeyVirtualMachine",
110
+ fields=[
111
+ (
112
+ "id",
113
+ models.BigAutoField(
114
+ auto_created=True, primary_key=True, serialize=False
115
+ ),
116
+ ),
117
+ (
118
+ "custom_field_data",
119
+ models.JSONField(
120
+ blank=True,
121
+ default=dict,
122
+ encoder=utilities.json.CustomFieldJSONEncoder,
123
+ ),
124
+ ),
125
+ (
126
+ "authorized_key",
127
+ models.ForeignKey(
128
+ on_delete=django.db.models.deletion.CASCADE,
129
+ to="netbox_authorized_keys.authorizedkey",
130
+ ),
131
+ ),
132
+ (
133
+ "tags",
134
+ taggit.managers.TaggableManager(
135
+ through="extras.TaggedItem", to="extras.Tag"
136
+ ),
137
+ ),
138
+ (
139
+ "virtual_machine",
140
+ models.ForeignKey(
141
+ on_delete=django.db.models.deletion.CASCADE,
142
+ to="virtualization.virtualmachine",
143
+ ),
144
+ ),
145
+ ],
146
+ options={
147
+ "unique_together": {("authorized_key", "virtual_machine")},
148
+ },
149
+ ),
150
+ migrations.AddField(
151
+ model_name="authorizedkey",
152
+ name="virtual_machines",
153
+ field=models.ManyToManyField(
154
+ blank=True,
155
+ related_name="authorized_keys",
156
+ through="netbox_authorized_keys.AuthorizedKeyVirtualMachine",
157
+ to="virtualization.virtualmachine",
158
+ ),
159
+ ),
160
+ ]
@@ -0,0 +1,33 @@
1
+ # Generated by Django 5.0.9 on 2024-11-26 08:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("netbox_authorized_keys", "0001_initial"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="authorizedkey",
15
+ name="full_name",
16
+ field=models.CharField(blank=True, max_length=200),
17
+ ),
18
+ migrations.AlterField(
19
+ model_name="authorizedkey",
20
+ name="description",
21
+ field=models.CharField(blank=True, default="", max_length=255),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name="authorizedkey",
25
+ name="public_key",
26
+ field=models.TextField(unique=True),
27
+ ),
28
+ migrations.AlterField(
29
+ model_name="authorizedkey",
30
+ name="username",
31
+ field=models.CharField(max_length=200),
32
+ ),
33
+ ]
@@ -0,0 +1 @@
1
+ from .authorizedkey import *
@@ -0,0 +1,116 @@
1
+ from django.db import models
2
+ from django.urls import reverse
3
+ from django.utils.translation import gettext_lazy as _
4
+ from netbox.models import NetBoxModel
5
+ from netbox.models.features import (
6
+ BookmarksMixin,
7
+ # ChangeLoggingMixin,
8
+ CloningMixin,
9
+ CustomFieldsMixin,
10
+ CustomLinksMixin,
11
+ CustomValidationMixin,
12
+ EventRulesMixin,
13
+ ExportTemplatesMixin,
14
+ JournalingMixin,
15
+ NotificationsMixin,
16
+ TagsMixin,
17
+ )
18
+ from utilities.querysets import RestrictedQuerySet
19
+
20
+
21
+ class AuthorizedKey(NetBoxModel):
22
+ public_key = models.TextField(verbose_name=_("Public Key"), null=False, blank=False, unique=True)
23
+ username = models.CharField(max_length=200, blank=False, null=False, verbose_name=_("Username"))
24
+ full_name = models.CharField(max_length=200, blank=True, null=False, verbose_name=_("Full Name"))
25
+
26
+ description = models.CharField(max_length=255, verbose_name=_("description"), blank=True, default="", null=False)
27
+ comments = models.TextField(blank=True, default="", null=False, verbose_name=_("Comments"))
28
+
29
+ devices = models.ManyToManyField(
30
+ to="dcim.device",
31
+ related_name="authorized_keys",
32
+ blank=True,
33
+ through="netbox_authorized_keys.AuthorizedKeyDevice",
34
+ verbose_name=_("Devices"),
35
+ )
36
+
37
+ virtual_machines = models.ManyToManyField(
38
+ to="virtualization.virtualmachine",
39
+ related_name="authorized_keys",
40
+ blank=True,
41
+ through="netbox_authorized_keys.AuthorizedKeyVirtualMachine",
42
+ verbose_name=_("Virtual Machines"),
43
+ )
44
+
45
+ objects = RestrictedQuerySet.as_manager()
46
+
47
+ def __str__(self):
48
+ if self.username and self.full_name and self.description:
49
+ return f"{self.username} - {self.full_name} - {self.description}"
50
+ elif self.username and self.full_name:
51
+ return f"{self.username} - {self.full_name}"
52
+ elif self.username and self.description:
53
+ return f"{self.username} - {self.description}"
54
+
55
+ return self.username
56
+
57
+ def get_absolute_url(self):
58
+ return reverse("plugins:netbox_authorized_keys:authorizedkey", args=[self.pk])
59
+
60
+ def serialize_object(self, *, exclude=None):
61
+ data = super().serialize_object(exclude=exclude or [])
62
+ data["devices"] = list(self.devices.values_list("pk", flat=True))
63
+ data["virtual_machines"] = list(self.virtual_machines.values_list("pk", flat=True))
64
+ return data
65
+
66
+
67
+ class AuthorizedKeyDevice(
68
+ BookmarksMixin,
69
+ # ChangeLoggingMixin,
70
+ CloningMixin,
71
+ CustomFieldsMixin,
72
+ CustomLinksMixin,
73
+ CustomValidationMixin,
74
+ ExportTemplatesMixin,
75
+ JournalingMixin,
76
+ NotificationsMixin,
77
+ TagsMixin,
78
+ EventRulesMixin,
79
+ models.Model,
80
+ ):
81
+ authorized_key = models.ForeignKey(to="netbox_authorized_keys.AuthorizedKey", on_delete=models.CASCADE)
82
+ device = models.ForeignKey(to="dcim.Device", on_delete=models.CASCADE)
83
+
84
+ objects = RestrictedQuerySet.as_manager()
85
+
86
+ class Meta:
87
+ unique_together = ("authorized_key", "device")
88
+
89
+ def __str__(self):
90
+ return f"{self.authorized_key} - {self.device}"
91
+
92
+
93
+ class AuthorizedKeyVirtualMachine(
94
+ BookmarksMixin,
95
+ # ChangeLoggingMixin,
96
+ CloningMixin,
97
+ CustomFieldsMixin,
98
+ CustomLinksMixin,
99
+ CustomValidationMixin,
100
+ ExportTemplatesMixin,
101
+ JournalingMixin,
102
+ NotificationsMixin,
103
+ TagsMixin,
104
+ EventRulesMixin,
105
+ models.Model,
106
+ ):
107
+ authorized_key = models.ForeignKey(to="netbox_authorized_keys.AuthorizedKey", on_delete=models.CASCADE)
108
+ virtual_machine = models.ForeignKey(to="virtualization.VirtualMachine", on_delete=models.CASCADE)
109
+
110
+ objects = RestrictedQuerySet.as_manager()
111
+
112
+ class Meta:
113
+ unique_together = ("authorized_key", "virtual_machine")
114
+
115
+ def __str__(self):
116
+ return f"{self.authorized_key} - {self.virtual_machine}"
@@ -0,0 +1,28 @@
1
+ from netbox.plugins import PluginMenu, PluginMenuItem
2
+
3
+ menu = PluginMenu(
4
+ label="Authorized Keys",
5
+ icon_class="mdi mdi-key",
6
+ groups=(
7
+ (
8
+ "SSH Public Keys",
9
+ (
10
+ PluginMenuItem(
11
+ link="plugins:netbox_authorized_keys:authorizedkey_list",
12
+ link_text="Authorized Keys",
13
+ permissions=["netbox_authorized_keys.view_authorizedkey"],
14
+ ),
15
+ PluginMenuItem(
16
+ link="plugins:netbox_authorized_keys:authorizedkeydevice_list",
17
+ link_text="Devices",
18
+ permissions=["netbox_authorized_keys.view_authorizedkey"],
19
+ ),
20
+ PluginMenuItem(
21
+ link="plugins:netbox_authorized_keys:authorizedkeyvirtualmachine_list",
22
+ link_text="Virtual Machines",
23
+ permissions=["netbox_authorized_keys.view_authorizedkey"],
24
+ ),
25
+ ),
26
+ ),
27
+ ),
28
+ )
@@ -0,0 +1 @@
1
+ from .authorizedkey import *
@@ -0,0 +1,50 @@
1
+ import django_tables2 as tables
2
+ from django.utils.html import format_html
3
+ from netbox.tables import NetBoxTable, columns
4
+
5
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
6
+
7
+
8
+ class AuthorizedKeyTable(NetBoxTable):
9
+ id = tables.Column(linkify=True, verbose_name="ID")
10
+ public_key = tables.Column(verbose_name="Public Key")
11
+
12
+ class Meta(NetBoxTable.Meta):
13
+ model = AuthorizedKey
14
+ fields = ("id", "public_key", "username", "full_name", "description", "comments", "tags")
15
+ default_columns = ("id", "username", "full_name", "description")
16
+
17
+ def render_public_key(self, value):
18
+ if len(value) > 23:
19
+ truncated_value = f"{value[:10]}...{value[-10:]}"
20
+ else:
21
+ truncated_value = value
22
+ return format_html('<span title="{}">{}</span>', value, truncated_value)
23
+
24
+
25
+ class AuthorizedKeyDeviceTable(NetBoxTable):
26
+ id = tables.Column(linkify=False, verbose_name="ID")
27
+
28
+ authorized_key = tables.Column(linkify=True)
29
+ device = tables.Column(linkify=True)
30
+ actions = columns.ActionsColumn(
31
+ actions=(),
32
+ )
33
+
34
+ class Meta(NetBoxTable.Meta):
35
+ model = AuthorizedKeyDevice
36
+ fields = ("authorized_key", "device")
37
+
38
+
39
+ class AuthorizedKeyVirtualMachineTable(NetBoxTable):
40
+ id = tables.Column(linkify=False, verbose_name="ID")
41
+
42
+ authorized_key = tables.Column(linkify=True)
43
+ virtual_machine = tables.Column(linkify=True)
44
+ actions = columns.ActionsColumn(
45
+ actions=(),
46
+ )
47
+
48
+ class Meta(NetBoxTable.Meta):
49
+ model = AuthorizedKeyVirtualMachine
50
+ fields = ("authorized_key", "virtual_machine")
@@ -0,0 +1,58 @@
1
+ from dcim.models import Device
2
+ from netbox.views import generic
3
+ from utilities.views import ViewTab, register_model_view
4
+ from virtualization.models import VirtualMachine
5
+
6
+ from netbox_authorized_keys.filtersets import AuthorizedKeyFilterSet
7
+ from netbox_authorized_keys.models import AuthorizedKey
8
+ from netbox_authorized_keys.tables import AuthorizedKeyTable
9
+
10
+
11
+ def create_authorized_key_tab_view(model):
12
+ view_name = f"{model._meta.model_name}-authorized-keys"
13
+ view_path = f"{model._meta.model_name}-authorized-keys"
14
+
15
+ class AuthorizedKeyTabView(generic.ObjectChildrenView):
16
+ child_model = AuthorizedKey
17
+ filterset = AuthorizedKeyFilterSet
18
+ template_name = "generic/object_children.html"
19
+ table = AuthorizedKeyTable
20
+ queryset = model.objects.all()
21
+
22
+ if model == Device:
23
+ tab = ViewTab(
24
+ label="Authorized Keys",
25
+ badge=lambda obj: AuthorizedKey.objects.filter(devices=obj.id).count(),
26
+ permission="netbox_authorized_keys.view_authorizedkey",
27
+ )
28
+
29
+ def get_children(self, request, parent):
30
+ children = self.child_model.objects.filter(devices=parent.id).restrict(request.user, "view")
31
+ return children
32
+
33
+ def get_table(self, data, request, bulk_actions=True):
34
+ table = super().get_table(data, request, bulk_actions)
35
+ table.exclude = "actions"
36
+ return table
37
+
38
+ elif model == VirtualMachine:
39
+ tab = ViewTab(
40
+ label="Authorized Keys",
41
+ badge=lambda obj: AuthorizedKey.objects.filter(virtual_machines=obj.id).count(),
42
+ permission="netbox_authorized_keys.view_authorizedkey",
43
+ )
44
+
45
+ def get_children(self, request, parent):
46
+ children = self.child_model.objects.filter(virtual_machines=parent.id).restrict(request.user, "view")
47
+ return children
48
+
49
+ def get_table(self, data, request, bulk_actions=True):
50
+ table = super().get_table(data, request, bulk_actions)
51
+ table.exclude = "actions"
52
+ return table
53
+
54
+ register_model_view(model, name=view_name, path=view_path)(AuthorizedKeyTabView)
55
+
56
+
57
+ for model in [Device, VirtualMachine]:
58
+ create_authorized_key_tab_view(model)
@@ -0,0 +1,27 @@
1
+ from django.urls import path
2
+ from netbox.views.generic import ObjectChangeLogView
3
+
4
+ from netbox_authorized_keys import models, views
5
+
6
+ urlpatterns = [
7
+ # Authorized Keys
8
+ path("authorized-keys/", views.AuthorizedKeyListView.as_view(), name="authorizedkey_list"),
9
+ path("authorized-keys/add/", views.AuthorizedKeyEditView.as_view(), name="authorizedkey_add"),
10
+ path("authorized-keys/<int:pk>/", views.AuthorizedKeyView.as_view(), name="authorizedkey"),
11
+ path("authorized-keys/<int:pk>/edit/", views.AuthorizedKeyEditView.as_view(), name="authorizedkey_edit"),
12
+ path("authorized-keys/<int:pk>/delete/", views.AuthorizedKeyDeleteView.as_view(), name="authorizedkey_delete"),
13
+ path(
14
+ "authorized-keys/<int:pk>/changelog/",
15
+ ObjectChangeLogView.as_view(),
16
+ name="authorizedkey_changelog",
17
+ kwargs={"model": models.AuthorizedKey},
18
+ ),
19
+ # Authorized Key Devices
20
+ path("authorized-keys/devices/", views.AuthorizedKeyDeviceListView.as_view(), name="authorizedkeydevice_list"),
21
+ # Authorized Key Virtual Machines
22
+ path(
23
+ "authorized-keys/virtual-machines/",
24
+ views.AuthorizedKeyVirtualMachineListView.as_view(),
25
+ name="authorizedkeyvirtualmachine_list",
26
+ ),
27
+ ]
@@ -0,0 +1,5 @@
1
+ __author__ = "Jan Krupa"
2
+ __email__ = "jan.krupa@cesnet.cz"
3
+ __version__ = "1.0.0"
4
+ __description__ = "NetBox plugin to store Authorized Keys"
5
+ __name__ = "netbox_authorized_keys"
@@ -0,0 +1 @@
1
+ from .authorizedkey import *
@@ -0,0 +1,62 @@
1
+ from dcim.models import Device
2
+ from netbox.views import generic
3
+ from virtualization.models import VirtualMachine
4
+
5
+ from netbox_authorized_keys.filtersets import (
6
+ AuthorizedKeyDeviceFilterSet,
7
+ AuthorizedKeyFilterSet,
8
+ AuthorizedKeyVirtualMachineFilterSet,
9
+ )
10
+ from netbox_authorized_keys.forms import (
11
+ AuthorizedKeyDeviceFilterForm,
12
+ AuthorizedKeyFilterForm,
13
+ AuthorizedKeyForm,
14
+ AuthorizedKeyVirtualMachineFilterForm,
15
+ )
16
+ from netbox_authorized_keys.models import AuthorizedKey, AuthorizedKeyDevice, AuthorizedKeyVirtualMachine
17
+ from netbox_authorized_keys.tables import AuthorizedKeyDeviceTable, AuthorizedKeyTable, AuthorizedKeyVirtualMachineTable
18
+
19
+
20
+ class AuthorizedKeyListView(generic.ObjectListView):
21
+ queryset = AuthorizedKey.objects.all()
22
+ table = AuthorizedKeyTable
23
+ filterset = AuthorizedKeyFilterSet
24
+ filterset_form = AuthorizedKeyFilterForm
25
+
26
+
27
+ class AuthorizedKeyView(generic.ObjectView):
28
+ queryset = AuthorizedKey.objects.all()
29
+
30
+ def get_extra_context(self, request, instance):
31
+ device_ids = list(Device.objects.filter(authorized_keys=instance).values_list("id", flat=True))
32
+ virtual_machine_ids = list(VirtualMachine.objects.filter(authorized_keys=instance).values_list("id", flat=True))
33
+
34
+ return {
35
+ "device_ids": device_ids,
36
+ "virtual_machine_ids": virtual_machine_ids,
37
+ }
38
+
39
+
40
+ class AuthorizedKeyEditView(generic.ObjectEditView):
41
+ queryset = AuthorizedKey.objects.all()
42
+ form = AuthorizedKeyForm
43
+
44
+
45
+ class AuthorizedKeyDeleteView(generic.ObjectDeleteView):
46
+ queryset = AuthorizedKey.objects.all()
47
+
48
+
49
+ # Authorized Key Devices
50
+ class AuthorizedKeyDeviceListView(generic.ObjectListView):
51
+ queryset = AuthorizedKeyDevice.objects.all()
52
+ table = AuthorizedKeyDeviceTable
53
+ filterset = AuthorizedKeyDeviceFilterSet
54
+ filterset_form = AuthorizedKeyDeviceFilterForm
55
+
56
+
57
+ # Authorized Key Virtual Machines
58
+ class AuthorizedKeyVirtualMachineListView(generic.ObjectListView):
59
+ queryset = AuthorizedKeyVirtualMachine.objects.all()
60
+ table = AuthorizedKeyVirtualMachineTable
61
+ filterset = AuthorizedKeyVirtualMachineFilterSet
62
+ filterset_form = AuthorizedKeyVirtualMachineFilterForm
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.1
2
+ Name: netbox_authorized_keys
3
+ Version: 1.0.0
4
+ Summary: NetBox plugin to store Authorized Keys
5
+ Author: Jan Krupa
6
+ License: MIT
@@ -0,0 +1,29 @@
1
+ README.md
2
+ setup.py
3
+ netbox_authorized_keys/__init__.py
4
+ netbox_authorized_keys/navigation.py
5
+ netbox_authorized_keys/template_content.py
6
+ netbox_authorized_keys/urls.py
7
+ netbox_authorized_keys/version.py
8
+ netbox_authorized_keys.egg-info/PKG-INFO
9
+ netbox_authorized_keys.egg-info/SOURCES.txt
10
+ netbox_authorized_keys.egg-info/dependency_links.txt
11
+ netbox_authorized_keys.egg-info/not-zip-safe
12
+ netbox_authorized_keys.egg-info/top_level.txt
13
+ netbox_authorized_keys/api/__init__.py
14
+ netbox_authorized_keys/api/serializers.py
15
+ netbox_authorized_keys/api/urls.py
16
+ netbox_authorized_keys/api/views.py
17
+ netbox_authorized_keys/filtersets/__init__.py
18
+ netbox_authorized_keys/filtersets/authorizedkey.py
19
+ netbox_authorized_keys/forms/__init__.py
20
+ netbox_authorized_keys/forms/authorizedkey.py
21
+ netbox_authorized_keys/migrations/0001_initial.py
22
+ netbox_authorized_keys/migrations/0002_authorizedkey_full_name_and_more.py
23
+ netbox_authorized_keys/migrations/__init__.py
24
+ netbox_authorized_keys/models/__init__.py
25
+ netbox_authorized_keys/models/authorizedkey.py
26
+ netbox_authorized_keys/tables/__init__.py
27
+ netbox_authorized_keys/tables/authorizedkey.py
28
+ netbox_authorized_keys/views/__init__.py
29
+ netbox_authorized_keys/views/authorizedkey.py
@@ -0,0 +1 @@
1
+ netbox_authorized_keys
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,44 @@
1
+ import codecs
2
+ import os.path
3
+
4
+ from setuptools import find_packages, setup
5
+
6
+ with open("README.md", "r") as fh:
7
+ long_description = fh.read()
8
+
9
+
10
+ def read(rel_path):
11
+ here = os.path.abspath(os.path.dirname(__file__))
12
+ with codecs.open(os.path.join(here, rel_path), "r") as fp:
13
+ return fp.read()
14
+
15
+
16
+ def get_config_param(rel_path, variable_name):
17
+ for line in read(rel_path).splitlines():
18
+ if line.startswith(variable_name):
19
+ delim = '"' if '"' in line else "'"
20
+ return line.split(delim)[1]
21
+ else:
22
+ raise RuntimeError(f"Unable to find {variable_name} string.")
23
+
24
+
25
+ def get_version(rel_path):
26
+ for line in read(rel_path).splitlines():
27
+ if line.startswith("__version__"):
28
+ delim = '"' if '"' in line else "'"
29
+ return line.split(delim)[1]
30
+ else:
31
+ raise RuntimeError("Unable to find version string.")
32
+
33
+
34
+ setup(
35
+ name=get_config_param("netbox_authorized_keys/version.py", "__name__"),
36
+ version=get_version("netbox_authorized_keys/version.py"),
37
+ description=get_config_param("netbox_authorized_keys/version.py", "__description__"),
38
+ author=get_config_param("netbox_authorized_keys/version.py", "__author__"),
39
+ license="MIT",
40
+ install_requires=[],
41
+ packages=find_packages(),
42
+ include_package_data=True,
43
+ zip_safe=False,
44
+ )