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.
- netbox_user_credentials_plugin/__init__.py +16 -0
- netbox_user_credentials_plugin/api/__init__.py +6 -0
- netbox_user_credentials_plugin/api/serializers.py +38 -0
- netbox_user_credentials_plugin/api/urls.py +21 -0
- netbox_user_credentials_plugin/api/views.py +20 -0
- netbox_user_credentials_plugin/filtersets.py +20 -0
- netbox_user_credentials_plugin/forms.py +128 -0
- netbox_user_credentials_plugin/migrations/0001_initial.py +34 -0
- netbox_user_credentials_plugin/migrations/0002_alter_usercredentials_options_and_more.py +76 -0
- netbox_user_credentials_plugin/migrations/0003_rename_usergroups_usercredentialgroups_and_more.py +25 -0
- netbox_user_credentials_plugin/migrations/0004_remove_usercredentials_groups_and_more.py +20 -0
- netbox_user_credentials_plugin/migrations/0005_alter_usercredentials_username.py +18 -0
- netbox_user_credentials_plugin/migrations/0006_alter_usercredentials_virtual_machine.py +20 -0
- netbox_user_credentials_plugin/migrations/0007_alter_usercredentials_username.py +18 -0
- netbox_user_credentials_plugin/migrations/0008_usercredentials_netbox_user_credentials_plugin_usercredentials_unique_usercredentials.py +20 -0
- netbox_user_credentials_plugin/migrations/0009_usercredentials_last_logon_and_more.py +23 -0
- netbox_user_credentials_plugin/migrations/0010_rename_last_logon_usercredentials_last_login_at_and_more.py +25 -0
- netbox_user_credentials_plugin/migrations/0011_alter_usercredentials_last_login_ip.py +20 -0
- netbox_user_credentials_plugin/migrations/__init__.py +7 -0
- netbox_user_credentials_plugin/models.py +83 -0
- netbox_user_credentials_plugin/navigation.py +28 -0
- netbox_user_credentials_plugin/search.py +71 -0
- netbox_user_credentials_plugin/tables.py +98 -0
- netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/usercredentials.html +43 -0
- netbox_user_credentials_plugin/templates/netbox_user_credentials_plugin/virtual_machine_user_credentials.html +5 -0
- netbox_user_credentials_plugin/urls.py +48 -0
- netbox_user_credentials_plugin/views.py +85 -0
- netbox_user_credentials_plugin-0.1.0.dist-info/METADATA +97 -0
- netbox_user_credentials_plugin-0.1.0.dist-info/RECORD +32 -0
- netbox_user_credentials_plugin-0.1.0.dist-info/WHEEL +5 -0
- netbox_user_credentials_plugin-0.1.0.dist-info/licenses/LICENSE +13 -0
- 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,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
|
+
]
|
netbox_user_credentials_plugin/migrations/0003_rename_usergroups_usercredentialgroups_and_more.py
ADDED
|
@@ -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,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,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
|
+

|
|
49
|
+
|
|
50
|
+
| Contact page - User Credentials | Virtual Machine page - User Credentials |
|
|
51
|
+
|:---:|:---:|
|
|
52
|
+
|  |  |
|
|
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,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
|