Repository: Onemind-Services-LLC/netbox-secrets (language: python)

Issue:
Updated test cases
# Pull request checklist

## Description

- Update the test cases

Closes: #2 



Discussion / hints:

Fix patch:
diff --git a/netbox_secretstore/__init__.py b/netbox_secrets/__init__.py
similarity index 90%
rename from netbox_secretstore/__init__.py
rename to netbox_secrets/__init__.py
index 9264a5d..a5e007f 100644
--- a/netbox_secretstore/__init__.py
+++ b/netbox_secrets/__init__.py
@@ -14,8 +14,8 @@ class NetBoxSecrets(PluginConfig):
     author = metadata.get('Author')
     author_email = metadata.get('Author-email')
     base_url = 'secrets'
-    min_version = '3.3.0beta1'
-    max_version = '3.3.9'
+    min_version = '3.3.0'
+    max_version = '3.3.99'
     required_settings = []
     default_settings = {
         'public_key_size': 2048
diff --git a/netbox_secretstore/admin.py b/netbox_secrets/admin.py
similarity index 100%
rename from netbox_secretstore/admin.py
rename to netbox_secrets/admin.py
diff --git a/netbox_secretstore/api/__init__.py b/netbox_secrets/api/__init__.py
similarity index 100%
rename from netbox_secretstore/api/__init__.py
rename to netbox_secrets/api/__init__.py
diff --git a/netbox_secretstore/api/nested_serializers.py b/netbox_secrets/api/nested_serializers.py
similarity index 100%
rename from netbox_secretstore/api/nested_serializers.py
rename to netbox_secrets/api/nested_serializers.py
diff --git a/netbox_secretstore/api/serializers.py b/netbox_secrets/api/serializers.py
similarity index 100%
rename from netbox_secretstore/api/serializers.py
rename to netbox_secrets/api/serializers.py
diff --git a/netbox_secretstore/api/urls.py b/netbox_secrets/api/urls.py
similarity index 100%
rename from netbox_secretstore/api/urls.py
rename to netbox_secrets/api/urls.py
diff --git a/netbox_secretstore/api/views.py b/netbox_secrets/api/views.py
similarity index 100%
rename from netbox_secretstore/api/views.py
rename to netbox_secrets/api/views.py
diff --git a/netbox_secretstore/constants.py b/netbox_secrets/constants.py
similarity index 100%
rename from netbox_secretstore/constants.py
rename to netbox_secrets/constants.py
diff --git a/netbox_secretstore/exceptions.py b/netbox_secrets/exceptions.py
similarity index 100%
rename from netbox_secretstore/exceptions.py
rename to netbox_secrets/exceptions.py
diff --git a/netbox_secretstore/filtersets.py b/netbox_secrets/filtersets.py
similarity index 90%
rename from netbox_secretstore/filtersets.py
rename to netbox_secrets/filtersets.py
index 1ff07ec..893f202 100644
--- a/netbox_secretstore/filtersets.py
+++ b/netbox_secrets/filtersets.py
@@ -7,7 +7,6 @@
 from virtualization.models import VirtualMachine
 from .models import Secret, SecretRole
 
-
 __all__ = (
     'SecretFilterSet',
     'SecretRoleFilterSet',
@@ -19,7 +18,10 @@ class SecretRoleFilterSet(NetBoxModelFilterSet):
         method='search',
         label='Search',
     )
-    tag = TagFilter()
+    name = django_filters.ModelMultipleChoiceFilter(
+        queryset=SecretRole.objects.all(),
+        field_name='name'
+    )
 
     class Meta:
         model = SecretRole
@@ -39,6 +41,10 @@ class SecretFilterSet(NetBoxModelFilterSet):
         method='search',
         label='Search',
     )
