netbox-user-credentials-plugin 0.1.0__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.
Files changed (32) hide show
  1. netbox_user_credentials_plugin/__init__.py +16 -0
  2. netbox_user_credentials_plugin/api/__init__.py +6 -0
  3. netbox_user_credentials_plugin/api/serializers.py +38 -0
  4. netbox_user_credentials_plugin/api/urls.py +21 -0
  5. netbox_user_credentials_plugin/api/views.py +20 -0
  6. netbox_user_credentials_plugin/filtersets.py +20 -0
  7. netbox_user_credentials_plugin/forms.py +128 -0
  8. netbox_user_credentials_plugin/migrations/0001_initial.py +34 -0
  9. netbox_user_credentials_plugin/migrations/0002_alter_usercredentials_options_and_more.py +76 -0
  10. netbox_user_credentials_plugin/migrations/0003_rename_usergroups_usercredentialgroups_and_more.py +25 -0
  11. netbox_user_credentials_plugin/migrations/0004_remove_usercredentials_groups_and_more.py +20 -0
  12. netbox_user_credentials_plugin/migrations/0005_alter_usercredentials_username.py +18 -0
  13. netbox_user_credentials_plugin/migrations/0006_alter_usercredentials_virtual_machine.py +20 -0
  14. netbox_user_credentials_plugin/migrations/0007_alter_usercredentials_username.py +18 -0
  15. netbox_user_credentials_plugin/migrations/0008_usercredentials_netbox_user_credentials_plugin_usercredentials_unique_usercredentials.py +20 -0
  16. netbox_user_credentials_plugin/migrations/0009_usercredentials_last_logon_and_more.py +23 -0
  17. netbox_user_credentials_plugin/migrations/0010_rename_last_logon_usercredentials_last_login_at_and_more.py +25 -0
  18. netbox_user_credentials_plugin/migrations/0011_alter_usercredentials_last_login_ip.py +20 -0
  19. netbox_user_credentials_plugin/migrations/__init__.py +7 -0
  20. netbox_user_credentials_plugin/models.py +83 -0
  21. netbox_user_credentials_plugin/navigation.py +28 -0
  22. netbox_user_credentials_plugin/search.py +71 -0
  23. netbox_user_credentials_plugin/tables.py +98 -0
  24. netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/usercredentials.html +43 -0
  25. netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/virtual_machine_user_credentials.html +5 -0
  26. netbox_user_credentials_plugin/urls.py +48 -0
  27. netbox_user_credentials_plugin/views.py +85 -0
  28. netbox_user_credentials_plugin-0.1.0.dist-info/METADATA +97 -0
  29. netbox_user_credentials_plugin-0.1.0.dist-info/RECORD +32 -0
  30. netbox_user_credentials_plugin-0.1.0.dist-info/WHEEL +5 -0
  31. netbox_user_credentials_plugin-0.1.0.dist-info/licenses/LICENSE +13 -0
  32. netbox_user_credentials_plugin-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,16 @@
