Skip to content

Commit

Permalink
Merge pull request #67 from DanSheps/develop
Browse files Browse the repository at this point in the history
Release v1.2.0
  • Loading branch information
DanSheps authored Apr 6, 2022
2 parents 5bb047a + 9ecbcec commit 60dd1a2
Show file tree
Hide file tree
Showing 22 changed files with 125 additions and 120 deletions.
1 change: 0 additions & 1 deletion .github/configuration.testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
}

PLUGINS = [
'netbox_plugin_extensions',
'netbox_secretstore',
]

Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -94,4 +93,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*
run: coverage report --skip-covered --omit *migrations*
4 changes: 2 additions & 2 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7]
python-version: [3.8]
steps:

- name: Checkout repo
Expand Down Expand Up @@ -41,4 +41,4 @@ jobs:
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
skip_existing: true
skip_existing: true
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
24 changes: 12 additions & 12 deletions docs/rest-api/working-with-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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@<filename>"
Expand All @@ -29,15 +29,15 @@ 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"
```

```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/",
Expand All @@ -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"
},
Expand All @@ -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="
Expand All @@ -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/",
Expand All @@ -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"
},
Expand All @@ -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="
Expand All @@ -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": [
{
Expand Down Expand Up @@ -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/",
Expand All @@ -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"
},
Expand Down
3 changes: 2 additions & 1 deletion netbox_secretstore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 3 additions & 4 deletions netbox_secretstore/api/serializers.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)

Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions netbox_secretstore/api/urls.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 4 additions & 4 deletions netbox_secretstore/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
)
Expand Down Expand Up @@ -131,7 +131,7 @@ class GetSessionKeyViewSet(ViewSet):
key is POSTed with the name `private_key`. An example:
curl -v -X POST -H "Authorization: Token <token>" -H "Accept: application/json; indent=4" \\
--data-urlencode "private_key@<filename>" https://netbox/api/secrets/get-session-key/
--data-urlencode "private_key@<filename>" 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:
Expand Down
6 changes: 3 additions & 3 deletions netbox_secretstore/forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
"""
Expand Down
23 changes: 11 additions & 12 deletions netbox_secretstore/forms/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -45,23 +44,23 @@ def validate_rsa_key(key, is_secret=True):
# Secret roles
#

class SecretRoleForm(CustomFieldModelForm):
class SecretRoleForm(NetBoxModelForm):
slug = SlugField()

class Meta:
model = SecretRole
fields = ('name', 'slug', 'description')


class SecretRoleCSVForm(CustomFieldModelCSVForm):
class SecretRoleCSVForm(CustomFieldCSVForm):
slug = SlugField()

class Meta:
model = SecretRole
fields = SecretRole.csv_headers


class SecretRoleBulkEditForm(CustomFieldModelBulkEditForm):
class SecretRoleBulkEditForm(CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=SecretRole.objects.all(),
widget=forms.MultipleHiddenInput
Expand All @@ -75,7 +74,7 @@ class Meta:
nullable_fields = ['description']


class SecretRoleFilterForm(CustomFieldModelFilterForm):
class SecretRoleFilterForm(CustomFieldFilterForm):
model = Secret
q = forms.CharField(
required=False,
Expand All @@ -88,7 +87,7 @@ class SecretRoleFilterForm(CustomFieldModelFilterForm):
# Secrets
#

class SecretForm(CustomFieldModelForm):
class SecretForm(NetBoxModelForm):
device = DynamicModelChoiceField(
queryset=Device.objects.all(),
required=False
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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()
Expand All @@ -241,7 +240,7 @@ class Meta:
]


class SecretFilterForm(CustomFieldModelFilterForm):
class SecretFilterForm(CustomFieldFilterForm):
model = Secret
q = forms.CharField(
required=False,
Expand Down
4 changes: 2 additions & 2 deletions netbox_secretstore/graphql/types.py
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -15,7 +15,7 @@ class Meta:
filterset_class = filtersets.SecretRoleFilterSet


class SecretType(PrimaryObjectType):
class SecretType(NetBoxObjectType):

class Meta:
model = models.Secret
Expand Down
Loading

0 comments on commit 60dd1a2

Please sign in to comment.