+    name = django_filters.ModelMultipleChoiceFilter(
+        queryset=SecretRole.objects.all(),
+        field_name='name'
+    )
     role_id = django_filters.ModelMultipleChoiceFilter(
         queryset=SecretRole.objects.all(),
         label='Role (ID)',
diff --git a/netbox_secretstore/forms/__init__.py b/netbox_secrets/forms/__init__.py
similarity index 100%
rename from netbox_secretstore/forms/__init__.py
rename to netbox_secrets/forms/__init__.py
diff --git a/netbox_secretstore/forms/fields.py b/netbox_secrets/forms/fields.py
similarity index 100%
rename from netbox_secretstore/forms/fields.py
rename to netbox_secrets/forms/fields.py
diff --git a/netbox_secretstore/forms/secrets.py b/netbox_secrets/forms/secrets.py
similarity index 84%
rename from netbox_secretstore/forms/secrets.py
rename to netbox_secrets/forms/secrets.py
index 1476ffd..1ccd627 100644
--- a/netbox_secretstore/forms/secrets.py
+++ b/netbox_secrets/forms/secrets.py
@@ -4,18 +4,11 @@
 from django.utils.translation import gettext as _
 
 from dcim.models import Device
-from netbox.forms import NetBoxModelForm
-from extras.forms import CustomFieldCSVForm, CustomFieldFilterForm, CustomFieldBulkEditForm, CustomFieldForm, TagForm
-from extras.models import Tag
-from utilities.forms import (
-    CSVModelChoiceField, SlugField, TagFilterField, DynamicModelChoiceField,
-    DynamicModelMultipleChoiceField,
-)
-from virtualization.models import VirtualMachine
+from netbox.forms import NetBoxModelBulkEditForm, NetBoxModelCSVForm, NetBoxModelFilterSetForm, NetBoxModelForm
 from netbox_secrets.constants import *
 from netbox_secrets.models import Secret, SecretRole, UserKey
-
-from .fields import PluginDynamicModelChoiceField, PluginDynamicModelMultipleChoiceField
+from utilities.forms import CSVModelChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
+from virtualization.models import VirtualMachine
 
 
 def validate_rsa_key(key, is_secret=True):
@@ -23,7 +16,8 @@ def validate_rsa_key(key, is_secret=True):
     Validate the format and type of an RSA key.
     """
     if key.startswith('ssh-rsa '):
-        raise forms.ValidationError("OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.")
+        raise forms.ValidationError(
+            "OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.")
     try:
         key = RSA.importKey(key)
     except ValueError:
@@ -52,15 +46,15 @@ class Meta:
         fields = ('name', 'slug', 'description')
 
 
-class SecretRoleCSVForm(CustomFieldCSVForm):
+class SecretRoleCSVForm(NetBoxModelCSVForm):
     slug = SlugField()
 
     class Meta:
         model = SecretRole
-        fields = SecretRole.csv_headers
+        fields = ('name', 'slug')
 
 
-class SecretRoleBulkEditForm(CustomFieldBulkEditForm):
+class SecretRoleBulkEditForm(NetBoxModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
         queryset=SecretRole.objects.all(),
         widget=forms.MultipleHiddenInput
@@ -70,17 +64,21 @@ class SecretRoleBulkEditForm(CustomFieldBulkEditForm):
         required=False
     )
 
+    model = SecretRole
     class Meta:
         nullable_fields = ['description']
 
 
-class SecretRoleFilterForm(CustomFieldFilterForm):
-    model = Secret
+class SecretRoleFilterForm(NetBoxModelFilterSetForm):
+    model = SecretRole
     q = forms.CharField(
         required=False,
         label=_('Search')
     )
-    tag = TagFilterField(model)
+    name = DynamicModelMultipleChoiceField(
+        queryset=SecretRole.objects.all(),
+        required=False
+    )
 
 
 #
@@ -112,19 +110,15 @@ class SecretForm(NetBoxModelForm):
         label='Plaintext (verify)',
         widget=forms.PasswordInput()
     )
-    role = PluginDynamicModelChoiceField(
+    role = DynamicModelChoiceField(
         queryset=SecretRole.objects.all()
     )
-    tags = DynamicModelMultipleChoiceField(
-        queryset=Tag.objects.all(),
-        required=False
-    )
 
     class Meta:
         model = Secret
-        fields = [
+        fields = (
             'device', 'virtual_machine', 'role', 'name', 'plaintext', 'plaintext2', 'tags',
-        ]
+        )
 
     def __init__(self, *args, **kwargs):
 
@@ -166,7 +160,7 @@ def save(self, *args, **kwargs):
         return super().save(*args, **kwargs)
 
 
-class SecretCSVForm(CustomFieldCSVForm):
+class SecretCSVForm(NetBoxModelCSVForm):
     role = CSVModelChoiceField(
         queryset=SecretRole.objects.all(),
         to_field_name='name',
@@ -190,7 +184,7 @@ class SecretCSVForm(CustomFieldCSVForm):
 
     class Meta:
         model = Secret
-        fields = ['role', 'name', 'plaintext', 'device', 'virtual_machine']
+        fields = ('role', 'name', 'plaintext', 'device', 'virtual_machine')
         help_texts = {
             'name': 'Name or username',
         }
@@ -220,12 +214,12 @@ def save(self, *args, **kwargs):
         return s
 
 
-class SecretBulkEditForm(TagForm, CustomFieldBulkEditForm):
+class SecretBulkEditForm(NetBoxModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
         queryset=Secret.objects.all(),
         widget=forms.MultipleHiddenInput()
     )
-    role = PluginDynamicModelChoiceField(
+    role = DynamicModelChoiceField(
         queryset=SecretRole.objects.all(),
         required=False
     )
@@ -234,24 +228,37 @@ class SecretBulkEditForm(TagForm, CustomFieldBulkEditForm):
         required=False
     )
 
+    model = Secret
     class Meta:
         nullable_fields = [
             'name',
         ]
 
 
-class SecretFilterForm(CustomFieldFilterForm):
+class SecretFilterForm(NetBoxModelFilterSetForm):
     model = Secret
     q = forms.CharField(
         required=False,
         label=_('Search')
     )
-    role_id = PluginDynamicModelMultipleChoiceField(
+    name = DynamicModelMultipleChoiceField(
+        queryset=Secret.objects.all(),
+        required=False
+    )
+    role_id = DynamicModelMultipleChoiceField(
         queryset=SecretRole.objects.all(),
         required=False,
         label=_('Role')
     )
-    tag = TagFilterField(model)
+    device = DynamicModelMultipleChoiceField(
+        queryset=Device.objects.all(),
+        required=False
+    )
+
+    virtual_machine = DynamicModelMultipleChoiceField(
+        queryset=VirtualMachine.objects.all(),
+        required=False
+    )
 
 
 #
@@ -259,7 +266,6 @@ class SecretFilterForm(CustomFieldFilterForm):
 #
 
 class UserKeyForm(forms.ModelForm):
-
     class Meta:
         model = UserKey
         fields = ['public_key']
diff --git a/netbox_secretstore/graphql/__init__.py b/netbox_secrets/graphql/__init__.py
similarity index 100%
rename from netbox_secretstore/graphql/__init__.py
rename to netbox_secrets/graphql/__init__.py
diff --git a/netbox_secretstore/graphql/schema.py b/netbox_secrets/graphql/schema.py
similarity index 100%
rename from netbox_secretstore/graphql/schema.py
rename to netbox_secrets/graphql/schema.py
diff --git a/netbox_secretstore/graphql/types.py b/netbox_secrets/graphql/types.py
similarity index 100%
rename from netbox_secretstore/graphql/types.py
rename to netbox_secrets/graphql/types.py
diff --git a/netbox_secretstore/hashers.py b/netbox_secrets/hashers.py
similarity index 100%
rename from netbox_secretstore/hashers.py
rename to netbox_secrets/hashers.py
diff --git a/netbox_secretstore/migrations/0001_initial.py b/netbox_secrets/migrations/0001_initial.py
similarity index 72%
rename from netbox_secretstore/migrations/0001_initial.py
rename to netbox_secrets/migrations/0001_initial.py
index 02102d5..890271f 100644
--- a/netbox_secretstore/migrations/0001_initial.py
+++ b/netbox_secrets/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.5 on 2021-07-20 17:24
+# Generated by Django 4.0.8 on 2022-12-28 11:35
 
 from django.conf import settings
 import django.core.serializers.json
@@ -11,45 +11,13 @@ class Migration(migrations.Migration):
 
     initial = True
 
-    replaces = [
-        ('secrets', '0001_initial'),
-        ('secrets', '0002_userkey_add_session_key'),
-        ('secrets', '0003_unicode_literals'),
-        ('secrets', '0004_tags'),
-        ('secrets', '0005_change_logging'),
-        ('secrets', '0006_custom_tag_models'),
-        ('secrets', '0007_secretrole_description'),
-        ('secrets', '0008_standardize_description'),
-        ('secrets', '0009_secretrole_drop_users_groups'),
-        ('secrets', '0010_custom_field_data'),
-        ('secrets', '0011_secret_generic_assignments'),
-        ('secrets', '0012_standardize_name_length'),
-        ('secrets', '0013_standardize_models')
-    ]
-
     dependencies = [
+        ('extras', '0077_customlink_extend_text_and_url'),
         ('contenttypes', '0002_remove_content_type_name'),
-        ('extras', '0061_extras_change_logging'),
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
-        migrations.CreateModel(
-            name='SecretRole',
-            fields=[
-                ('created', models.DateField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
-                ('id', models.BigAutoField(primary_key=True, serialize=False)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'db_table': 'secrets_secretrole',
-                'ordering': ['name'],
-            },
-        ),
         migrations.CreateModel(
             name='UserKey',
             fields=[
@@ -61,7 +29,6 @@ class Migration(migrations.Migration):
                 ('user', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='user_key', to=settings.AUTH_USER_MODEL)),
             ],
             options={
-                'db_table': 'secrets_userkey',
                 'ordering': ['user__username'],
             },
         ),
@@ -75,27 +42,41 @@ class Migration(migrations.Migration):
                 ('userkey', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='session_key', to='netbox_secrets.userkey')),
             ],
             options={
-                'db_table': 'secrets_sessionkey',
                 'ordering': ['userkey__user__username'],
             },
         ),
+        migrations.CreateModel(
+            name='SecretRole',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
         migrations.CreateModel(
             name='Secret',
             fields=[
-                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
                 ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
-                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                 ('assigned_object_id', models.PositiveIntegerField()),
                 ('name', models.CharField(blank=True, max_length=100)),
                 ('ciphertext', models.BinaryField(max_length=65568)),
                 ('hash', models.CharField(editable=False, max_length=128)),
-                ('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='secrets', to='contenttypes.contenttype')),
                 ('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='secrets', to='netbox_secrets.secretrole')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
             ],
             options={
-                'db_table': 'secrets_secret',
                 'ordering': ('role', 'name', 'pk'),
                 'unique_together': {('assigned_object_type', 'assigned_object_id', 'role', 'name')},
             },
diff --git a/netbox_secrets/migrations/0002_populate_userkeys.py b/netbox_secrets/migrations/0002_populate_userkeys.py
new file mode 100644
index 0000000..3f5cb48
--- /dev/null
+++ b/netbox_secrets/migrations/0002_populate_userkeys.py
@@ -0,0 +1,41 @@
+from django.db import migrations
+
+
+def populate_userkeys(apps, schema_editor):
+    """Populate the UserKey model with data from the SecretStore model."""
+    UserKeyOld = apps.get_model('netbox_secretstore', 'UserKey')
+    UserKey = apps.get_model('netbox_secrets', 'UserKey')
+
+    # Retrieve the necessary data from SecretStore objects
+    userkeys = UserKeyOld.objects.values(
+        'id', 'created', 'last_updated', 'user', 'public_key', 'master_key_cipher'
+    )
+
+    # Queue UserKeys to be created
+    userkeys_to_create = []
+    userkey_count = userkeys.count()
+    for i, userkey in enumerate(userkeys, start=1):
+        userkeys_to_create.append(UserKey(
+            id=userkey['id'],
+            created=userkey['created'],
+            last_updated=userkey['last_updated'],
+            user_id=userkey['user'],
+            public_key=userkey['public_key'],
+            master_key_cipher=userkey['master_key_cipher'],
+        ))
+
+    # Bulk create the userkey objects
+    UserKey.objects.bulk_create(userkeys_to_create, batch_size=100)
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('netbox_secrets', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_userkeys,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]
diff --git a/netbox_secrets/migrations/0003_populate_secretroles.py b/netbox_secrets/migrations/0003_populate_secretroles.py
new file mode 100644
index 0000000..04db7ab
--- /dev/null
+++ b/netbox_secrets/migrations/0003_populate_secretroles.py
@@ -0,0 +1,42 @@
+from django.db import migrations
+
+
+def populate_secretroles(apps, schema_editor):
+    """Populate the SecretRole model with data from the SecretStore model."""
+    SecretRoleOld = apps.get_model('netbox_secretstore', 'SecretRole')
+    SecretRole = apps.get_model('netbox_secrets', 'SecretRole')
+
+    # Retrieve the necessary data from SecretStore objects
+    roles = SecretRoleOld.objects.values(
+        'id', 'name', 'slug', 'description', 'created', 'last_updated', 'custom_field_data'
+    )
+
+    # Queue SecretRoles to be created
+    roles_to_create = []
+    role_count = roles.count()
+    for i, role in enumerate(roles, start=1):
+        roles_to_create.append(SecretRole(
+            id=role['id'],
+            name=role['name'],
+            slug=role['slug'],
+            description=role['description'],
+            created=role['created'],
+            last_updated=role['last_updated'],
+            custom_field_data=role['custom_field_data'],
+        ))
+
+    # Bulk create the role objects
+    SecretRole.objects.bulk_create(roles_to_create, batch_size=100)
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('netbox_secrets', '0002_populate_userkeys'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_secretroles,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]
diff --git a/netbox_secrets/migrations/0004_populate_secrets.py b/netbox_secrets/migrations/0004_populate_secrets.py
new file mode 100644
index 0000000..203815c
--- /dev/null
+++ b/netbox_secrets/migrations/0004_populate_secrets.py
@@ -0,0 +1,46 @@
+from django.db import migrations
+
+
+def populate_secrets(apps, schema_editor):
+    """Populate the Secret model with data from the SecretStore model."""
+    SecretOld = apps.get_model('netbox_secretstore', 'Secret')
+    Secret = apps.get_model('netbox_secrets', 'Secret')
+
+    # Retrieve the necessary data from SecretStore objects
+    secrets = SecretOld.objects.values(
+        'created', 'last_updated', 'custom_field_data', 'id', 'assigned_object_id', 'name', 'ciphertext', 'hash',
+        'assigned_object_type_id', 'role_id',
+    )
+
+    # Queue Secrets to be created
+    secrets_to_create = []
+    secret_count = secrets.count()
+    for i, secret in enumerate(secrets, start=1):
+        secrets_to_create.append(Secret(
+            created=secret['created'],
+            last_updated=secret['last_updated'],
+            custom_field_data=secret['custom_field_data'],
+            id=secret['id'],
+            assigned_object_id=secret['assigned_object_id'],
+            name=secret['name'],
+            ciphertext=secret['ciphertext'],
+            assigned_object_type_id=secret['assigned_object_type_id'],
+            role_id=secret['role_id'],
+            hash=secret['hash'],
+        ))
+
+    # Bulk create the secret objects
+    Secret.objects.bulk_create(secrets_to_create, batch_size=100)
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('netbox_secrets', '0003_populate_secretroles'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_secrets,
+            reverse_code=migrations.RunPython.noop
+        )
+    ]
diff --git a/netbox_secretstore/migrations/__init__.py b/netbox_secrets/migrations/__init__.py
similarity index 100%
rename from netbox_secretstore/migrations/__init__.py
rename to netbox_secrets/migrations/__init__.py
diff --git a/netbox_secretstore/models/__init__.py b/netbox_secrets/models/__init__.py
similarity index 100%
rename from netbox_secretstore/models/__init__.py
rename to netbox_secrets/models/__init__.py
diff --git a/netbox_secretstore/models/secrets.py b/netbox_secrets/models/secrets.py
similarity index 99%
rename from netbox_secretstore/models/secrets.py
rename to netbox_secrets/models/secrets.py
index 16448d7..8ecd6a5 100644
--- a/netbox_secretstore/models/secrets.py
+++ b/netbox_secrets/models/secrets.py
@@ -238,7 +238,7 @@ def get_session_key(self, master_key):
         return session_key
 
 
-class SecretRole(OrganizationalModel):
+class SecretRole(NetBoxModel):
     """
     A SecretRole represents an arbitrary functional classification of Secrets. For example, a user might define roles
     such as "Login Credentials" or "SNMP Communities."
@@ -256,8 +256,6 @@ class SecretRole(OrganizationalModel):
         blank=True,
     )
 