1
+ from netbox.plugins import PluginConfig
2
+
3
+
4
+ class UserCredentialsConfig(PluginConfig):
5
+ name = "netbox_user_credentials_plugin"
6
+ verbose_name = "NetBox User Credentials"
7
+ description = "NetBox plugin for managing virtual machine users credentials"
8
+ author = "Dmitry Konyushenko"
9
+ author_email = "konyushenko.dmitriy@gmail.com"
10
+ version = "0.1.0"
11
+ base_url = "netbox_user_credentials_plugin"
12
+ min_version = "4.5.0"
13
+ max_version = "4.6.2"
14
+
15
+
16
+ config = UserCredentialsConfig
@@ -0,0 +1,6 @@
1
+ """
2
+ REST API for NetBox User Credentials Plugin.
3
+
4
+ For more information on developing NetBox REST APIs, see:
5
+ https://docs.netbox.dev/en/stable/plugins/development/rest-api/
6
+ """
@@ -0,0 +1,38 @@
1
+ """
2
+ API serializers for NetBox User Credentials Plugin.
3
+
4
+ Serializers are required for NetBox event handling (webhooks, change logging).
5
+ They also power the REST API endpoints.
6
+
7
+ For more information on NetBox REST API serializers, see:
8
+ https://docs.netbox.dev/en/stable/plugins/development/rest-api/#serializers
9
+
10
+ For Django REST Framework serializers, see:
11
+ https://www.django-rest-framework.org/api-guide/serializers/
12
+ """
13
+
14
+ from netbox.api.serializers import NetBoxModelSerializer
15
+ from rest_framework import serializers
16
+
17
+ from ..models import UserCredentials
18
+
19
+
20
+ class UserCredentialsSerializer(NetBoxModelSerializer):
21
+ url = serializers.HyperlinkedIdentityField(
22
+ view_name="plugins-api:netbox_user_credentials_plugin-api:usercredentials-detail"
23
+ )
24
+
25
+ class Meta:
26
+ model = UserCredentials
27
+ fields = ("id",
28
+ "url",
29
+ "display",
30
+ "username",
31
+ "virtual_machine",
32
+ "contact",
33
+ "password_valid_from",
34
+ "password_valid_to",
35
+ "tags",
36
+ "custom_fields",
37
+ "created",
38
+ "last_updated")
@@ -0,0 +1,21 @@
1
+ """
2
+ API URL patterns for NetBox User Credentials Plugin.
3
+
4
+ For more information on NetBox REST API routing, see:
5
+ https://docs.netbox.dev/en/stable/plugins/development/rest-api/#routers
6
+
7
+ For Django REST Framework routers, see:
8
+ https://www.django-rest-framework.org/api-guide/routers/
9
+ """
10
+
11
+ from netbox.api.routers import NetBoxRouter
12
+
13
+ from .views import UserCredentialsViewSet
14
+
15
+ app_name = "netbox_user_credentials_plugin"
16
+
17
+ router = NetBoxRouter()
18
+ router.register("user-credentials", UserCredentialsViewSet)
19
+
20
+ urlpatterns = router.urls
21
+
@@ -0,0 +1,20 @@
1
+ """
2
+ API viewsets for NetBox User Credentials Plugin.
3
+
4
+ For more information on NetBox REST API viewsets, see:
5
+ https://docs.netbox.dev/en/stable/plugins/development/rest-api/#viewsets
6
+
7
+ For Django REST Framework viewsets, see:
8
+ https://www.django-rest-framework.org/api-guide/viewsets/
9
+ """
10
+
11
+ from netbox.api.viewsets import NetBoxModelViewSet
12
+
13
+ from ..models import UserCredentials
14
+ from .serializers import UserCredentialsSerializer
15
+
16
+
17
+ class UserCredentialsViewSet(NetBoxModelViewSet):
18
+ queryset = UserCredentials.objects.all()
19
+ serializer_class = UserCredentialsSerializer
20
+
@@ -0,0 +1,20 @@
1
+ from netbox.filtersets import NetBoxModelFilterSet
2
+
3
+ from .models import UserCredentials
4
+
5
+
6
+ class UserCredentialsFilterSet(NetBoxModelFilterSet):
7
+ class Meta:
8
+ model = UserCredentials
9
+ fields = [
10
+ "username",
11
+ "virtual_machine",
12
+ "contact",
13
+ "password_valid_from",
14
+ "password_valid_to",
15
+ "last_login_at",
16
+ "last_login_ip",
17
+ ]
18
+
19
+ def search(self, queryset, username, value):
20
+ return queryset.filter(username__icontains=value)
@@ -0,0 +1,128 @@
1
+ from django import forms
2
+ from netbox.forms import (
3
+ NetBoxModelBulkEditForm,
4
+ NetBoxModelFilterSetForm,
5
+ NetBoxModelForm,
6
+ )
7
+ from tenancy.models import Contact
8
+ from utilities.forms.fields import (
9
+ DynamicModelChoiceField,
10
+ DynamicModelMultipleChoiceField,
11
+ )
12
+ from utilities.forms.widgets import DatePicker
13
+ from virtualization.models import VirtualMachine
14
+ from ipam.models import IPAddress
15
+
16
+ from .models import UserCredentials
17
+
18
+
19
+ class UserCredentialsForm(NetBoxModelForm):
20
+
21
+ class Meta:
22
+ model = UserCredentials
23
+ fields = (
24
+ "username",
25
+ "virtual_machine",
26
+ "contact",
27
+ "password_valid_from",
28
+ "password_valid_to",
29
+ "last_login_at",
30
+ "last_login_ip",
31
+ "comments",
32
+ )
33
+ widgets = {
34
+ "password_valid_from": DatePicker(),
35
+ "password_valid_to": DatePicker(),
36
+ "last_login_at": DatePicker(),
37
+ }
38
+
39
+
40
+ class UserCredentialsFilterForm(NetBoxModelFilterSetForm):
41
+ model = UserCredentials
42
+
43
+ username = forms.CharField(
44
+ required=False,
45
+ label="Username",
46
+ )
47
+
48
+ virtual_machine = DynamicModelMultipleChoiceField(
49
+ queryset=VirtualMachine.objects.all(),
50
+ required=False,
51
+ )
52
+
53
+ contact = DynamicModelMultipleChoiceField(
54
+ queryset=Contact.objects.all(),
55
+ required=False,
56
+ )
57
+
58
+ last_login_ip = DynamicModelMultipleChoiceField(
59
+ queryset=IPAddress.objects.all(),
60
+ required=False,
61
+ )
62
+
63
+ password_valid_from = forms.DateField(
64
+ required=False,
65
+ widget=DatePicker(),
66
+ )
67
+
68
+ password_valid_to = forms.DateField(
69
+ required=False,
70
+ widget=DatePicker(),
71
+ )
72
+
73
+ last_login_at = forms.DateField(
74
+ required=False,
75
+ widget=DatePicker(),
76
+ )
77
+
78
+
79
+ class UserCredentialsBulkEditForm(NetBoxModelBulkEditForm):
80
+
81
+ username = forms.CharField(
82
+ required=False,
83
+ )
84
+
85
+ virtual_machine = DynamicModelChoiceField(
86
+ queryset=VirtualMachine.objects.all(),
87
+ required=False,
88
+ )
89
+
90
+ contact = DynamicModelChoiceField(
91
+ queryset=Contact.objects.all(),
92
+ required=False,
93
+ )
94
+
95
+ password_valid_from = forms.DateField(
96
+ required=False,
97
+ widget=DatePicker(),
98
+ )
99
+
100
+ password_valid_to = forms.DateField(
101
+ required=False,
102
+ widget=DatePicker(),
103
+ )
104
+
105
+ last_login_at = forms.DateField(
106
+ required=False,
107
+ widget=DatePicker(),
108
+ )
109
+
110
+ last_login_ip = forms.GenericIPAddressField(
111
+ required=False,
112
+ )
113
+
114
+ comments = forms.CharField(
115
+ required=False,
116
+ widget=forms.Textarea,
117
+ )
118
+
119
+ model = UserCredentials
120
+
121
+ nullable_fields = (
122
+ "contact",
123
+ "password_valid_from",
124
+ "password_valid_to",
125
+ "last_login_at",
126
+ "last_login_ip",
127
+ "comments",
128
+ )
@@ -0,0 +1,34 @@
1
+ # Generated by Django 6.0.5 on 2026-06-14 20:13
2
+
3
+ import netbox.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
+ ('extras', '0138_customfieldchoiceset_choice_colors'),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name='Usercredentials',
20
+ fields=[
21
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
22
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
23
+ ('last_updated', models.DateTimeField(auto_now=True, null=True)),
24
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
25
+ ('name', models.CharField(max_length=100, unique=True)),
26
+ ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
27
+ ],
28
+ options={
29
+ 'verbose_name_plural': 'Usercredentialss',
30
+ 'ordering': ('name',),
31
+ },
32
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
33
+ ),
34
+ ]
@@ -0,0 +1,76 @@
1
+ # Generated by Django 6.0.5 on 2026-06-15 18:06
2
+
3
+ import django.db.models.deletion
4
+ import netbox.models.deletion
5
+ import taggit.managers
6
+ import utilities.json
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+
12
+ dependencies = [
13
+ ('extras', '0138_customfieldchoiceset_choice_colors'),
14
+ ('netbox_user_credentials_plugin', '0001_initial'),
15
+ ('tenancy', '0024_default_ordering_indexes'),
16
+ ('virtualization', '0056_virtualmachine_render_config_permission'),
17
+ ]
18
+
19
+ operations = [
20
+ migrations.AlterModelOptions(
21
+ name='usercredentials',
22
+ options={'ordering': ('username',)},
23
+ ),
24
+ migrations.RenameField(
25
+ model_name='usercredentials',
26
+ old_name='name',
27
+ new_name='username',
28
+ ),
29
+ migrations.AddField(
30
+ model_name='usercredentials',
31
+ name='comments',
32
+ field=models.TextField(blank=True),
33
+ ),
34
+ migrations.AddField(
35
+ model_name='usercredentials',
36
+ name='contact',
37
+ field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_credentials_contact', to='tenancy.contact'),
38
+ ),
39
+ migrations.AddField(
40
+ model_name='usercredentials',
41
+ name='password_valid_from',
42
+ field=models.DateField(blank=True, default=None, null=True),
43
+ ),
44
+ migrations.AddField(
45
+ model_name='usercredentials',
46
+ name='password_valid_to',
47
+ field=models.DateField(blank=True, default=None, null=True),
48
+ ),
49
+ migrations.AddField(
50
+ model_name='usercredentials',
51
+ name='virtual_machine',
52
+ field=models.OneToOneField(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='user_credentials_virtual_machine', to='virtualization.virtualmachine'),
53
+ preserve_default=False,
54
+ ),
55
+ migrations.CreateModel(
56
+ name='UserGroups',
57
+ fields=[
58
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
59
+ ('created', models.DateTimeField(auto_now_add=True, null=True)),
60
+ ('last_updated', models.DateTimeField(auto_now=True, null=True)),
61
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
62
+ ('name', models.CharField(max_length=100, unique=True)),
63
+ ('comments', models.TextField(blank=True)),
64
+ ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
65
+ ],
66
+ options={
67
+ 'abstract': False,
68
+ },
69
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
70
+ ),
71
+ migrations.AddField(
72
+ model_name='usercredentials',
73
+ name='groups',
74
+ field=models.ManyToManyField(blank=True, related_name='user_credentials_groups', to='netbox_user_credentials_plugin.usergroups'),
75
+ ),
76
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 6.0.5 on 2026-06-16 18:40
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('extras', '0138_customfieldchoiceset_choice_colors'),
11
+ ('netbox_user_credentials_plugin', '0002_alter_usercredentials_options_and_more'),
12
+ ('tenancy', '0024_default_ordering_indexes'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.RenameModel(
17
+ old_name='UserGroups',
18
+ new_name='UserCredentialGroups',
19
+ ),
20
+ migrations.AlterField(
21
+ model_name='usercredentials',
22
+ name='contact',
23
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_credentials_contact', to='tenancy.contact'),
24
+ ),
25
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 6.0.5 on 2026-06-17 19:35
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_user_credentials_plugin', '0003_rename_usergroups_usercredentialgroups_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='usercredentials',
15
+ name='groups',
16
+ ),
17
+ migrations.DeleteModel(
18
+ name='UserCredentialGroups',
19
+ ),
20
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 6.0.5 on 2026-06-17 19:41
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_user_credentials_plugin', '0004_remove_usercredentials_groups_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='usercredentials',
15
+ name='username',
16
+ field=models.CharField(max_length=101, unique=True),
17
+ ),
18
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 6.0.5 on 2026-06-18 19:49
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('netbox_user_credentials_plugin', '0005_alter_usercredentials_username'),
11
+ ('virtualization', '0056_virtualmachine_render_config_permission'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name='usercredentials',
17
+ name='virtual_machine',
18
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_credentials_virtual_machine', to='virtualization.virtualmachine'),
19
+ ),
20
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 6.0.5 on 2026-06-21 10:03
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_user_credentials_plugin', '0006_alter_usercredentials_virtual_machine'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='usercredentials',
15
+ name='username',
16
+ field=models.CharField(max_length=101),
17
+ ),
18
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 6.0.5 on 2026-06-21 18:29
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('extras', '0138_customfieldchoiceset_choice_colors'),
10
+ ('netbox_user_credentials_plugin', '0007_alter_usercredentials_username'),
11
+ ('tenancy', '0024_default_ordering_indexes'),
12
+ ('virtualization', '0056_virtualmachine_render_config_permission'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddConstraint(
17
+ model_name='usercredentials',
18
+ constraint=models.UniqueConstraint(fields=('username', 'virtual_machine'), name='netbox_user_credentials_plugin_usercredentials_unique_usercredentials'),
19
+ ),
20
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 6.0.5 on 2026-06-22 16:50
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('netbox_user_credentials_plugin', '0008_usercredentials_netbox_user_credentials_plugin_usercredentials_unique_usercredentials'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='usercredentials',
15
+ name='last_logon',
16
+ field=models.DateField(blank=True, default=None, null=True),
17
+ ),
18
+ migrations.AlterField(
19
+ model_name='usercredentials',
20
+ name='username',
21
+ field=models.CharField(max_length=100),
22
+ ),
23
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 6.0.5 on 2026-06-22 17:18
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('ipam', '0090_vlangroup_recompute_total_vlan_ids'),
11
+ ('netbox_user_credentials_plugin', '0009_usercredentials_last_logon_and_more'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.RenameField(
16
+ model_name='usercredentials',
17
+ old_name='last_logon',
18
+ new_name='last_login_at',
19
+ ),
20
+ migrations.AddField(
21
+ model_name='usercredentials',
22
+ name='last_login_ip',
23
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_credentials_contact', to='ipam.ipaddress'),
24
+ ),
25
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 6.0.5 on 2026-06-22 17:24
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('ipam', '0090_vlangroup_recompute_total_vlan_ids'),
11
+ ('netbox_user_credentials_plugin', '0010_rename_last_logon_usercredentials_last_login_at_and_more'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name='usercredentials',
17
+ name='last_login_ip',
18
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_credentials_last_login_ip', to='ipam.ipaddress'),
19
+ ),
20
+ ]
@@ -0,0 +1,7 @@
1
+ """
2
+ Django migrations for NetBox User Credentials Plugin.
3
+
4
+ This directory contains database migrations for the plugin.
5
+ For more information on Django migrations, see:
6
+ https://docs.djangoproject.com/en/stable/topics/migrations/
7
+ """
@@ -0,0 +1,83 @@
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 tenancy.models import *
6
+
7
+
8
+ class UserCredentials(NetBoxModel):
9
+ username = models.CharField(
10
+ max_length=100, verbose_name=_("Username"), help_text=_("System login username")
11
+ )
12
+ virtual_machine = models.ForeignKey(
13
+ to="virtualization.VirtualMachine",
14
+ on_delete=models.CASCADE,
15
+ related_name="user_credentials_virtual_machine",
16
+ verbose_name=_("Virtual Machine"),
17
+ help_text=_("Virtual machine associated with these credentials"),
18
+ )
19
+ contact = models.ForeignKey(
20
+ to="tenancy.Contact",
21
+ on_delete=models.SET_NULL,
22
+ blank=True,
23
+ null=True,
24
+ default=None,
25
+ related_name="user_credentials_contact",
26
+ verbose_name=_("Contact"),
27
+ help_text=_("Associated contact entity"),
28
+ )
29
+ password_valid_from = models.DateField(
30
+ blank=True,
31
+ null=True,
32
+ default=None,
33
+ verbose_name=_("Password valid from"),
34
+ help_text=_("Date from which password becomes valid"),
35
+ )
36
+ password_valid_to = models.DateField(
37
+ blank=True,
38
+ null=True,
39
+ default=None,
40
+ verbose_name=_("Password valid to"),
41
+ help_text=_("Last logon time"),
42
+ )
43
+ last_login_at = models.DateField(
44
+ blank=True,
45
+ null=True,
46
+ default=None,
47
+ verbose_name=_("Last login timestamp"),
48
+ help_text=_("Date until which password is valid"),
49
+ )
50
+ last_login_ip = models.ForeignKey(
51
+ to="ipam.IPAddress",
52
+ on_delete=models.SET_NULL,
53
+ blank=True,
54
+ null=True,
55
+ default=None,
56
+ related_name="user_credentials_last_login_ip",
57
+ verbose_name=_("Last login IP"),
58
+ help_text=_("Last login IP"),
59
+ )
60
+ comments = models.TextField(
61
+ blank=True,
62
+ verbose_name=_("Comments"),
63
+ help_text=_("Additional notes or remarks"),
64
+ )
65
+
66
+ class Meta:
67
+ app_label = "netbox_user_credentials_plugin"
68
+ ordering = ("username",)
69
+ verbose_name_plural = _("User credentials")
70
+ constraints = [
71
+ models.UniqueConstraint(
72
+ fields=["username", "virtual_machine"],
73
+ name="%(app_label)s_%(class)s_unique_usercredentials",
74
+ )
75
+ ]
76
+
77
+ def get_absolute_url(self):
78
+ return reverse(
79
+ "plugins:netbox_user_credentials_plugin:usercredentials", args=[self.pk]
80
+ )
81
+
82
+ def __str__(self):
83
+ return self.username
@@ -0,0 +1,28 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from netbox.plugins import PluginMenu, PluginMenuButton, PluginMenuItem
3
+
4
+ plugin_buttons = [
5
+ PluginMenuButton(
6
+ link="plugins:netbox_user_credentials_plugin:usercredentials_add",
7
+ title=_("Add"),
8
+ icon_class="mdi mdi-plus-thick",
9
+ )
10
+ ]
11
+
12
+ usercredentials_item = PluginMenuItem(
13
+ link="plugins:netbox_user_credentials_plugin:usercredentials_list",
14
+ link_text=_("Users"),
15
+ buttons=plugin_buttons,
16
+ )
17
+
18
+
19
+ menu = PluginMenu(
20
+ label=_("User Credentials"),
21
+ groups=(
22
+ (
23
+ _("User Credentials"),
24
+ (usercredentials_item,),
25
+ ),
26
+ ),
27
+ icon_class="mdi mdi-card-account-details",
28
+ )
@@ -0,0 +1,71 @@
1
+ from netbox.search import SearchIndex
2
+
3
+ from .models import UserCredentials
4
+
5
+
6
+ class UserСredentialsIndex(SearchIndex):
7
+ """
8
+ Search index for Usercredentials model.
9
+
10
+ This enables Usercredentials objects to appear in NetBox's global
11
+ search results.
12
+ """
13
+
14
+ model = UserCredentials
15
+
16
+ # Fields to index for search with their weights
17
+ # Higher weight = higher priority in search results
18
+ #
19
+ # Weight Guidelines:
20
+ # 50 - Unique serialized attribute (e.g., asset_tag)
21
+ # 60 - Unique per related object (e.g., serial)
22
+ # 100 - Primary human identifier (e.g., name)
23
+ # 110 - Slug fields
24
+ # 200 - Secondary identifier
25
+ # 300 - Highly unique descriptive text
26
+ # 500 - Description field
27
+ # 1000 - Custom field default
28
+ # 2000 - Other discrete attributes
29
+ # 5000 - Comments field
30
+ fields = (
31
+ ("username", 100), # Primary identifier
32
+ # ('slug', 110), # Uncomment if your model has a slug field
33
+ # ('description', 500), # Uncomment if your model has a description field
34
+ # ('comments', 5000), # Uncomment if your model has a comments field
35
+ )
36
+
37
+ # Optional: Fields to display in search results (not indexed, just shown)
38
+ # These help users identify the correct result
39
+ # Foreign key fields are automatically prefetched for efficiency
40
+ display_attrs = (
41
+ # 'status', # Example: Display status
42
+ # 'tenant', # Example: Display tenant relationship
43
+ # 'description', # Example: Display description
44
+ )
45
+
46
+ # Optional: Custom category label for grouping in search UI
47
+ # If not specified, defaults to the app's verbose name
48
+ # category = 'NetBox User Credentials Plugin'
49
+
50
+
51
+ # Register all search indexes for this plugin
52
+ # The PluginConfig will automatically load these indexes
53
+ indexes = (UserСredentialsIndex,)
54
+
55
+
56
+ # Example: Multiple models with different search configurations
57
+ #
58
+ # class AnotherModelIndex(SearchIndex):
59
+ # """Search index for another model."""
60
+ # model = AnotherModel
61
+ # fields = (
62
+ # ('name', 100),
63
+ # ('identifier', 200),
64
+ # ('description', 500),
65
+ # )
66
+ # display_attrs = ('status', 'type')
67
+ #
68
+ # indexes = (
69
+ # UsercredentialsIndex,
70
+ # AnotherModelIndex,
71
+ # )
@@ -0,0 +1,98 @@
1
+ import django_tables2 as tables
2
+ from netbox.tables import NetBoxTable, columns
3
+
4
+ from .models import UserCredentials
5
+
6
+
7
+ class UserCredentialsTable(NetBoxTable):
8
+ username = tables.Column(linkify=True)
9
+ virtual_machine = tables.Column(linkify=True)
10
+ contact = tables.Column(linkify=True)
11
+ last_login_ip = tables.Column(linkify=True)
12
+ tags = columns.TagColumn()
13
+ comments = columns.MarkdownColumn()
14
+
15
+ class Meta(NetBoxTable.Meta):
16
+ model = UserCredentials
17
+ fields = (
18
+ "pk",
19
+ "id",
20
+ "username",
21
+ "virtual_machine",
22
+ "contact",
23
+ "password_valid_from",
24
+ "password_valid_to",
25
+ "last_login_at",
26
+ "last_login_ip",
27
+ "comments",
28
+ "tags",
29
+ "created",
30
+ "last_updated",
31
+ )
32
+ default_columns = (
33
+ "username",
34
+ "virtual_machine",
35
+ "password_valid_from",
36
+ "password_valid_to",
37
+ )
38
+
39
+
40
+ class VirtualMachineUserCredentialsTable(NetBoxTable):
41
+ username = tables.Column(linkify=True)
42
+ contact = tables.Column(linkify=True)
43
+ last_login_ip = tables.Column(linkify=True)
44
+ tags = columns.TagColumn()
45
+ comments = columns.MarkdownColumn()
46
+
47
+ class Meta(NetBoxTable.Meta):
48
+ model = UserCredentials
49
+ fields = (
50
+ "pk",
51
+ "id",
52
+ "username",
53
+ "contact",
54
+ "password_valid_from",
55
+ "password_valid_to",
56
+ "last_login_at",
57
+ "last_login_ip",
58
+ "comments",
59
+ "tags",
60
+ "created",
61
+ "last_updated",
62
+ )
63
+ default_columns = (
64
+ "username",
65
+ "last_login_at",
66
+ "last_login_ip",
67
+ )
68
+
69
+
70
+ class ContactUserCredentialsTable(NetBoxTable):
71
+ username = tables.Column(linkify=True)
72
+ virtual_machine = tables.Column(linkify=True)
73
+ last_login_ip = tables.Column(linkify=True)
74
+ tags = columns.TagColumn()
75
+ comments = columns.MarkdownColumn()
76
+
77
+ class Meta(NetBoxTable.Meta):
78
+ model = UserCredentials
79
+ fields = (
80
+ "pk",
81
+ "id",
82
+ "username",
83
+ "virtual_machine",
84
+ "password_valid_from",
85
+ "password_valid_to",
86
+ "last_login_at",
87
+ "last_login_ip",
88
+ "comments",
89
+ "tags",
90
+ "created",
91
+ "last_updated",
92
+ )
93
+ default_columns = (
94
+ "username",
95
+ "virtual_machine",
96
+ "password_valid_from",
97
+ "password_valid_to",
98
+ )
@@ -0,0 +1,43 @@
1
+
2
+ {% extends 'generic/object.html' %}
3
+ {% load render_table from django_tables2 %}
4
+
5
+ {% block content %}
6
+ <div class="row mb-3">
7
+ <div class="col col-md-6">
8
+ <div class="card">
9
+
10
+ <h5 class="card-header">NetBox User Credentials Plugin</h5>
11
+
12
+ <table class="table table-hover attr-table">
13
+ <tr>
14
+ <th scope="row">Username</th>
15
+ <td>{{ object.username }}</td>
16
+ </tr>
17
+ <tr>
18
+ <th scope="row">Virtual Machine</th>
19
+ <td>{{ object.virtual_machine|linkify }}</td>
20
+ </tr>
21
+ <tr>
22
+ <th scope="row">Contact</th>
23
+ <td>{{ object.contact|linkify }}</td>
24
+ </tr>
25
+ <tr>
26
+ <th scope="row">Password valid from</th>
27
+ <td>{{ object.password_valid_from|placeholder }}</td>
28
+ </tr>
29
+ <tr>
30
+ <th scope="row">Password valid to</th>
31
+ <td>{{ object.password_valid_to|placeholder }}</td>
32
+ </tr>
33
+ </table>
34
+ </div>
35
+ {% include 'inc/panels/custom_fields.html' %}
36
+ </div>
37
+ <div class="col col-md-6">
38
+ {% include 'inc/panels/tags.html' %}
39
+ {% include 'inc/panels/comments.html' %}
40
+ </div>
41
+ </div>
42
+ {% endblock content %}
43
+
@@ -0,0 +1,5 @@
1
+ {% extends 'generic/object_children.html' %}
2
+
3
+ {% block content %}
4
+ {{ block.super }}
5
+ {% endblock %}
@@ -0,0 +1,48 @@
1
+ from django.urls import path
2
+ from netbox.views.generic import ObjectChangeLogView
3
+
4
+ from . import models, views
5
+
6
+ urlpatterns = (
7
+ path(
8
+ "user-credentials/",
9
+ views.UserCredentialsListView.as_view(),
10
+ name="usercredentials_list",
11
+ ),
12
+ path(
13
+ "user-credentials/add/",
14
+ views.UserCredentialsEditView.as_view(),
15
+ name="usercredentials_add",
16
+ ),
17
+ path(
18
+ "user-credentials/edit/",
19
+ views.UserCredentialsBulkEditView.as_view(),
20
+ name="usercredentials_bulk_edit",
21
+ ),
22
+ path(
23
+ "user-credentials/delete/",
24
+ views.UserCredentialsBulkDeleteView.as_view(),
25
+ name="usercredentials_bulk_delete",
26
+ ),
27
+ path(
28
+ "user-credentials/<int:pk>/",
29
+ views.UserCredentialsView.as_view(),
30
+ name="usercredentials",
31
+ ),
32
+ path(
33
+ "user-credentials/<int:pk>/edit/",
34
+ views.UserCredentialsEditView.as_view(),
35
+ name="usercredentials_edit",
36
+ ),
37
+ path(
38
+ "user-credentials/<int:pk>/delete/",
39
+ views.UserCredentialsDeleteView.as_view(),
40
+ name="usercredentials_delete",
41
+ ),
42
+ path(
43
+ "usercredentials/<int:pk>/changelog/",
44
+ ObjectChangeLogView.as_view(),
45
+ name="usercredentials_changelog",
46
+ kwargs={"model": models.UserCredentials},
47
+ ),
48
+ )
@@ -0,0 +1,85 @@
1
+ from netbox.views import generic
2
+ from tenancy.models import Contact
3
+ from utilities.views import ViewTab, register_model_view
4
+ from virtualization.models import VirtualMachine
5
+
6
+ from . import filtersets, forms, models, tables
7
+
8
+
9
+ @register_model_view(models.UserCredentials)
10
+ class UserCredentialsView(generic.ObjectView):
11
+ queryset = models.UserCredentials.objects.all()
12
+
13
+
14
+ @register_model_view(models.UserCredentials, name="list", path="", detail=False)
15
+ class UserCredentialsListView(generic.ObjectListView):
16
+ queryset = models.UserCredentials.objects.all()
17
+ table = tables.UserCredentialsTable
18
+ filterset = filtersets.UserCredentialsFilterSet
19
+ filterset_form = forms.UserCredentialsFilterForm
20
+
21
+
22
+ @register_model_view(VirtualMachine, name="usercredentials", path="user-credentials")
23
+ class VirtualMachineCredentialsView(generic.ObjectChildrenView):
24
+ queryset = VirtualMachine.objects.all()
25
+
26
+ child_model = models.UserCredentials
27
+ table = tables.VirtualMachineUserCredentialsTable
28
+ filterset = filtersets.UserCredentialsFilterSet
29
+
30
+ tab = ViewTab(
31
+ label="User Credentials",
32
+ badge=lambda obj: models.UserCredentials.objects.filter(
33
+ virtual_machine=obj
34
+ ).count(),
35
+ hide_if_empty=False,
36
+ )
37
+
38
+ def get_children(self, request, parent):
39
+ qs = models.UserCredentials.objects.filter(virtual_machine=parent)
40
+
41
+ return self.filterset(request.GET, qs).qs
42
+
43
+
44
+ @register_model_view(Contact, name="usercredentials", path="user-credentials")
45
+ class ContactCredentialsView(generic.ObjectChildrenView):
46
+ queryset = Contact.objects.all()
47
+
48
+ child_model = models.UserCredentials
49
+ table = tables.ContactUserCredentialsTable
50
+ filterset = filtersets.UserCredentialsFilterSet
51
+
52
+ tab = ViewTab(
53
+ label="User Credentials",
54
+ badge=lambda obj: models.UserCredentials.objects.filter(contact=obj).count(),
55
+ hide_if_empty=False,
56
+ )
57
+
58
+ def get_children(self, request, parent):
59
+ qs = models.UserCredentials.objects.filter(contact=parent)
60
+
61
+ return self.filterset(request.GET, qs).qs
62
+
63
+
64
+ @register_model_view(models.UserCredentials, name="add", detail=False)
65
+ @register_model_view(models.UserCredentials, name="edit")
66
+ class UserCredentialsEditView(generic.ObjectEditView):
67
+ queryset = models.UserCredentials.objects.all()
68
+ form = forms.UserCredentialsForm
69
+
70
+
71
+ @register_model_view(models.UserCredentials, name="delete")
72
+ class UserCredentialsDeleteView(generic.ObjectDeleteView):
73
+ queryset = models.UserCredentials.objects.all()
74
+
75
+
76
+ class UserCredentialsBulkDeleteView(generic.BulkDeleteView):
77
+ queryset = models.UserCredentials.objects.all()
78
+ table = tables.UserCredentialsTable
79
+
80
+
81
+ class UserCredentialsBulkEditView(generic.BulkEditView):
82
+ queryset = models.UserCredentials.objects.all()
83
+ form = forms.UserCredentialsBulkEditForm
84
+ table = tables.UserCredentialsTable
85
+ filterset = filtersets.UserCredentialsFilterSet
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: netbox-user-credentials-plugin
3
+ Version: 0.1.0
4
+ Summary: NetBox plugin for User Credentials
5
+ Author-email: Dmitry Konyushenko <konyushenko.dmitriy@gmail.com>
6
+ License-Expression: WTFPL
7
+ Project-URL: Homepage, https://github.com/ilyjorsey/netbox-user-credentials-plugin
8
+ Project-URL: Documentation, https://github.com/ilyjorsey/netbox-user-credentials-plugin/blob/main/README.md
9
+ Project-URL: Source, https://github.com/ilyjorsey/netbox-user-credentials-plugin
10
+ Project-URL: Tracker, https://github.com/ilyjorsey/netbox-user-credentials-plugin/issues
11
+ Keywords: netbox,netbox-plugin
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: Django
14
+ Classifier: Framework :: Django :: 5.1
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Intended Audience :: Telecommunications Industry
18
+ Classifier: Natural Language :: English
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: System :: Networking
24
+ Requires-Python: >=3.12.0
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: test
28
+ Requires-Dist: check-manifest==0.51; extra == "test"
29
+ Requires-Dist: ruff==0.14.14; extra == "test"
30
+ Requires-Dist: pre-commit==6.0.0; extra == "test"
31
+ Requires-Dist: pytest==9.0.2; extra == "test"
32
+ Dynamic: license-file
33
+
34
+ # NetBox User Credentials Plugin
35
+
36
+ NetBox plugin for managing User Credentials.
37
+
38
+ The standard NetBox VM model only allows contacts to be assigned. However, if you need to keep an inventory of users associated with virtual machines, including multiple users linked to the same contact, NetBox does not provide sufficient functionality out of the box. The User Credentials plugin helps solve this problem.
39
+
40
+ - Free software: WTFPL
41
+ - Documentation: https://ilyjorsey.github.io/netbox-user-credentials-plugin/
42
+
43
+ ## Screenshots
44
+
45
+
46
+ | User Credentials page |
47
+ |:---:|
48
+ ![User Credential](docs/images/uc_view.png)
49
+
50
+ | Contact page - User Credentials | Virtual Machine page - User Credentials |
51
+ |:---:|:---:|
52
+ | ![Contact User Credentials](docs/images/contact_uc_view.png) | ![Virtual Machine User Credentials](docs/images/vm_uc_view.png) |
53
+
54
+ ## Compatibility
55
+
56
+ This plugin requires **NetBox 4.5** or later.
57
+
58
+ | NetBox Version | Plugin Version |
59
+ |----------------|----------------|
60
+ | 4.5+ | 0.1.0 |
61
+
62
+ For more detailed compatibility information, see [COMPATIBILITY.md](COMPATIBILITY.md).
63
+
64
+ ## REST API
65
+
66
+ This plugin provides a REST API endpoint for managing User Credentials resources:
67
+
68
+ - `/api/plugins/netbox_user_credentials_plugin/user-credentials/` - List and create plugin objects
69
+
70
+
71
+
72
+ ## Installing
73
+
74
+ For adding to a NetBox Docker setup see
75
+ [the general instructions for using netbox-docker with plugins](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins).
76
+
77
+ Enable the plugin in `/opt/netbox/netbox/netbox/configuration.py`,
78
+ or if you use netbox-docker, your `/configuration/plugins.py` file :
79
+
80
+ ```python
81
+ PLUGINS = [
82
+ 'netbox_user_credentials_plugin'
83
+ ]
84
+
85
+ PLUGINS_CONFIG = {
86
+ "netbox_user_credentials_plugin": {},
87
+ }
88
+ ```
89
+
90
+ ## Usage
91
+
92
+ For detailed usage instructions, please refer to the [documentation](https://ilyjorsey.github.io/netbox-user-credentials-plugin/).
93
+
94
+
95
+ ### Feature Requests & Reporting Bugs
96
+
97
+ Feature Requests & Reporting Bugs can be submitted as [GitHub Issues](https://github.com/ilyjorsey/netbox-user-credentials-plugin/issues).
@@ -0,0 +1,32 @@
1
+ netbox_user_credentials_plugin/__init__.py,sha256=RwmA1Ozb5_ofRshGIoExDarYIO0q6azenMRm4ra1jr0,495
2
+ netbox_user_credentials_plugin/filtersets.py,sha256=GFpb34WtP8o7wrtESQUD3l4WWPLld7yQnBO3Ds4n20s,533
3
+ netbox_user_credentials_plugin/forms.py,sha256=7rNAJGP5MY5t-_s3eCWfOOo36g08zZrgrfxrKMxlajU,2862
4
+ netbox_user_credentials_plugin/models.py,sha256=N1XsuOfiDtE0z4V016slj8kqMEY3_5B5y3p1sJD84Ic,2597
5
+ netbox_user_credentials_plugin/navigation.py,sha256=GgISIJ8PIOXDNTEaaS1RDDJ5n0LHiG3Fwfhy2_e-nLg,701
6
+ netbox_user_credentials_plugin/search.py,sha256=e7JrdQ6YD_b-QJIJd0r06d0eEPNOgEMzGrQMsbb0cNU,2282
7
+ netbox_user_credentials_plugin/tables.py,sha256=aQlkrMyN0T_iLVOWOFi6ezGnY2Wf-EdvmSuIbeJ0cow,2617
8
+ netbox_user_credentials_plugin/urls.py,sha256=-scpxiWeXU2fxveTIOZSmHv7ojw8inKctjSzC5mVYiM,1320
9
+ netbox_user_credentials_plugin/views.py,sha256=VvY069geQbCXsledUGgQVHSClsfoWZVa50DQXF5cmMQ,2933
10
+ netbox_user_credentials_plugin/api/__init__.py,sha256=AqFpJz8ZzL96Yvmu2L4zu00frn3f4l1Lc6lvHDQvB4s,176
11
+ netbox_user_credentials_plugin/api/serializers.py,sha256=GwIbriW5_YKWshyKxUihnScZl_rVIfLl1nFhAcSCrRg,1123
12
+ netbox_user_credentials_plugin/api/urls.py,sha256=X3RuUj4lY_PiZV9qq33mq3Zpk7W7KvumuonBvis2xRY,532
13
+ netbox_user_credentials_plugin/api/views.py,sha256=baQHR4h7_ynmR8dX4L9aAYiVSNAWW9Fxe-cU8DezauM,574
14
+ netbox_user_credentials_plugin/migrations/0001_initial.py,sha256=VVmq-f1NUMyhys_ld6Hhp3KsNwOqaoHQjakEiLEVqX0,1233
15
+ netbox_user_credentials_plugin/migrations/0002_alter_usercredentials_options_and_more.py,sha256=Ua7XFFMJXhwB9TmiWv1JkYht2i7TtkcYZsnmaEd9RxY,3123
16
+ netbox_user_credentials_plugin/migrations/0003_rename_usergroups_usercredentialgroups_and_more.py,sha256=riQw2hPsWHZvMViPDFMC75DleWFNx3FuwyhHYxP8hcs,844
17
+ netbox_user_credentials_plugin/migrations/0004_remove_usercredentials_groups_and_more.py,sha256=zTz3KXeQrP6LMTtJtYUq8RFm-E4nuPk8XOFxzEiBgrQ,473
18
+ netbox_user_credentials_plugin/migrations/0005_alter_usercredentials_username.py,sha256=kYisqfjOAjYhH92Pn2P9zBzNtlLtpndYm9lqP99XOik,454
19
+ netbox_user_credentials_plugin/migrations/0006_alter_usercredentials_virtual_machine.py,sha256=cX4ulboGJ4jQzNVsoVB4bhqjNKmc57n3X2BmZCSdUOw,664
20
+ netbox_user_credentials_plugin/migrations/0007_alter_usercredentials_username.py,sha256=l9y-RsTBkkrk6Hqp3jDB3G2QiNh7cDzee2Ru2H-fV8g,440
21
+ netbox_user_credentials_plugin/migrations/0008_usercredentials_netbox_user_credentials_plugin_usercredentials_unique_usercredentials.py,sha256=aJTjXVtfbPO0d5rEbSLZAAznJROfTEMA0WpND7YrKKE,714
22
+ netbox_user_credentials_plugin/migrations/0009_usercredentials_last_logon_and_more.py,sha256=U9Xi_xKYNMJkDFLZWOR8Crpz5s90iOoYmgUb-GnmNZg,674
23
+ netbox_user_credentials_plugin/migrations/0010_rename_last_logon_usercredentials_last_login_at_and_more.py,sha256=YmzG2ChKWQZgPPlm0Y3blQvCyWkF_x7d5dcnI9m5igs,823
24
+ netbox_user_credentials_plugin/migrations/0011_alter_usercredentials_last_login_ip.py,sha256=dvhq7sKcRdiSHG2ctO84EYa8e7594-kPaRNlCEJC5rs,694
25
+ netbox_user_credentials_plugin/migrations/__init__.py,sha256=yBmHL8jr2-nqgxu8OcVgyWN0aqYKFSdfmteGAi7dXTo,231
26
+ netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/usercredentials.html,sha256=2cbE01av8X324495KSw680oWMH1cO1sZ2IltlILAxEQ,1236
27
+ netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/virtual_machine_user_credentials.html,sha256=EgIGZW__mV7NbnfWcxvqT5d-toVS5HSunXlQpa8z0Rw,100
28
+ netbox_user_credentials_plugin-0.1.0.dist-info/licenses/LICENSE,sha256=29F5Z4mEpyEsjtOcm4owWEMeALuucOJlFEY0NpdqcyA,506
29
+ netbox_user_credentials_plugin-0.1.0.dist-info/METADATA,sha256=t_afC7P9lIG1UcM0uQXdNgA8UuvN_td7ykDUgfuVEjY,3566
30
+ netbox_user_credentials_plugin-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
31
+ netbox_user_credentials_plugin-0.1.0.dist-info/top_level.txt,sha256=TXlUuLfQZMNpDhNEhLwO7-4CWp0rnd6AZEvIrGfvfD8,31
32
+ netbox_user_credentials_plugin-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2026 Dmitry Konyushenko <konyushenko.dmitriy@gmail.com>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
@@ -0,0 +1 @@
1
+ netbox_user_credentials_plugin