From b57d29acef49d76d7677628c40620c5abfb361dc Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Tue, 4 Jan 2022 10:03:55 -0600 Subject: [PATCH 01/10] PVRB --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index acdd904..33a1968 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox-secretstore', - version='1.0.13', + version='1.0.14', description='Netbox Secret Store', long_description='A Secret store for NetBox', url='https://github.com/dansheps/netbox-secretstore/', From 13212e89be22760d6527ba9e7e630ffbaec933cf Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Tue, 4 Jan 2022 10:08:27 -0600 Subject: [PATCH 02/10] PVRB --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 33a1968..82493e7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox-secretstore', - version='1.0.14', + version='1.0.15', description='Netbox Secret Store', long_description='A Secret store for NetBox', url='https://github.com/dansheps/netbox-secretstore/', From d4e1137451eb27d46eb007e09f5cd12208ceeba1 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Sat, 8 Jan 2022 00:10:08 -0600 Subject: [PATCH 03/10] 3.2 compatability --- netbox_secretstore/navigation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/netbox_secretstore/navigation.py b/netbox_secretstore/navigation.py index fd1f37d..02ed0be 100644 --- a/netbox_secretstore/navigation.py +++ b/netbox_secretstore/navigation.py @@ -10,13 +10,13 @@ link="plugins:netbox_secretstore:secretrole_add", title="Add Secret Role", icon_class="mdi mdi-plus-thick", - color="success", + color="green", ), PluginMenuButton( link="plugins:netbox_secretstore:secretrole_import", title="Import Secret Role", icon_class="mdi mdi-upload", - color="info", + color="teal", ), ), ), @@ -28,13 +28,13 @@ link="plugins:netbox_secretstore:secret_add", title="Add Secret", icon_class="mdi mdi-plus-thick", - color="success", + color="green", ), PluginMenuButton( link="plugins:netbox_secretstore:secret_import", title="Import Secret", icon_class="mdi mdi-upload", - color="info", + color="teal", ), ), ), From 194e3028c4b46e78ca71c9887fd7bdf535ca80ec Mon Sep 17 00:00:00 2001 From: kirk444 Date: Wed, 16 Mar 2022 12:05:54 -0500 Subject: [PATCH 04/10] Update working-with-secrets.md fix api path since it's a plugin now --- docs/rest-api/working-with-secrets.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/rest-api/working-with-secrets.md b/docs/rest-api/working-with-secrets.md index 5fbbf73..d3de4fb 100644 --- a/docs/rest-api/working-with-secrets.md +++ b/docs/rest-api/working-with-secrets.md @@ -4,10 +4,10 @@ As with most other objects, the REST API can be used to view, create, modify, an ## Generating a Session Key -In order to encrypt or decrypt secret data, a session key must be attached to the API request. To generate a session key, send an authenticated request to the `/api/secrets/get-session-key/` endpoint with the private RSA key which matches your [UserKey](../core-functionality/secrets.md#user-keys). The private key must be POSTed with the name `private_key`. +In order to encrypt or decrypt secret data, a session key must be attached to the API request. To generate a session key, send an authenticated request to the `/api/plugins/netbox_secretstore/secrets/get-session-key/` endpoint with the private RSA key which matches your [UserKey](../core-functionality/secrets.md#user-keys). The private key must be POSTed with the name `private_key`. ```no-highlight -$ curl -X POST http://netbox/api/secrets/get-session-key/ \ +$ curl -X POST http://netbox/api/plugins/netbox_secretstore/secrets/get-session-key/ \ -H "Authorization: Token $TOKEN" \ -H "Accept: application/json; indent=4" \ --data-urlencode "private_key@" @@ -29,7 +29,7 @@ The request uses the provided private key to unlock your stored copy of the mast A session key is not needed to retrieve unencrypted secrets: The secret is returned like any normal object with its `plaintext` field set to null. ```no-highlight -$ curl http://netbox/api/secrets/secrets/2587/ \ +$ curl http://netbox/api/plugins/netbox_secretstore/secrets/2587/ \ -H "Authorization: Token $TOKEN" \ -H "Accept: application/json; indent=4" ``` @@ -37,7 +37,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ ```json { "id": 2587, - "url": "http://netbox/api/secrets/secrets/2587/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/2587/", "device": { "id": 1827, "url": "http://netbox/api/dcim/devices/1827/", @@ -46,7 +46,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ }, "role": { "id": 1, - "url": "http://netbox/api/secrets/secret-roles/1/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/secret-roles/1/", "name": "Login Credentials", "slug": "login-creds" }, @@ -63,7 +63,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ To decrypt a secret, we must include our session key in the `X-Session-Key` header when sending the `GET` request: ```no-highlight -$ curl http://netbox/api/secrets/secrets/2587/ \ +$ curl http://netbox/api/plugins/netbox_secretstore/secrets/secrets/2587/ \ -H "Authorization: Token $TOKEN" \ -H "Accept: application/json; indent=4" \ -H "X-Session-Key: dyEnxlc9lnGzaOAV1dV/xqYPV63njIbdZYOgnAlGPHk=" @@ -72,7 +72,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ ```json { "id": 2587, - "url": "http://netbox/api/secrets/secrets/2587/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/secrets/2587/", "device": { "id": 1827, "url": "http://netbox/api/dcim/devices/1827/", @@ -81,7 +81,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ }, "role": { "id": 1, - "url": "http://netbox/api/secrets/secret-roles/1/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/secret-roles/1/", "name": "Login Credentials", "slug": "login-creds" }, @@ -98,7 +98,7 @@ $ curl http://netbox/api/secrets/secrets/2587/ \ Multiple secrets within a list can be decrypted in this manner as well: ```no-highlight -$ curl http://netbox/api/secrets/secrets/?limit=3 \ +$ curl http://netbox/api/plugins/netbox_secretstore/secrets/secrets/?limit=3 \ -H "Authorization: Token $TOKEN" \ -H "Accept: application/json; indent=4" \ -H "X-Session-Key: dyEnxlc9lnGzaOAV1dV/xqYPV63njIbdZYOgnAlGPHk=" @@ -107,7 +107,7 @@ $ curl http://netbox/api/secrets/secrets/?limit=3 \ ```json { "count": 3482, - "next": "http://netbox/api/secrets/secrets/?limit=3&offset=3", + "next": "http://netbox/api/plugins/netbox_secretstore/secrets/secrets/?limit=3&offset=3", "previous": null, "results": [ { @@ -145,7 +145,7 @@ $ curl -X POST http://netbox/api/secrets/secrets/ \ ```json { "id": 6194, - "url": "http://netbox/api/secrets/secrets/9194/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/secrets/9194/", "device": { "id": 1827, "url": "http://netbox/api/dcim/devices/1827/", @@ -154,7 +154,7 @@ $ curl -X POST http://netbox/api/secrets/secrets/ \ }, "role": { "id": 1, - "url": "http://netbox/api/secrets/secret-roles/1/", + "url": "http://netbox/api/plugins/netbox_secretstore/secrets/secret-roles/1/", "name": "Login Credentials", "slug": "login-creds" }, From 333e2de4019a05a4b1b39373454dd7d936b404d4 Mon Sep 17 00:00:00 2001 From: kirk444 Date: Wed, 16 Mar 2022 12:07:03 -0500 Subject: [PATCH 05/10] Update views.py Update paths for api --- netbox_secretstore/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_secretstore/api/views.py b/netbox_secretstore/api/views.py index 1f02490..9110287 100644 --- a/netbox_secretstore/api/views.py +++ b/netbox_secretstore/api/views.py @@ -131,7 +131,7 @@ class GetSessionKeyViewSet(ViewSet): key is POSTed with the name `private_key`. An example: curl -v -X POST -H "Authorization: Token " -H "Accept: application/json; indent=4" \\ - --data-urlencode "private_key@" https://netbox/api/secrets/get-session-key/ + --data-urlencode "private_key@" https://netbox/api/plugins/netbox_secretstore/secrets/get-session-key/ This request will yield a base64-encoded session key to be included in an `X-Session-Key` header in future requests: From 0e25ee51435a2dc18a7f5f4c71e7656b8ea5acb1 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Tue, 5 Apr 2022 23:50:06 -0500 Subject: [PATCH 06/10] Update for v3.2 --- .github/configuration.testing.py | 1 - README.md | 2 - netbox_secretstore/__init__.py | 3 +- netbox_secretstore/api/serializers.py | 7 ++- netbox_secretstore/api/urls.py | 4 +- netbox_secretstore/api/views.py | 6 +-- netbox_secretstore/forms/fields.py | 6 +-- netbox_secretstore/forms/secrets.py | 23 ++++----- netbox_secretstore/graphql/types.py | 4 +- netbox_secretstore/models/secrets.py | 13 +++-- netbox_secretstore/tables.py | 12 ++--- .../templates/netbox_secretstore/secret.html | 4 +- .../netbox_secretstore/secretrole.html | 2 +- netbox_secretstore/urls.py | 2 +- netbox_secretstore/utils/tables.py | 25 ++-------- netbox_secretstore/views.py | 50 +++++++++---------- setup.py | 5 +- 17 files changed, 71 insertions(+), 98 deletions(-) diff --git a/.github/configuration.testing.py b/.github/configuration.testing.py index b7a0e41..fcbfee2 100644 --- a/.github/configuration.testing.py +++ b/.github/configuration.testing.py @@ -15,7 +15,6 @@ } PLUGINS = [ - 'netbox_plugin_extensions', 'netbox_secretstore', ] diff --git a/README.md b/README.md index 278d881..4c5d0e0 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,9 @@ Installation * Install NetBox as per NetBox documentation * Add to local_requirements.txt: - * `netbox-plugin-extensions` * `netbox-secretstore` * Install requirements: `./venv/bin/pip install -r local_requirements.txt` * Add to PLUGINS in NetBox configuration: - * `'netbox_plugin_extensions',` * `'netbox_secretstore',` * Run migration: `./venv/bin/python netbox/manage.py migrate` * Run collectstatic: `./venv/bin/python netbox/manage.py collectstatic --no-input` diff --git a/netbox_secretstore/__init__.py b/netbox_secretstore/__init__.py index 0b95c71..80b89ab 100644 --- a/netbox_secretstore/__init__.py +++ b/netbox_secretstore/__init__.py @@ -17,7 +17,8 @@ class NetBoxSecretStore(PluginConfig): author = metadata.get('Author') author_email = metadata.get('Author-email') base_url = 'netbox_secretstore' - min_version = '3.0.0' + min_version = '3.2.0' + max_version = '3.3.0beta1' required_settings = [] default_settings = { 'public_key_size': 2048 diff --git a/netbox_secretstore/api/serializers.py b/netbox_secretstore/api/serializers.py index cf247d8..89a453f 100644 --- a/netbox_secretstore/api/serializers.py +++ b/netbox_secretstore/api/serializers.py @@ -1,10 +1,9 @@ from django.contrib.contenttypes.models import ContentType from drf_yasg.utils import swagger_serializer_method -from netbox_plugin_extensions.api.serializers import PluginOrganizationalModelSerializer from rest_framework import serializers from netbox.api import ContentTypeField -from netbox.api.serializers import PrimaryModelSerializer +from netbox.api.serializers import NetBoxModelSerializer, NestedGroupModelSerializer from netbox_secretstore.constants import SECRET_ASSIGNMENT_MODELS from netbox_secretstore.models import Secret, SecretRole from utilities.api import get_serializer_for_model @@ -15,7 +14,7 @@ # Secrets # -class SecretRoleSerializer(PluginOrganizationalModelSerializer): +class SecretRoleSerializer(NestedGroupModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_secretstore-api:secretrole-detail') secret_count = serializers.IntegerField(read_only=True) @@ -27,7 +26,7 @@ class Meta: ] -class SecretSerializer(PrimaryModelSerializer): +class SecretSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_secretstore-api:secret-detail') assigned_object_type = ContentTypeField( queryset=ContentType.objects.filter(SECRET_ASSIGNMENT_MODELS) diff --git a/netbox_secretstore/api/urls.py b/netbox_secretstore/api/urls.py index b7e8942..abe92c3 100644 --- a/netbox_secretstore/api/urls.py +++ b/netbox_secretstore/api/urls.py @@ -1,8 +1,8 @@ -from netbox.api import OrderedDefaultRouter +from netbox.api import NetBoxRouter from . import views -router = OrderedDefaultRouter() +router = NetBoxRouter() router.APIRootView = views.SecretsRootView # Secrets diff --git a/netbox_secretstore/api/views.py b/netbox_secretstore/api/views.py index 1f02490..cd1d6c8 100644 --- a/netbox_secretstore/api/views.py +++ b/netbox_secretstore/api/views.py @@ -11,8 +11,8 @@ from rest_framework.routers import APIRootView from rest_framework.viewsets import ViewSet -from extras.api.views import CustomFieldModelViewSet -from netbox.api.views import ModelViewSet +from extras.api.views import CustomFieldViewSet +from netbox.api.viewsets import ModelViewSet from netbox_secretstore import filtersets from netbox_secretstore.exceptions import InvalidKey from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey @@ -37,7 +37,7 @@ def get_view_name(self): # Secret Roles # -class SecretRoleViewSet(CustomFieldModelViewSet): +class SecretRoleViewSet(CustomFieldViewSet): queryset = SecretRole.objects.annotate( secret_count=count_related(Secret, 'role') ) diff --git a/netbox_secretstore/forms/fields.py b/netbox_secretstore/forms/fields.py index c3dbe54..1bf0f09 100644 --- a/netbox_secretstore/forms/fields.py +++ b/netbox_secretstore/forms/fields.py @@ -2,7 +2,7 @@ from django.forms import BoundField from django.urls import reverse -from utilities.forms import fields, widgets +from utilities.forms import widgets, DynamicModelChoiceField class PluginDynamicModelChoiceMixin: @@ -45,11 +45,11 @@ def get_bound_field(self, form, field_name): return bound_field -class PluginDynamicModelChoiceField(PluginDynamicModelChoiceMixin, fields.DynamicModelChoiceField): +class PluginDynamicModelChoiceField(PluginDynamicModelChoiceMixin, DynamicModelChoiceField): pass -class PluginDynamicModelMultipleChoiceField(PluginDynamicModelChoiceMixin, fields.DynamicModelChoiceField): +class PluginDynamicModelMultipleChoiceField(PluginDynamicModelChoiceMixin, DynamicModelChoiceField): """ A multiple-choice version of DynamicModelChoiceField. """ diff --git a/netbox_secretstore/forms/secrets.py b/netbox_secretstore/forms/secrets.py index 447e6ab..96e776a 100644 --- a/netbox_secretstore/forms/secrets.py +++ b/netbox_secretstore/forms/secrets.py @@ -4,12 +4,11 @@ from django.utils.translation import gettext as _ from dcim.models import Device -from extras.forms import ( - AddRemoveTagsForm, CustomFieldModelBulkEditForm, CustomFieldModelFilterForm, CustomFieldModelForm, CustomFieldModelCSVForm, -) +from netbox.forms import NetBoxModelForm +from extras.forms import CustomFieldCSVForm, CustomFieldFilterForm, CustomFieldBulkEditForm, CustomFieldForm, TagForm from extras.models import Tag from utilities.forms import ( - BootstrapMixin, CSVModelChoiceField, SlugField, TagFilterField, DynamicModelChoiceField, + CSVModelChoiceField, SlugField, TagFilterField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ) from virtualization.models import VirtualMachine @@ -45,7 +44,7 @@ def validate_rsa_key(key, is_secret=True): # Secret roles # -class SecretRoleForm(CustomFieldModelForm): +class SecretRoleForm(NetBoxModelForm): slug = SlugField() class Meta: @@ -53,7 +52,7 @@ class Meta: fields = ('name', 'slug', 'description') -class SecretRoleCSVForm(CustomFieldModelCSVForm): +class SecretRoleCSVForm(CustomFieldCSVForm): slug = SlugField() class Meta: @@ -61,7 +60,7 @@ class Meta: fields = SecretRole.csv_headers -class SecretRoleBulkEditForm(CustomFieldModelBulkEditForm): +class SecretRoleBulkEditForm(CustomFieldBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=SecretRole.objects.all(), widget=forms.MultipleHiddenInput @@ -75,7 +74,7 @@ class Meta: nullable_fields = ['description'] -class SecretRoleFilterForm(CustomFieldModelFilterForm): +class SecretRoleFilterForm(CustomFieldFilterForm): model = Secret q = forms.CharField( required=False, @@ -88,7 +87,7 @@ class SecretRoleFilterForm(CustomFieldModelFilterForm): # Secrets # -class SecretForm(CustomFieldModelForm): +class SecretForm(NetBoxModelForm): device = DynamicModelChoiceField( queryset=Device.objects.all(), required=False @@ -167,7 +166,7 @@ def save(self, *args, **kwargs): return super().save(*args, **kwargs) -class SecretCSVForm(CustomFieldModelCSVForm): +class SecretCSVForm(CustomFieldCSVForm): role = CSVModelChoiceField( queryset=SecretRole.objects.all(), to_field_name='name', @@ -221,7 +220,7 @@ def save(self, *args, **kwargs): return s -class SecretBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): +class SecretBulkEditForm(TagForm, CustomFieldBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput() @@ -241,7 +240,7 @@ class Meta: ] -class SecretFilterForm(CustomFieldModelFilterForm): +class SecretFilterForm(CustomFieldFilterForm): model = Secret q = forms.CharField( required=False, diff --git a/netbox_secretstore/graphql/types.py b/netbox_secretstore/graphql/types.py index 50f17f2..eac7190 100644 --- a/netbox_secretstore/graphql/types.py +++ b/netbox_secretstore/graphql/types.py @@ -1,5 +1,5 @@ from netbox_secretstore import filtersets, models -from netbox.graphql.types import ObjectType, OrganizationalObjectType, PrimaryObjectType +from netbox.graphql.types import ObjectType, NetBoxObjectType __all__ = ( 'SecretRoleType', @@ -15,7 +15,7 @@ class Meta: filterset_class = filtersets.SecretRoleFilterSet -class SecretType(PrimaryObjectType): +class SecretType(NetBoxObjectType): class Meta: model = models.Secret diff --git a/netbox_secretstore/models/secrets.py b/netbox_secretstore/models/secrets.py index 2e04a77..b65e397 100644 --- a/netbox_secretstore/models/secrets.py +++ b/netbox_secretstore/models/secrets.py @@ -15,8 +15,7 @@ from dcim.models import Device from virtualization.models import VirtualMachine -from extras.utils import extras_features -from netbox.models import BigIDModel, OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from utilities.querysets import RestrictedQuerySet from netbox_secretstore.exceptions import InvalidKey from netbox_secretstore.hashers import SecretValidationHasher @@ -32,12 +31,13 @@ ) -class UserKey(BigIDModel): +class UserKey(models.Model): """ A UserKey stores a user's personal RSA (public) encryption key, which is used to generate their unique encrypted copy of the master encryption key. The encrypted instance of the master key can be decrypted only with the user's matching (private) decryption key. """ + id = models.BigAutoField(primary_key=True) created = models.DateField( auto_now_add=True ) @@ -165,10 +165,11 @@ def activate(self, master_key): self.save() -class SessionKey(BigIDModel): +class SessionKey(models.Model): """ A SessionKey stores a User's temporary key to be used for the encryption and decryption of secrets. """ + id = models.BigAutoField(primary_key=True) userkey = models.OneToOneField( to='UserKey', on_delete=models.CASCADE, @@ -235,7 +236,6 @@ def get_session_key(self, master_key): return session_key -@extras_features('custom_fields', 'export_templates', 'webhooks') class SecretRole(OrganizationalModel): """ A SecretRole represents an arbitrary functional classification of Secrets. For example, a user might define roles @@ -275,8 +275,7 @@ def to_csv(self): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') -class Secret(PrimaryModel): +class Secret(NetBoxModel): """ A Secret stores an AES256-encrypted copy of sensitive data, such as passwords or secret keys. An irreversible SHA-256 hash is stored along with the ciphertext for validation upon decryption. Each Secret is assigned to exactly diff --git a/netbox_secretstore/tables.py b/netbox_secretstore/tables.py index 4befbd2..7bf3dff 100644 --- a/netbox_secretstore/tables.py +++ b/netbox_secretstore/tables.py @@ -1,6 +1,6 @@ import django_tables2 as tables -from utilities.tables import BaseTable, LinkedCountColumn, TagColumn, ToggleColumn +from netbox.tables import BaseTable, columns, NetBoxTable from netbox_secretstore.utils.tables import PluginButtonsColumn from .models import SecretRole, Secret @@ -10,11 +10,11 @@ # class SecretRoleTable(BaseTable): - pk = ToggleColumn() + pk = columns.ToggleColumn() name = tables.Column( linkify=True ) - secret_count = LinkedCountColumn( + secret_count = columns.LinkedCountColumn( viewname='plugins:netbox_secretstore:secret_list', url_params={'role_id': 'pk'}, verbose_name='Secrets' @@ -31,8 +31,8 @@ class Meta(BaseTable.Meta): # Secrets # -class SecretTable(BaseTable): - pk = ToggleColumn() +class SecretTable(NetBoxTable): + pk = columns.ToggleColumn() id = tables.Column( # Provides a link to the secret linkify=True ) @@ -43,7 +43,7 @@ class SecretTable(BaseTable): role = tables.Column( linkify=True ) - tags = TagColumn( + tags = columns.TagColumn( url_name='plugins:netbox_secretstore:secret_list' ) diff --git a/netbox_secretstore/templates/netbox_secretstore/secret.html b/netbox_secretstore/templates/netbox_secretstore/secret.html index 2989698..6f99847 100644 --- a/netbox_secretstore/templates/netbox_secretstore/secret.html +++ b/netbox_secretstore/templates/netbox_secretstore/secret.html @@ -1,4 +1,4 @@ -{% extends 'netbox_plugin_extensions/generic/object.html' %} +{% extends 'generic/object.html' %} {% load buttons %} {% load helpers %} {% load static %} @@ -68,7 +68,7 @@
- {% include 'netbox_plugin_extensions/inc/panels/tags.html' %} + {% include 'inc/panels/tags.html' %} {% plugin_right_page object %} diff --git a/netbox_secretstore/templates/netbox_secretstore/secretrole.html b/netbox_secretstore/templates/netbox_secretstore/secretrole.html index fd0e23c..06971c6 100644 --- a/netbox_secretstore/templates/netbox_secretstore/secretrole.html +++ b/netbox_secretstore/templates/netbox_secretstore/secretrole.html @@ -1,4 +1,4 @@ -{% extends 'netbox_plugin_extensions/generic/object.html' %} +{% extends 'generic/object.html' %} {% load helpers %} {% load plugins %} diff --git a/netbox_secretstore/urls.py b/netbox_secretstore/urls.py index 958e310..07de2dc 100644 --- a/netbox_secretstore/urls.py +++ b/netbox_secretstore/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from extras.views import ObjectChangeLogView, ObjectJournalView +from netbox.views.generic import ObjectChangeLogView, ObjectJournalView from .views import * from .models import Secret, SecretRole diff --git a/netbox_secretstore/utils/tables.py b/netbox_secretstore/utils/tables.py index a56bb7e..b93ccaf 100644 --- a/netbox_secretstore/utils/tables.py +++ b/netbox_secretstore/utils/tables.py @@ -1,24 +1,5 @@ -from utilities.tables import ButtonsColumn +from netbox.tables import columns -class PluginButtonsColumn(ButtonsColumn): - """ - Render edit, delete, and changelog buttons for an object. - - :param model: Model class to use for calculating URL view names - :param prepend_content: Additional template content to render in the column (optional) - :param return_url_extra: String to append to the return URL (e.g. for specifying a tab) (optional) - """ - - template_code = """ - {{% load plugin_buttons %}} - {{% if "changelog" in buttons %}} - {{% plugin_tr_changelog_button record %}} - {{% endif %}} - {{% if "edit" in buttons and perms.{app_label}.change_{model_name} %}} - {{% plugin_tr_edit_button record return_url_extra %}} - {{% endif %}} - {{% if "delete" in buttons and perms.{app_label}.delete_{model_name} %}} - {{% plugin_tr_delete_button record return_url_extra %}} - {{% endif %}} - """ +class PluginButtonsColumn(columns.ActionsColumn): + pass diff --git a/netbox_secretstore/views.py b/netbox_secretstore/views.py index 89fdc19..a7e7a76 100644 --- a/netbox_secretstore/views.py +++ b/netbox_secretstore/views.py @@ -9,13 +9,12 @@ from django.utils.safestring import mark_safe from django.views.generic.base import View -from netbox_plugin_extensions.views.generic import PluginObjectListView, PluginObjectView, PluginObjectEditView, \ - PluginObjectDeleteView +from netbox.views.generic import ObjectListView, ObjectView, ObjectEditView, ObjectDeleteView, ObjectImportView, \ + BulkEditView, BulkDeleteView from netbox_secretstore.forms import UserKeyForm, SecretRoleFilterForm from netbox.views import generic from utilities.forms import ConfirmationForm -from utilities.tables import paginate_table from utilities.utils import count_related from .tables import * from .forms import * @@ -37,7 +36,7 @@ def get_session_key(request): # Secret roles # -class SecretRoleListView(PluginObjectListView): +class SecretRoleListView(ObjectListView): queryset = SecretRole.objects.annotate( secret_count=count_related(Secret, 'role') ) @@ -46,7 +45,7 @@ class SecretRoleListView(PluginObjectListView): filterset_form = SecretRoleFilterForm -class SecretRoleView(PluginObjectView): +class SecretRoleView(ObjectView): queryset = SecretRole.objects.all() def get_extra_context(self, request, instance): @@ -54,31 +53,30 @@ def get_extra_context(self, request, instance): role=instance ) - secrets_table = SecretTable(secrets) - secrets_table.columns.hide('role') - paginate_table(secrets_table, request) + secrets_table = SecretTable(secrets, exclude=('role',)) + secrets_table.configure(request) return { 'secrets_table': secrets_table, } -class SecretRoleEditView(PluginObjectEditView): +class SecretRoleEditView(ObjectEditView): queryset = SecretRole.objects.all() - model_form = SecretRoleForm + form = SecretRoleForm -class SecretRoleDeleteView(PluginObjectDeleteView): +class SecretRoleDeleteView(ObjectDeleteView): queryset = SecretRole.objects.all() -class SecretRoleBulkImportView(generic.BulkImportView): +class SecretRoleBulkImportView(ObjectImportView): queryset = SecretRole.objects.all() - model_form = SecretRoleCSVForm + form = SecretRoleCSVForm table = SecretRoleTable -class SecretRoleBulkEditView(generic.BulkEditView): +class SecretRoleBulkEditView(BulkEditView): queryset = SecretRole.objects.annotate( secret_count=count_related(Secret, 'role') ) @@ -87,7 +85,7 @@ class SecretRoleBulkEditView(generic.BulkEditView): form = SecretRoleBulkEditForm -class SecretRoleBulkDeleteView(generic.BulkDeleteView): +class SecretRoleBulkDeleteView(BulkDeleteView): queryset = SecretRole.objects.annotate( secret_count=count_related(Secret, 'role') ) @@ -98,7 +96,7 @@ class SecretRoleBulkDeleteView(generic.BulkDeleteView): # Secrets # -class SecretListView(PluginObjectListView): +class SecretListView(ObjectListView): queryset = Secret.objects.all() filterset = SecretFilterSet filterset_form = SecretFilterForm @@ -106,13 +104,13 @@ class SecretListView(PluginObjectListView): action_buttons = ('add', 'import', 'export') -class SecretView(PluginObjectView): +class SecretView(ObjectView): queryset = Secret.objects.all() -class SecretEditView(PluginObjectEditView): +class SecretEditView(ObjectEditView): queryset = Secret.objects.all() - model_form = SecretForm + form = SecretForm template_name = 'netbox_secretstore/secret_edit.html' def dispatch(self, request, *args, **kwargs): @@ -132,8 +130,8 @@ def dispatch(self, request, *args, **kwargs): def post(self, request, *args, **kwargs): logger = logging.getLogger('netbox.views.ObjectEditView') session_key = get_session_key(request) - secret = self.get_object(kwargs) - form = self.model_form(request.POST, instance=secret) + secret = self.get_object(**kwargs) + form = self.form(request.POST, instance=secret) if form.is_valid(): logger.debug("Form validation was successful") @@ -179,13 +177,13 @@ def post(self, request, *args, **kwargs): }) -class SecretDeleteView(PluginObjectDeleteView): +class SecretDeleteView(ObjectDeleteView): queryset = Secret.objects.all() -class SecretBulkImportView(generic.BulkImportView): +class SecretBulkImportView(ObjectImportView): queryset = Secret.objects.all() - model_form = SecretCSVForm + form = SecretCSVForm table = SecretTable template_name = 'netbox_secretstore/secret_import.html' widget_attrs = {'class': 'requires-session-key'} @@ -230,14 +228,14 @@ def post(self, request): }) -class SecretBulkEditView(generic.BulkEditView): +class SecretBulkEditView(BulkEditView): queryset = Secret.objects.prefetch_related('role') filterset = SecretFilterSet table = SecretTable form = SecretBulkEditForm -class SecretBulkDeleteView(generic.BulkDeleteView): +class SecretBulkDeleteView(BulkDeleteView): queryset = Secret.objects.prefetch_related('role') filterset = SecretFilterSet table = SecretTable diff --git a/setup.py b/setup.py index 82493e7..e61ca6b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox-secretstore', - version='1.0.15', + version='1.0.16', description='Netbox Secret Store', long_description='A Secret store for NetBox', url='https://github.com/dansheps/netbox-secretstore/', @@ -13,7 +13,6 @@ install_requires=[ 'importlib', 'pycryptodome', - 'netbox-plugin-extensions>=1.0.8' ], packages=find_packages(), include_package_data=True, @@ -22,6 +21,6 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">=3.7", + python_requires=">=3.8", zip_safe=False, ) From 05dda7900eaa031fb9fa6452bbdb5c00d59acc88 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Tue, 5 Apr 2022 23:50:48 -0500 Subject: [PATCH 07/10] Update for v3.2 - Migration --- ...secret_created_alter_secret_id_and_more.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 netbox_secretstore/migrations/0005_alter_secret_created_alter_secret_id_and_more.py diff --git a/netbox_secretstore/migrations/0005_alter_secret_created_alter_secret_id_and_more.py b/netbox_secretstore/migrations/0005_alter_secret_created_alter_secret_id_and_more.py new file mode 100644 index 0000000..433cde4 --- /dev/null +++ b/netbox_secretstore/migrations/0005_alter_secret_created_alter_secret_id_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.0.3 on 2022-04-06 02:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('netbox_secretstore', '0004_secretrole_tags'), + ] + + operations = [ + migrations.AlterField( + model_name='secret', + name='created', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.AlterField( + model_name='secret', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='secretrole', + name='created', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.AlterField( + model_name='secretrole', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False), + ), + ] From c222f3db79f04d2308a173470d805cc4da223b89 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Tue, 5 Apr 2022 23:53:15 -0500 Subject: [PATCH 08/10] Update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53e3ba5..b293f7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7] + python-version: [3.8] node-version: [14.x] services: redis: @@ -94,4 +94,4 @@ jobs: run: coverage run --source="netbox-secretstore/netbox_secretstore/" netbox/netbox/manage.py test netbox-secretstore/netbox_secretstore/ - name: Show coverage report - run: coverage report --skip-covered --omit *migrations* \ No newline at end of file + run: coverage report --skip-covered --omit *migrations* From ce98eb5381615ef2cd868bd8b858bffbac11198f Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 6 Apr 2022 01:18:03 -0500 Subject: [PATCH 09/10] Update pypi.yml --- .github/workflows/pypi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 4d9d908..37e829f 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7] + python-version: [3.8] steps: - name: Checkout repo @@ -41,4 +41,4 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} - skip_existing: true \ No newline at end of file + skip_existing: true From 9ecbcec0f57991c2f6374ee52b7df55402c73af6 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 6 Apr 2022 01:25:07 -0500 Subject: [PATCH 10/10] Update ci.yml --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b293f7f..e4daf83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -r netbox/requirements.txt - pip install -r netbox/docs/requirements.txt pip install pycodestyle coverage ln -sr ./netbox-secretstore/.github/configuration.testing.py ./netbox/netbox/netbox/configuration.py yarn --cwd netbox-secretstore/netbox_secretstore/project-static