-    objects = RestrictedQuerySet.as_manager()
-
     csv_headers = ['name', 'slug', 'description']
 
     class Meta:
@@ -289,7 +287,8 @@ class Secret(NetBoxModel):
     """
     assigned_object_type = models.ForeignKey(
         to=ContentType,
-        on_delete=models.PROTECT
+        on_delete=models.PROTECT,
+        related_name='secrets',
     )
     assigned_object_id = models.PositiveIntegerField()
     assigned_object = GenericForeignKey(
@@ -426,7 +425,6 @@ def validate(self, plaintext):
             raise Exception("Hash has not been generated for this secret.")
         return check_password(plaintext, self.hash, preferred=SecretValidationHasher())
 
-
 GenericRelation(
     to=Secret,
     content_type_field='assigned_object_type',
diff --git a/netbox_secretstore/navigation.py b/netbox_secrets/navigation.py
similarity index 100%
rename from netbox_secretstore/navigation.py
rename to netbox_secrets/navigation.py
diff --git a/netbox_secretstore/project-static/.eslintignore b/netbox_secrets/project-static/.eslintignore
similarity index 100%
rename from netbox_secretstore/project-static/.eslintignore
rename to netbox_secrets/project-static/.eslintignore
diff --git a/netbox_secretstore/project-static/.eslintrc b/netbox_secrets/project-static/.eslintrc
similarity index 100%
rename from netbox_secretstore/project-static/.eslintrc
rename to netbox_secrets/project-static/.eslintrc
diff --git a/netbox_secretstore/project-static/.prettierignore b/netbox_secrets/project-static/.prettierignore
similarity index 100%
rename from netbox_secretstore/project-static/.prettierignore
rename to netbox_secrets/project-static/.prettierignore
diff --git a/netbox_secretstore/project-static/.prettierrc b/netbox_secrets/project-static/.prettierrc
similarity index 100%
rename from netbox_secretstore/project-static/.prettierrc
rename to netbox_secrets/project-static/.prettierrc
diff --git a/netbox_secretstore/project-static/bundle.js b/netbox_secrets/project-static/bundle.js
similarity index 100%
rename from netbox_secretstore/project-static/bundle.js
rename to netbox_secrets/project-static/bundle.js
diff --git a/netbox_secretstore/project-static/package.json b/netbox_secrets/project-static/package.json
similarity index 100%
rename from netbox_secretstore/project-static/package.json
rename to netbox_secrets/project-static/package.json
diff --git a/netbox_secretstore/project-static/src/bs.ts b/netbox_secrets/project-static/src/bs.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/bs.ts
rename to netbox_secrets/project-static/src/bs.ts
diff --git a/netbox_secretstore/project-static/src/global.d.ts b/netbox_secrets/project-static/src/global.d.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/global.d.ts
rename to netbox_secrets/project-static/src/global.d.ts
diff --git a/netbox_secretstore/project-static/src/index.ts b/netbox_secrets/project-static/src/index.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/index.ts
rename to netbox_secrets/project-static/src/index.ts
diff --git a/netbox_secretstore/project-static/src/secrets.ts b/netbox_secrets/project-static/src/secrets.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/secrets.ts
rename to netbox_secrets/project-static/src/secrets.ts
diff --git a/netbox_secretstore/project-static/src/types.ts b/netbox_secrets/project-static/src/types.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/types.ts
rename to netbox_secrets/project-static/src/types.ts
diff --git a/netbox_secretstore/project-static/src/util.ts b/netbox_secrets/project-static/src/util.ts
similarity index 100%
rename from netbox_secretstore/project-static/src/util.ts
rename to netbox_secrets/project-static/src/util.ts
diff --git a/netbox_secretstore/project-static/tsconfig.json b/netbox_secrets/project-static/tsconfig.json
similarity index 100%
rename from netbox_secretstore/project-static/tsconfig.json
rename to netbox_secrets/project-static/tsconfig.json
diff --git a/netbox_secretstore/project-static/yarn.lock b/netbox_secrets/project-static/yarn.lock
similarity index 100%
rename from netbox_secretstore/project-static/yarn.lock
rename to netbox_secrets/project-static/yarn.lock
diff --git a/netbox_secretstore/querysets.py b/netbox_secrets/querysets.py
similarity index 100%
rename from netbox_secretstore/querysets.py
rename to netbox_secrets/querysets.py
diff --git a/netbox_secretstore/static/netbox_secretstore/secrets.js b/netbox_secrets/static/netbox_secrets/secrets.js
similarity index 100%
rename from netbox_secretstore/static/netbox_secretstore/secrets.js
rename to netbox_secrets/static/netbox_secrets/secrets.js
diff --git a/netbox_secretstore/static/netbox_secretstore/secrets.js.map b/netbox_secrets/static/netbox_secrets/secrets.js.map
similarity index 100%
rename from netbox_secretstore/static/netbox_secretstore/secrets.js.map
rename to netbox_secrets/static/netbox_secrets/secrets.js.map
diff --git a/netbox_secretstore/tables.py b/netbox_secrets/tables.py
similarity index 90%
rename from netbox_secretstore/tables.py
rename to netbox_secrets/tables.py
index bc161b4..2aff26f 100644
--- a/netbox_secretstore/tables.py
+++ b/netbox_secrets/tables.py
@@ -36,6 +36,9 @@ class SecretTable(NetBoxTable):
     id = tables.Column(  # Provides a link to the secret
         linkify=True
     )
+    name = tables.Column(
+        linkify=True
+    )
     assigned_object = tables.Column(
         linkify=True,
         orderable=False,
@@ -51,4 +54,4 @@ class SecretTable(NetBoxTable):
     class Meta(BaseTable.Meta):
         model = Secret
         fields = ('pk', 'id', 'assigned_object', 'role', 'name', 'last_updated', 'hash', 'tags')
-        default_columns = ('pk', 'id', 'assigned_object', 'role', 'name', 'last_updated')
+        default_columns = ('pk', 'id', 'name', 'assigned_object', 'role', 'last_updated')
diff --git a/netbox_secretstore/template_content.py b/netbox_secrets/template_content.py
similarity index 100%
rename from netbox_secretstore/template_content.py
rename to netbox_secrets/template_content.py
diff --git a/netbox_secretstore/templates/netbox_secrets/activate_keys.html b/netbox_secrets/templates/netbox_secrets/activate_keys.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/activate_keys.html
rename to netbox_secrets/templates/netbox_secrets/activate_keys.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/add.html b/netbox_secrets/templates/netbox_secrets/buttons/add.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/add.html
rename to netbox_secrets/templates/netbox_secrets/buttons/add.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/clone.html b/netbox_secrets/templates/netbox_secrets/buttons/clone.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/clone.html
rename to netbox_secrets/templates/netbox_secrets/buttons/clone.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/delete.html b/netbox_secrets/templates/netbox_secrets/buttons/delete.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/delete.html
rename to netbox_secrets/templates/netbox_secrets/buttons/delete.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/edit.html b/netbox_secrets/templates/netbox_secrets/buttons/edit.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/edit.html
rename to netbox_secrets/templates/netbox_secrets/buttons/edit.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/export.html b/netbox_secrets/templates/netbox_secrets/buttons/export.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/export.html
rename to netbox_secrets/templates/netbox_secrets/buttons/export.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/import.html b/netbox_secrets/templates/netbox_secrets/buttons/import.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/import.html
rename to netbox_secrets/templates/netbox_secrets/buttons/import.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/tr_changelog.html b/netbox_secrets/templates/netbox_secrets/buttons/tr_changelog.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/tr_changelog.html
rename to netbox_secrets/templates/netbox_secrets/buttons/tr_changelog.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/tr_delete.html b/netbox_secrets/templates/netbox_secrets/buttons/tr_delete.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/tr_delete.html
rename to netbox_secrets/templates/netbox_secrets/buttons/tr_delete.html
diff --git a/netbox_secretstore/templates/netbox_secrets/buttons/tr_edit.html b/netbox_secrets/templates/netbox_secrets/buttons/tr_edit.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/buttons/tr_edit.html
rename to netbox_secrets/templates/netbox_secrets/buttons/tr_edit.html
diff --git a/netbox_secretstore/templates/netbox_secrets/inc/assigned_secrets.html b/netbox_secrets/templates/netbox_secrets/inc/assigned_secrets.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/inc/assigned_secrets.html
rename to netbox_secrets/templates/netbox_secrets/inc/assigned_secrets.html
diff --git a/netbox_secretstore/templates/netbox_secrets/inc/device_secrets.html b/netbox_secrets/templates/netbox_secrets/inc/device_secrets.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/inc/device_secrets.html
rename to netbox_secrets/templates/netbox_secrets/inc/device_secrets.html
diff --git a/netbox_secretstore/templates/netbox_secrets/inc/private_key_modal.html b/netbox_secrets/templates/netbox_secrets/inc/private_key_modal.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/inc/private_key_modal.html
rename to netbox_secrets/templates/netbox_secrets/inc/private_key_modal.html
diff --git a/netbox_secretstore/templates/netbox_secrets/secret.html b/netbox_secrets/templates/netbox_secrets/secret.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/secret.html
rename to netbox_secrets/templates/netbox_secrets/secret.html
diff --git a/netbox_secretstore/templates/netbox_secrets/secret_import.html b/netbox_secrets/templates/netbox_secrets/secret_bulk_import.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/secret_import.html
rename to netbox_secrets/templates/netbox_secrets/secret_bulk_import.html
diff --git a/netbox_secretstore/templates/netbox_secrets/secret_delete.html b/netbox_secrets/templates/netbox_secrets/secret_delete.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/secret_delete.html
rename to netbox_secrets/templates/netbox_secrets/secret_delete.html
diff --git a/netbox_secretstore/templates/netbox_secrets/secret_edit.html b/netbox_secrets/templates/netbox_secrets/secret_edit.html
similarity index 98%
rename from netbox_secretstore/templates/netbox_secrets/secret_edit.html
rename to netbox_secrets/templates/netbox_secrets/secret_edit.html
index 1aaa4b3..a900e88 100644
--- a/netbox_secretstore/templates/netbox_secrets/secret_edit.html
+++ b/netbox_secrets/templates/netbox_secrets/secret_edit.html
@@ -49,7 +49,6 @@ <h4>Secret Assignment</h4>
     {% endwith %}
     {% render_field form.role %}
     {% render_field form.name %}
-    {% render_field form.userkeys %}
     {% render_field form.tags %}
     </div>
 </div>
diff --git a/netbox_secretstore/templates/netbox_secrets/secretrole.html b/netbox_secrets/templates/netbox_secrets/secretrole.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/secretrole.html
rename to netbox_secrets/templates/netbox_secrets/secretrole.html
diff --git a/netbox_secretstore/templates/netbox_secrets/sessionkey_delete.html b/netbox_secrets/templates/netbox_secrets/sessionkey_delete.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/sessionkey_delete.html
rename to netbox_secrets/templates/netbox_secrets/sessionkey_delete.html
diff --git a/netbox_secretstore/templates/netbox_secrets/userkey.html b/netbox_secrets/templates/netbox_secrets/userkey.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/userkey.html
rename to netbox_secrets/templates/netbox_secrets/userkey.html
diff --git a/netbox_secretstore/templates/netbox_secrets/userkey_edit.html b/netbox_secrets/templates/netbox_secrets/userkey_edit.html
similarity index 100%
rename from netbox_secretstore/templates/netbox_secrets/userkey_edit.html
rename to netbox_secrets/templates/netbox_secrets/userkey_edit.html
diff --git a/netbox_secretstore/urls.py b/netbox_secrets/urls.py
similarity index 100%
rename from netbox_secretstore/urls.py
rename to netbox_secrets/urls.py
diff --git a/netbox_secretstore/utils/__init__.py b/netbox_secrets/utils/__init__.py
similarity index 100%
rename from netbox_secretstore/utils/__init__.py
rename to netbox_secrets/utils/__init__.py
diff --git a/netbox_secretstore/utils/crypto.py b/netbox_secrets/utils/crypto.py
similarity index 100%
rename from netbox_secretstore/utils/crypto.py
rename to netbox_secrets/utils/crypto.py
diff --git a/netbox_secretstore/utils/hashers.py b/netbox_secrets/utils/hashers.py
similarity index 100%
rename from netbox_secretstore/utils/hashers.py
rename to netbox_secrets/utils/hashers.py
diff --git a/netbox_secretstore/utils/helpers.py b/netbox_secrets/utils/helpers.py
similarity index 100%
rename from netbox_secretstore/utils/helpers.py
rename to netbox_secrets/utils/helpers.py
diff --git a/netbox_secretstore/utils/tables.py b/netbox_secrets/utils/tables.py
similarity index 100%
rename from netbox_secretstore/utils/tables.py
rename to netbox_secrets/utils/tables.py
diff --git a/netbox_secretstore/views.py b/netbox_secrets/views.py
similarity index 89%
rename from netbox_secretstore/views.py
rename to netbox_secrets/views.py
index e8db051..887a7a7 100644
--- a/netbox_secretstore/views.py
+++ b/netbox_secrets/views.py
@@ -3,23 +3,20 @@
 
 from django.contrib import messages
 from django.contrib.auth.mixins import LoginRequiredMixin
-from django.shortcuts import redirect, render, get_object_or_404
+from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
 from django.views.generic.base import View
 
-from netbox.views.generic import ObjectListView, ObjectView, ObjectEditView, ObjectDeleteView, ObjectImportView, \
-    BulkEditView, BulkDeleteView
-from netbox_secrets.forms import UserKeyForm, SecretRoleFilterForm
-
 from netbox.views import generic
+from netbox_secrets.forms import SecretRoleFilterForm
 from utilities.forms import ConfirmationForm
 from utilities.utils import count_related
-from .tables import *
-from .forms import *
 from .filtersets import *
-from .models import SecretRole, Secret, SessionKey, UserKey
+from .forms import *
+from .models import SessionKey, UserKey
+from .tables import *
 
 
 def get_session_key(request):
@@ -36,7 +33,7 @@ def get_session_key(request):
 # Secret roles
 #
 
-class SecretRoleListView(ObjectListView):
+class SecretRoleListView(generic.ObjectListView):
     queryset = SecretRole.objects.annotate(
         secret_count=count_related(Secret, 'role')
     )
@@ -45,7 +42,7 @@ class SecretRoleListView(ObjectListView):
     filterset_form = SecretRoleFilterForm
 
 
-class SecretRoleView(ObjectView):
+class SecretRoleView(generic.ObjectView):
     queryset = SecretRole.objects.all()
 
     def get_extra_context(self, request, instance):
@@ -61,22 +58,22 @@ def get_extra_context(self, request, instance):
         }
 
 
-class SecretRoleEditView(ObjectEditView):
+class SecretRoleEditView(generic.ObjectEditView):
     queryset = SecretRole.objects.all()
     form = SecretRoleForm
 
 
-class SecretRoleDeleteView(ObjectDeleteView):
+class SecretRoleDeleteView(generic.ObjectDeleteView):
     queryset = SecretRole.objects.all()
 
 
-class SecretRoleBulkImportView(ObjectImportView):
+class SecretRoleBulkImportView(generic.BulkImportView):
     queryset = SecretRole.objects.all()
-    form = SecretRoleCSVForm
+    model_form = SecretRoleCSVForm
     table = SecretRoleTable
 
 
-class SecretRoleBulkEditView(BulkEditView):
+class SecretRoleBulkEditView(generic.BulkEditView):
     queryset = SecretRole.objects.annotate(
         secret_count=count_related(Secret, 'role')
     )
@@ -85,7 +82,7 @@ class SecretRoleBulkEditView(BulkEditView):
     form = SecretRoleBulkEditForm
 
 
-class SecretRoleBulkDeleteView(BulkDeleteView):
+class SecretRoleBulkDeleteView(generic.BulkDeleteView):
     queryset = SecretRole.objects.annotate(
         secret_count=count_related(Secret, 'role')
     )
@@ -96,7 +93,7 @@ class SecretRoleBulkDeleteView(BulkDeleteView):
 # Secrets
 #
 
-class SecretListView(ObjectListView):
+class SecretListView(generic.ObjectListView):
     queryset = Secret.objects.all()
     filterset = SecretFilterSet
     filterset_form = SecretFilterForm
@@ -104,11 +101,11 @@ class SecretListView(ObjectListView):
     action_buttons = ('add', 'import', 'export')
 
 
-class SecretView(ObjectView):
+class SecretView(generic.ObjectView):
     queryset = Secret.objects.all()
 
 
-class SecretEditView(ObjectEditView):
+class SecretEditView(generic.ObjectEditView):
     queryset = Secret.objects.all()
     form = SecretForm
     template_name = 'netbox_secrets/secret_edit.html'
@@ -177,15 +174,15 @@ def post(self, request, *args, **kwargs):
         })
 
 
-class SecretDeleteView(ObjectDeleteView):
+class SecretDeleteView(generic.ObjectDeleteView):
     queryset = Secret.objects.all()
 
 
-class SecretBulkImportView(ObjectImportView):
+class SecretBulkImportView(generic.BulkImportView):
     queryset = Secret.objects.all()
-    form = SecretCSVForm
+    model_form = SecretCSVForm
     table = SecretTable
-    template_name = 'netbox_secrets/secret_import.html'
+    template_name = 'netbox_secrets/secret_bulk_import.html'
     widget_attrs = {'class': 'requires-session-key'}
 
     master_key = None
@@ -228,14 +225,14 @@ def post(self, request):
         })
 
 
-class SecretBulkEditView(BulkEditView):
+class SecretBulkEditView(generic.BulkEditView):
     queryset = Secret.objects.prefetch_related('role')
     filterset = SecretFilterSet
     table = SecretTable
     form = SecretBulkEditForm
 
 
-class SecretBulkDeleteView(BulkDeleteView):
+class SecretBulkDeleteView(generic.BulkDeleteView):
     queryset = Secret.objects.prefetch_related('role')
     filterset = SecretFilterSet
     table = SecretTable
@@ -293,7 +290,7 @@ def post(self, request):
         })
 
 
-class SessionKeyDeleteView(ObjectDeleteView):
+class SessionKeyDeleteView(generic.ObjectDeleteView):
     queryset = SessionKey.objects.all()
 
     def get(self, request):
@@ -308,11 +305,9 @@ def get(self, request):
         })
 
     def post(self, request):
-
         sessionkey = get_object_or_404(SessionKey, userkey__user=request.user)
         form = ConfirmationForm(request.POST)
         if form.is_valid():
-
             # Delete session key
             sessionkey.delete()
             messages.success(request, "Session key deleted")
diff --git a/netbox_secretstore/migrations/0002_rename_table.py b/netbox_secretstore/migrations/0002_rename_table.py
deleted file mode 100644
index bcc1811..0000000
--- a/netbox_secretstore/migrations/0002_rename_table.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 3.2.5 on 2021-07-20 17:30
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_secrets', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterModelTable(
-            name='secret',
-            table=None,
-        ),
-        migrations.AlterModelTable(
-            name='secretrole',
-            table=None,
-        ),
-        migrations.AlterModelTable(
-            name='sessionkey',
-            table=None,
-        ),
-        migrations.AlterModelTable(
-            name='userkey',
-            table=None,
-        ),
-    ]
diff --git a/netbox_secretstore/migrations/0003_modify_objectchange_records.py b/netbox_secretstore/migrations/0003_modify_objectchange_records.py
deleted file mode 100644
index 9847fc5..0000000
--- a/netbox_secretstore/migrations/0003_modify_objectchange_records.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Generated by Django 3.2.5 on 2021-07-20 17:30
-
-from django.db import migrations
-
-
-def secrets_to_nbsecrets(apps, schema_editor):
-    try:
-        ObjectChange = apps.get_model('extras', 'ObjectChange')
-        ContentType = apps.get_model('contenttypes', 'ContentType')
-
-        ctsecret = ContentType.objects.get(app_label='secrets', model='secret')
-        ctsecretrole = ContentType.objects.get(app_label='secrets', model='secretrole')
-        ctuserkey = ContentType.objects.get(app_label='secrets', model='userkey')
-        ctsessionkey = ContentType.objects.get(app_label='secrets', model='sessionkey')
-
-        ctnbsecret = ContentType.objects.get(app_label='netbox_secrets', model='secret')
-        ctnbsecretrole = ContentType.objects.get(app_label='netbox_secrets', model='secretrole')
-        ctnbuserkey = ContentType.objects.get(app_label='netbox_secrets', model='userkey')
-        ctnbsessionkey = ContentType.objects.get(app_label='netbox_secrets', model='sessionkey')
-
-        ObjectChange.objects.filter(changed_object_type_id=ctsecret.id).update(changed_object_type_id=ctnbsecret.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctsecretrole.id).update(changed_object_type_id=ctnbsecretrole.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctsessionkey.id).update(changed_object_type_id=ctnbsessionkey.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctuserkey.id).update(changed_object_type_id=ctnbuserkey.id)
-    except ContentType.DoesNotExist:
-        pass
-
-
-def nbsecrets_to_secrets(apps, schema_editor):
-    try:
-
-        ObjectChange = apps.get_model('extras', 'ObjectChange')
-        ContentType = apps.get_model('contenttypes', 'ContentType')
-
-        ctsecret = ContentType.objects.get(app_label='secrets', model='secret')
-        ctsecretrole = ContentType.objects.get(app_label='secrets', model='secretrole')
-        ctuserkey = ContentType.objects.get(app_label='secrets', model='userkey')
-        ctsessionkey = ContentType.objects.get(app_label='secrets', model='sessionkey')
-
-        ctnbsecret = ContentType.objects.get(app_label='netbox_secrets', model='secret')
-        ctnbsecretrole = ContentType.objects.get(app_label='netbox_secrets', model='secretrole')
-        ctnbuserkey = ContentType.objects.get(app_label='netbox_secrets', model='userkey')
-        ctnbsessionkey = ContentType.objects.get(app_label='netbox_secrets', model='sessionkey')
-
-        ObjectChange.objects.filter(changed_object_type_id=ctnbsecret.id).update(changed_object_type_id=ctsecret.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctnbsecretrole.id).update(changed_object_type_id=ctsecretrole.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctnbsessionkey.id).update(changed_object_type_id=ctsessionkey.id)
-        ObjectChange.objects.filter(changed_object_type_id=ctnbuserkey.id).update(changed_object_type_id=ctuserkey.id)
-    except ContentType.DoesNotExist:
-        pass
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_secrets', '0002_rename_table'),
-    ]
-
-    operations = [
-        migrations.RunPython(code=secrets_to_nbsecrets, reverse_code=nbsecrets_to_secrets)
-    ]
diff --git a/netbox_secretstore/migrations/0004_secretrole_tags.py b/netbox_secretstore/migrations/0004_secretrole_tags.py
deleted file mode 100644
index 3998724..0000000
--- a/netbox_secretstore/migrations/0004_secretrole_tags.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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_secrets', '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/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
deleted file mode 100644
index a28b7c5..0000000
--- a/netbox_secretstore/migrations/0005_alter_secret_created_alter_secret_id_and_more.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated by Django 4.0.3 on 2022-04-06 02:54
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_secrets', '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),
-        ),
-    ]
diff --git a/setup.py b/setup.py
index 93a24ae..ae74eff 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 setup(
     name='netbox-secrets',
-    version='1.3.2',
+    version='1.4.0',
     description='Netbox Secret Store',
     long_description='A Secret store for NetBox',
     url='https://github.com/dansheps/netbox-secrets/',


Test patch:
diff --git a/netbox_secretstore/tests/__init__.py b/netbox_secrets/tests/__init__.py
similarity index 100%
rename from netbox_secretstore/tests/__init__.py
rename to netbox_secrets/tests/__init__.py
diff --git a/netbox_secretstore/tests/constants.py b/netbox_secrets/tests/constants.py
similarity index 100%
rename from netbox_secretstore/tests/constants.py
rename to netbox_secrets/tests/constants.py
diff --git a/netbox_secretstore/tests/test_api.py b/netbox_secrets/tests/test_api.py
similarity index 82%
rename from netbox_secretstore/tests/test_api.py
rename to netbox_secrets/tests/test_api.py
index c85aef4..e325947 100644
--- a/netbox_secretstore/tests/test_api.py
+++ b/netbox_secrets/tests/test_api.py
@@ -4,11 +4,10 @@
 from rest_framework import status
 
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
+from netbox_secrets.models import Secret, SecretRole, SessionKey, UserKey
 from utilities.testing import APITestCase, APIViewTestCases
 from .constants import PRIVATE_KEY, PUBLIC_KEY
 
-from netbox_secrets.models import Secret, SecretRole, SessionKey, UserKey
-
 
 class SecretsTestMixin:
     view_namespace = 'plugins-api:netbox_secrets'
@@ -25,37 +24,25 @@ def test_graphql_list_objects(self):
 class AppTest(APITestCase):
 
     def test_root(self):
-
         url = reverse('plugins-api:netbox_secrets-api:api-root')
         response = self.client.get('{}?format=api'.format(url), **self.header)
 
         self.assertEqual(response.status_code, 200)
 
 
-class SecretRoleTest(SecretsTestMixin, APIViewTestCases.APIViewTestCase):
+class SecretRoleTest(
+    APIViewTestCases.GetObjectViewTestCase,
+    APIViewTestCases.ListObjectsViewTestCase,
+    APIViewTestCases.CreateObjectViewTestCase,
+    APIViewTestCases.UpdateObjectViewTestCase,
+    APIViewTestCases.DeleteObjectViewTestCase,
+):
     model = SecretRole
+    view_namespace = 'plugins-api:netbox_secrets'
     brief_fields = ['display', 'id', 'name', 'secret_count', 'slug', 'url']
-    create_data = [
-        {
-            'name': 'Secret Role 4',
-            'slug': 'secret-role-4',
-        },
-        {
-            'name': 'Secret Role 5',
-            'slug': 'secret-role-5',
-        },
-        {
-            'name': 'Secret Role 6',
-            'slug': 'secret-role-6',
-        },
-    ]
-    bulk_update_data = {
-        'description': 'New description',
-    }
 
     @classmethod
     def setUpTestData(cls):
-
         secret_roles = (
             SecretRole(name='Secret Role 1', slug='secret-role-1'),
             SecretRole(name='Secret Role 2', slug='secret-role-2'),
@@ -63,10 +50,33 @@ def setUpTestData(cls):
         )
         SecretRole.objects.bulk_create(secret_roles)
 
+        cls.create_data = [
+            {
+                'name': 'Secret Role 4',
+                'slug': 'secret-role-4',
+            },
+            {
+                'name': 'Secret Role 5',
+                'slug': 'secret-role-5',
+            },
+            {
+                'name': 'Secret Role 6',
+                'slug': 'secret-role-6',
+            },
+        ]
+
 
-class SecretTest(SecretsTestMixin, APIViewTestCases.APIViewTestCase):
+class SecretTest(
+    APIViewTestCases.GetObjectViewTestCase,
+    APIViewTestCases.ListObjectsViewTestCase,
+    APIViewTestCases.CreateObjectViewTestCase,
+    APIViewTestCases.UpdateObjectViewTestCase,
+    APIViewTestCases.DeleteObjectViewTestCase,
+):
     model = Secret
-    brief_fields = ['display', 'id', 'name', 'url']
+    view_namespace = 'plugins-api:netbox_secrets'
+    brief_fields = ['assigned_object', 'assigned_object_id', 'assigned_object_type', 'created', 'custom_fields',
+                    'display', 'hash', 'id', 'last_updated', 'name', 'plaintext', 'role', 'tags', 'url']
 
     def setUp(self):
         super().setUp()
@@ -141,7 +151,6 @@ def prepare_instance(self, instance):
 class GetSessionKeyTest(APITestCase):
 
     def setUp(self):
-
         super().setUp()
 
         userkey = UserKey(user=self.user, public_key=PUBLIC_KEY)
@@ -155,7 +164,6 @@ def setUp(self):
         }
 
     def test_get_session_key(self):
-
         encoded_session_key = base64.b64encode(self.session_key.key).decode()
 
         url = reverse('plugins-api:netbox_secrets-api:get-session-key-list')
@@ -169,7 +177,6 @@ def test_get_session_key(self):
         self.assertNotEqual(response.data.get('session_key'), encoded_session_key)
 
     def test_get_session_key_preserved(self):
-
         encoded_session_key = base64.b64encode(self.session_key.key).decode()
 
         url = reverse('plugins-api:netbox_secrets-api:get-session-key-list') + '?preserve_key=True'
diff --git a/netbox_secretstore/tests/test_filters.py b/netbox_secrets/tests/test_filters.py
similarity index 95%
rename from netbox_secretstore/tests/test_filters.py
rename to netbox_secrets/tests/test_filters.py
index deebdc0..ad8eabe 100644
--- a/netbox_secretstore/tests/test_filters.py
+++ b/netbox_secrets/tests/test_filters.py
@@ -1,10 +1,9 @@
 from django.test import TestCase
 
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
-from virtualization.models import Cluster, ClusterType, VirtualMachine
-
 from netbox_secrets.filtersets import *
 from netbox_secrets.models import Secret, SecretRole
+from virtualization.models import Cluster, ClusterType, VirtualMachine
 
 
 class SecretRoleTestCase(TestCase):
@@ -13,7 +12,6 @@ class SecretRoleTestCase(TestCase):
 
     @classmethod
     def setUpTestData(cls):
-
         roles = (
             SecretRole(name='Secret Role 1', slug='secret-role-1'),
             SecretRole(name='Secret Role 2', slug='secret-role-2'),
@@ -26,7 +24,8 @@ def test_id(self):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_name(self):
-        params = {'name': ['Secret Role 1', 'Secret Role 2']}
+        name = SecretRole.objects.all()
+        params = {'name': [name[0].pk, name[1].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_slug(self):
@@ -40,7 +39,6 @@ class SecretTestCase(TestCase):
 
     @classmethod
     def setUpTestData(cls):
-
         site = Site.objects.create(name='Site 1', slug='site-1')
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
         device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
@@ -86,8 +84,9 @@ def test_id(self):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_name(self):
-        params = {'name': ['Secret 1', 'Secret 2']}
-        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        name = Secret.objects.all()
+        params = {'name': [name[0].pk, name[1].pk, name[2].pk, name[3].pk, name[4].pk, name[5].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
 
     def test_role(self):
         roles = SecretRole.objects.all()[:2]
diff --git a/netbox_secretstore/tests/test_form.py b/netbox_secrets/tests/test_form.py
similarity index 99%
rename from netbox_secretstore/tests/test_form.py
rename to netbox_secrets/tests/test_form.py
index 950ff56..378e7d3 100644
--- a/netbox_secretstore/tests/test_form.py
+++ b/netbox_secrets/tests/test_form.py
@@ -1,10 +1,8 @@
 from django.test import TestCase
 
-from utilities.testing import create_test_user
-
 from netbox_secrets.forms import UserKeyForm
 from netbox_secrets.models import UserKey
-
+from utilities.testing import create_test_user
 from .constants import PUBLIC_KEY, SSH_PUBLIC_KEY
 
 
diff --git a/netbox_secretstore/tests/test_models.py b/netbox_secrets/tests/test_models.py
similarity index 97%
rename from netbox_secretstore/tests/test_models.py
rename to netbox_secrets/tests/test_models.py
index e15fb1c..2e8c186 100644
--- a/netbox_secretstore/tests/test_models.py
+++ b/netbox_secrets/tests/test_models.py
@@ -6,9 +6,9 @@
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 
-from netbox_secrets.utils.hashers import SecretValidationHasher
 from netbox_secrets.models import Secret, UserKey
-from netbox_secrets.utils.crypto import encrypt_master_key, decrypt_master_key, generate_random_key
+from netbox_secrets.utils.crypto import decrypt_master_key, encrypt_master_key, generate_random_key
+from netbox_secrets.utils.hashers import SecretValidationHasher
 
 
 class UserKeyTestCase(TestCase):
@@ -87,7 +87,6 @@ class SecretTestCase(TestCase):
 
     @classmethod
     def setUpTestData(cls):
-
         # Generate a random key for encryption/decryption of secrets
         cls.secret_key = generate_random_key()
 
@@ -108,7 +107,8 @@ def test_01_encrypt_decrypt(self):
         # Ensure proper hashing algorithm is used
         hasher, iterations, salt, sha256 = s.hash.split('$')
         self.assertEqual(hasher, 'pbkdf2_sha256', "Hashing algorithm has been modified to: {}".format(hasher))
-        self.assertGreaterEqual(int(iterations), SecretValidationHasher.iterations, "Insufficient iteration count ({}) for hash".format(iterations))
+        self.assertGreaterEqual(int(iterations), SecretValidationHasher.iterations,
+                                "Insufficient iteration count ({}) for hash".format(iterations))
         self.assertGreaterEqual(len(salt), 12, "Hash salt is too short ({} chars)".format(len(salt)))
 
         # Test hash validation
diff --git a/netbox_secretstore/tests/test_views.py b/netbox_secrets/tests/test_views.py
similarity index 99%
rename from netbox_secretstore/tests/test_views.py
rename to netbox_secrets/tests/test_views.py
index 86ec765..f21b511 100644
--- a/netbox_secretstore/tests/test_views.py
+++ b/netbox_secrets/tests/test_views.py
@@ -4,10 +4,8 @@
 from django.urls import reverse
 
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
-from utilities.testing import ViewTestCases
-
 from netbox_secrets.models import Secret, SecretRole, SessionKey, UserKey
-
+from utilities.testing import ViewTestCases
 from .constants import PRIVATE_KEY, PUBLIC_KEY
 
 
@@ -29,7 +27,6 @@ class SecretRoleTestCase(SecretsTestMixin, ViewTestCases.OrganizationalObjectVie
 
     @classmethod
     def setUpTestData(cls):
-
         SecretRole.objects.bulk_create([
             SecretRole(name='Secret Role 1', slug='secret-role-1'),
             SecretRole(name='Secret Role 2', slug='secret-role-2'),
@@ -68,7 +65,6 @@ class SecretTestCase(
 
     @classmethod
     def setUpTestData(cls):
-
         site = Site.objects.create(name='Site 1', slug='site-1')
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
         devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1')
@@ -107,7 +103,6 @@ def setUpTestData(cls):
         }
 
     def setUp(self):
-
         super().setUp()
 
         # Set up a master key for the test user