diff --git a/.github/configuration.testing.py b/.github/configuration.testing.py index fcbfee2..b7a0e41 100644 --- a/.github/configuration.testing.py +++ b/.github/configuration.testing.py @@ -15,6 +15,7 @@ } PLUGINS = [ + 'netbox_plugin_extensions', 'netbox_secretstore', ] diff --git a/README.md b/README.md index e2b9201..278d881 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,14 @@ This is the continuation of the secrets app. Installation ---- -To install, add to your local_requirements.txt and pip install -r local_requirements.txt \ No newline at end of file +* 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/api/views.py b/netbox_secretstore/api/views.py index 4bba206..1f02490 100644 --- a/netbox_secretstore/api/views.py +++ b/netbox_secretstore/api/views.py @@ -13,7 +13,7 @@ from extras.api.views import CustomFieldModelViewSet from netbox.api.views import ModelViewSet -from netbox_secretstore import filters +from netbox_secretstore import filtersets from netbox_secretstore.exceptions import InvalidKey from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey from utilities.utils import count_related @@ -42,7 +42,7 @@ class SecretRoleViewSet(CustomFieldModelViewSet): secret_count=count_related(Secret, 'role') ) serializer_class = serializers.SecretRoleSerializer - filterset_class = filters.SecretRoleFilterSet + filterset_class = filtersets.SecretRoleFilterSet # @@ -52,7 +52,7 @@ class SecretRoleViewSet(CustomFieldModelViewSet): class SecretViewSet(ModelViewSet): queryset = Secret.objects.prefetch_related('role', 'tags') serializer_class = serializers.SecretSerializer - filterset_class = filters.SecretFilterSet + filterset_class = filtersets.SecretFilterSet master_key = None diff --git a/netbox_secretstore/filters.py b/netbox_secretstore/filtersets.py similarity index 86% rename from netbox_secretstore/filters.py rename to netbox_secretstore/filtersets.py index 74e80ca..2a40cb9 100644 --- a/netbox_secretstore/filters.py +++ b/netbox_secretstore/filtersets.py @@ -16,11 +16,24 @@ class SecretRoleFilterSet(ChangeLoggedModelFilterSet): + q = django_filters.CharFilter( + method='search', + label='Search', + ) + tag = TagFilter() class Meta: model = SecretRole fields = ['id', 'name', 'slug'] + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(slug__icontains=value) + ) + class SecretFilterSet(BaseFilterSet): q = django_filters.CharFilter( diff --git a/netbox_secretstore/forms/secrets.py b/netbox_secretstore/forms/secrets.py index 66416c0..447e6ab 100644 --- a/netbox_secretstore/forms/secrets.py +++ b/netbox_secretstore/forms/secrets.py @@ -75,6 +75,15 @@ class Meta: nullable_fields = ['description'] +class SecretRoleFilterForm(CustomFieldModelFilterForm): + model = Secret + q = forms.CharField( + required=False, + label=_('Search') + ) + tag = TagFilterField(model) + + # # Secrets # diff --git a/netbox_secretstore/graphql/__init__.py b/netbox_secretstore/graphql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netbox_secretstore/graphql/schema.py b/netbox_secretstore/graphql/schema.py new file mode 100644 index 0000000..0ad2abc --- /dev/null +++ b/netbox_secretstore/graphql/schema.py @@ -0,0 +1,12 @@ +import graphene + +from netbox.graphql.fields import ObjectField, ObjectListField +from .types import * + + +class SecretStoreQuery(graphene.ObjectType): + secret = ObjectField(SecretType) + secret_list = ObjectListField(SecretType) + + secretrole = ObjectField(SecretRoleType) + secretrole_list = ObjectListField(SecretRoleType) diff --git a/netbox_secretstore/graphql/types.py b/netbox_secretstore/graphql/types.py new file mode 100644 index 0000000..50f17f2 --- /dev/null +++ b/netbox_secretstore/graphql/types.py @@ -0,0 +1,23 @@ +from netbox_secretstore import filtersets, models +from netbox.graphql.types import ObjectType, OrganizationalObjectType, PrimaryObjectType + +__all__ = ( + 'SecretRoleType', + 'SecretType', +) + + +class SecretRoleType(ObjectType): + + class Meta: + model = models.SecretRole + fields = '__all__' + filterset_class = filtersets.SecretRoleFilterSet + + +class SecretType(PrimaryObjectType): + + class Meta: + model = models.Secret + fields = '__all__' + filterset_class = filtersets.SecretFilterSet diff --git a/netbox_secretstore/migrations/0004_secretrole_tags.py b/netbox_secretstore/migrations/0004_secretrole_tags.py new file mode 100644 index 0000000..ee9b1cc --- /dev/null +++ b/netbox_secretstore/migrations/0004_secretrole_tags.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.9 on 2021-12-21 15:27 + +from django.db import migrations +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0066_customfield_name_validation'), + ('netbox_secretstore', '0003_modify_objectchange_records'), + ] + + operations = [ + migrations.AddField( + model_name='secretrole', + name='tags', + field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'), + ), + ] diff --git a/netbox_secretstore/templates/netbox_secretstore/secret.html b/netbox_secretstore/templates/netbox_secretstore/secret.html index 572e0e9..2989698 100644 --- a/netbox_secretstore/templates/netbox_secretstore/secret.html +++ b/netbox_secretstore/templates/netbox_secretstore/secret.html @@ -68,7 +68,7 @@
- {% include 'inc/panels/tags.html' with tags=object.tags.all url='plugins:netbox_secretstore:secret_list' %} + {% include 'netbox_plugin_extensions/inc/panels/tags.html' %} {% plugin_right_page object %} diff --git a/netbox_secretstore/templates/netbox_secretstore/userkey_edit.html b/netbox_secretstore/templates/netbox_secretstore/userkey_edit.html index 22eacc9..e1c7186 100644 --- a/netbox_secretstore/templates/netbox_secretstore/userkey_edit.html +++ b/netbox_secretstore/templates/netbox_secretstore/userkey_edit.html @@ -1,4 +1,4 @@ -{% extends 'netbox_plugin_extensions/generic/object_edit.html' %} +{% extends 'generic/object_edit.html' %} {% load form_helpers %} {% load plugins %} {% load static %} diff --git a/netbox_secretstore/tests/test_api.py b/netbox_secretstore/tests/test_api.py index 38fbd85..f8c5f0b 100644 --- a/netbox_secretstore/tests/test_api.py +++ b/netbox_secretstore/tests/test_api.py @@ -4,10 +4,23 @@ from rest_framework import status from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site -from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey from utilities.testing import APITestCase, APIViewTestCases from .constants import PRIVATE_KEY, PUBLIC_KEY +from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey + + +class SecretsTestMixin: + view_namespace = 'plugins-api:netbox_secretstore' + + # Skip GraphQL tests for now + def test_graphql_get_object(self): + pass + + # Skip GraphQL tests for now + def test_graphql_list_objects(self): + pass + class AppTest(APITestCase): @@ -19,7 +32,7 @@ def test_root(self): self.assertEqual(response.status_code, 200) -class SecretRoleTest(APIViewTestCases.APIViewTestCase): +class SecretRoleTest(SecretsTestMixin, APIViewTestCases.APIViewTestCase): model = SecretRole brief_fields = ['display', 'id', 'name', 'secret_count', 'slug', 'url'] create_data = [ @@ -51,7 +64,7 @@ def setUpTestData(cls): SecretRole.objects.bulk_create(secret_roles) -class SecretTest(APIViewTestCases.APIViewTestCase): +class SecretTest(SecretsTestMixin, APIViewTestCases.APIViewTestCase): model = Secret brief_fields = ['display', 'id', 'name', 'url'] diff --git a/netbox_secretstore/tests/test_filters.py b/netbox_secretstore/tests/test_filters.py index 16f609f..308e9c2 100644 --- a/netbox_secretstore/tests/test_filters.py +++ b/netbox_secretstore/tests/test_filters.py @@ -1,10 +1,11 @@ from django.test import TestCase from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site -from netbox_secretstore.filters import * -from models import Secret, SecretRole from virtualization.models import Cluster, ClusterType, VirtualMachine +from netbox_secretstore.filtersets import * +from netbox_secretstore.models import Secret, SecretRole + class SecretRoleTestCase(TestCase): queryset = SecretRole.objects.all() diff --git a/netbox_secretstore/tests/test_form.py b/netbox_secretstore/tests/test_form.py index 187a091..38fdffa 100644 --- a/netbox_secretstore/tests/test_form.py +++ b/netbox_secretstore/tests/test_form.py @@ -1,7 +1,10 @@ from django.test import TestCase -from forms import UserKeyForm -from models import UserKey + from utilities.testing import create_test_user + +from netbox_secretstore.forms import UserKeyForm +from netbox_secretstore.models import UserKey + from .constants import PUBLIC_KEY, SSH_PUBLIC_KEY @@ -10,8 +13,8 @@ class UserKeyFormTestCase(TestCase): def setUp(self): user = create_test_user( permissions=[ - 'secrets.view_secretrole', - 'secrets.add_secretrole', + 'netbox_secretstore.view_secretrole', + 'netbox_secretstore.add_secretrole', ] ) self.userkey = UserKey(user=user) diff --git a/netbox_secretstore/tests/test_views.py b/netbox_secretstore/tests/test_views.py index 17017d3..a9cbe64 100644 --- a/netbox_secretstore/tests/test_views.py +++ b/netbox_secretstore/tests/test_views.py @@ -4,12 +4,27 @@ from django.urls import reverse from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site -from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey from utilities.testing import ViewTestCases + +from netbox_secretstore.models import Secret, SecretRole, SessionKey, UserKey + from .constants import PRIVATE_KEY, PUBLIC_KEY -class SecretRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): +class SecretsTestMixin: + def _get_base_url(self): + """ + Return the base format for a URL for the test's model. Override this to test for a model which belongs + to a different app (e.g. testing Interfaces within the virtualization app). + """ + return '{}:{}:{}_{{}}'.format( + 'plugins', + self.model._meta.app_label, + self.model._meta.model_name + ) + + +class SecretRoleTestCase(SecretsTestMixin, ViewTestCases.OrganizationalObjectViewTestCase): model = SecretRole @classmethod @@ -41,6 +56,7 @@ def setUpTestData(cls): # TODO: Change base class to PrimaryObjectViewTestCase class SecretTestCase( + SecretsTestMixin, ViewTestCases.GetObjectViewTestCase, ViewTestCases.GetObjectChangelogViewTestCase, ViewTestCases.DeleteObjectViewTestCase, @@ -103,7 +119,7 @@ def setUp(self): @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) def test_import_objects(self): - self.add_permissions('secrets.add_secret') + self.add_permissions('netbox_secretstore.add_secret') device = Device.objects.get(name='Device 1') csv_data = ( diff --git a/netbox_secretstore/views.py b/netbox_secretstore/views.py index 9e3cc16..89fdc19 100644 --- a/netbox_secretstore/views.py +++ b/netbox_secretstore/views.py @@ -11,7 +11,7 @@ from netbox_plugin_extensions.views.generic import PluginObjectListView, PluginObjectView, PluginObjectEditView, \ PluginObjectDeleteView -from netbox_secretstore.forms import UserKeyForm +from netbox_secretstore.forms import UserKeyForm, SecretRoleFilterForm from netbox.views import generic from utilities.forms import ConfirmationForm @@ -19,7 +19,7 @@ from utilities.utils import count_related from .tables import * from .forms import * -from .filters import * +from .filtersets import * from .models import SecretRole, Secret, SessionKey, UserKey @@ -42,6 +42,8 @@ class SecretRoleListView(PluginObjectListView): secret_count=count_related(Secret, 'role') ) table = SecretRoleTable + filterset = SecretRoleFilterSet + filterset_form = SecretRoleFilterForm class SecretRoleView(PluginObjectView): diff --git a/setup.py b/setup.py index 388cc8d..acdd904 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox-secretstore', - version='1.0.11', + version='1.0.13', description='Netbox Secret Store', long_description='A Secret store for NetBox', url='https://github.com/dansheps/netbox-secretstore/',