mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Converted secrets import view to new scheme
This commit is contained in:
@ -13,8 +13,7 @@ from django.views.generic import View
|
|||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from utilities.paginator import EnhancedPaginator
|
from utilities.paginator import EnhancedPaginator
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView, BulkImportView2, ObjectDeleteView, ObjectEditView,
|
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
ObjectListView,
|
|
||||||
)
|
)
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -7,7 +7,7 @@ from django import forms
|
|||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from utilities.forms import BootstrapMixin, BulkEditForm, BulkImportForm, CSVDataField, FilterChoiceField, SlugField
|
from utilities.forms import BootstrapMixin, BulkEditForm, CSVDataField2, FilterChoiceField, SlugField
|
||||||
from .models import Secret, SecretRole, UserKey
|
from .models import Secret, SecretRole, UserKey
|
||||||
|
|
||||||
|
|
||||||
@ -65,27 +65,37 @@ class SecretForm(BootstrapMixin, forms.ModelForm):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class SecretFromCSVForm(forms.ModelForm):
|
class SecretCSVForm(forms.ModelForm):
|
||||||
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
|
device = forms.ModelChoiceField(
|
||||||
error_messages={'invalid_choice': 'Device not found.'})
|
queryset=Device.objects.all(),
|
||||||
role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), to_field_name='name',
|
to_field_name='name',
|
||||||
error_messages={'invalid_choice': 'Invalid secret role.'})
|
help_text='Device name',
|
||||||
plaintext = forms.CharField()
|
error_messages={
|
||||||
|
'invalid_choice': 'Device not found.',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
role = forms.ModelChoiceField(
|
||||||
|
queryset=SecretRole.objects.all(),
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Name of assigned role',
|
||||||
|
error_messages={
|
||||||
|
'invalid_choice': 'Invalid secret role.',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
plaintext = forms.CharField(
|
||||||
|
help_text='Plaintext secret data'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Secret
|
model = Secret
|
||||||
fields = ['device', 'role', 'name', 'plaintext']
|
fields = ['device', 'role', 'name', 'plaintext']
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
s = super(SecretFromCSVForm, self).save(*args, **kwargs)
|
s = super(SecretCSVForm, self).save(*args, **kwargs)
|
||||||
s.plaintext = str(self.cleaned_data['plaintext'])
|
s.plaintext = str(self.cleaned_data['plaintext'])
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class SecretImportForm(BootstrapMixin, BulkImportForm):
|
|
||||||
csv = CSVDataField(csv_form=SecretFromCSVForm, widget=forms.Textarea(attrs={'class': 'requires-session-key'}))
|
|
||||||
|
|
||||||
|
|
||||||
class SecretBulkEditForm(BootstrapMixin, BulkEditForm):
|
class SecretBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput)
|
pk = forms.ModelMultipleChoiceField(queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), required=False)
|
role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), required=False)
|
||||||
|
@ -16,7 +16,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
url(r'^secrets/$', views.SecretListView.as_view(), name='secret_list'),
|
url(r'^secrets/$', views.SecretListView.as_view(), name='secret_list'),
|
||||||
url(r'^secrets/import/$', views.secret_import, name='secret_import'),
|
url(r'^secrets/import/$', views.SecretBulkImportView.as_view(), name='secret_import'),
|
||||||
url(r'^secrets/edit/$', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
url(r'^secrets/edit/$', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
||||||
url(r'^secrets/delete/$', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
url(r'^secrets/delete/$', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
||||||
url(r'^secrets/(?P<pk>\d+)/$', views.SecretView.as_view(), name='secret'),
|
url(r'^secrets/(?P<pk>\d+)/$', views.SecretView.as_view(), name='secret'),
|
||||||
|
@ -12,7 +12,9 @@ from django.utils.decorators import method_decorator
|
|||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView
|
from utilities.views import (
|
||||||
|
BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
|
)
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
from .decorators import userkey_required
|
from .decorators import userkey_required
|
||||||
from .models import SecretRole, Secret, SessionKey
|
from .models import SecretRole, Secret, SessionKey
|
||||||
@ -185,58 +187,50 @@ class SecretDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
|||||||
default_return_url = 'secrets:secret_list'
|
default_return_url = 'secrets:secret_list'
|
||||||
|
|
||||||
|
|
||||||
@permission_required('secrets.add_secret')
|
class SecretBulkImportView(BulkImportView2):
|
||||||
@userkey_required()
|
permission_required = 'ipam.add_vlan'
|
||||||
def secret_import(request):
|
model_form = forms.SecretCSVForm
|
||||||
|
table = tables.SecretTable
|
||||||
|
default_return_url = 'secrets:secret_list'
|
||||||
|
|
||||||
session_key = request.COOKIES.get('session_key', None)
|
master_key = None
|
||||||
|
|
||||||
if request.method == 'POST':
|
def _save_obj(self, obj_form):
|
||||||
form = forms.SecretImportForm(request.POST)
|
"""
|
||||||
|
Encrypt each object before saving it to the database.
|
||||||
|
"""
|
||||||
|
obj = obj_form.save(commit=False)
|
||||||
|
obj.encrypt(self.master_key)
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
if session_key is None:
|
def post(self, request):
|
||||||
form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
|
|
||||||
|
|
||||||
if form.is_valid():
|
# Grab the session key from cookies.
|
||||||
|
session_key = request.COOKIES.get('session_key')
|
||||||
|
if session_key:
|
||||||
|
|
||||||
new_secrets = []
|
# Attempt to derive the master key using the provided session key.
|
||||||
|
|
||||||
session_key = base64.b64decode(session_key)
|
|
||||||
master_key = None
|
|
||||||
try:
|
try:
|
||||||
sk = SessionKey.objects.get(userkey__user=request.user)
|
sk = SessionKey.objects.get(userkey__user=request.user)
|
||||||
master_key = sk.get_master_key(session_key)
|
self.master_key = sk.get_master_key(base64.b64decode(session_key))
|
||||||
except SessionKey.DoesNotExist:
|
except SessionKey.DoesNotExist:
|
||||||
form.add_error(None, "No session key found for this user.")
|
messages.error(request, "No session key found for this user.")
|
||||||
|
|
||||||
if master_key is None:
|
if self.master_key is not None:
|
||||||
form.add_error(None, "Invalid private key! Unable to encrypt secret data.")
|
return super(SecretBulkImportView, self).post(request)
|
||||||
else:
|
else:
|
||||||
try:
|
messages.error(request, "Invalid private key! Unable to encrypt secret data.")
|
||||||
with transaction.atomic():
|
|
||||||
for secret in form.cleaned_data['csv']:
|
|
||||||
secret.encrypt(master_key)
|
|
||||||
secret.save()
|
|
||||||
new_secrets.append(secret)
|
|
||||||
|
|
||||||
table = tables.SecretTable(new_secrets)
|
else:
|
||||||
messages.success(request, "Imported {} new secrets.".format(len(new_secrets)))
|
messages.error(request, "No session key was provided with the request. Unable to encrypt secret data.")
|
||||||
|
|
||||||
return render(request, 'import_success.html', {
|
return render(request, self.template_name, {
|
||||||
'table': table,
|
'form': self._import_form(request.POST),
|
||||||
'return_url': 'secrets:secret_list',
|
'fields': self.model_form().fields,
|
||||||
})
|
'obj_type': self.model_form._meta.model._meta.verbose_name,
|
||||||
|
'return_url': self.default_return_url,
|
||||||
except IntegrityError as e:
|
})
|
||||||
form.add_error('csv', "Record {}: {}".format(len(new_secrets) + 1, e.__cause__))
|
|
||||||
|
|
||||||
else:
|
|
||||||
form = forms.SecretImportForm()
|
|
||||||
|
|
||||||
return render(request, 'secrets/secret_import.html', {
|
|
||||||
'form': form,
|
|
||||||
'return_url': 'secrets:secret_list',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
@ -10,7 +10,7 @@ from circuits.models import Circuit
|
|||||||
from dcim.models import Site, Rack, Device
|
from dcim.models import Site, Rack, Device
|
||||||
from ipam.models import IPAddress, Prefix, VLAN, VRF
|
from ipam.models import IPAddress, Prefix, VLAN, VRF
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
BulkDeleteView, BulkEditView, BulkImportView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
)
|
)
|
||||||
from .models import Tenant, TenantGroup
|
from .models import Tenant, TenantGroup
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
|
@ -448,6 +448,12 @@ class BulkImportView2(View):
|
|||||||
|
|
||||||
return ImportForm(*args, **kwargs)
|
return ImportForm(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_obj(self, obj_form):
|
||||||
|
"""
|
||||||
|
Provide a hook to modify the object immediately before saving it (e.g. to encrypt secret data).
|
||||||
|
"""
|
||||||
|
return obj_form.save()
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
@ -471,7 +477,7 @@ class BulkImportView2(View):
|
|||||||
for row, data in enumerate(form.cleaned_data['csv'], start=1):
|
for row, data in enumerate(form.cleaned_data['csv'], start=1):
|
||||||
obj_form = self.model_form(data)
|
obj_form = self.model_form(data)
|
||||||
if obj_form.is_valid():
|
if obj_form.is_valid():
|
||||||
obj = obj_form.save()
|
obj = self._save_obj(obj_form)
|
||||||
new_objs.append(obj)
|
new_objs.append(obj)
|
||||||
else:
|
else:
|
||||||
for field, err in obj_form.errors.items():
|
for field, err in obj_form.errors.items():
|
||||||
@ -501,9 +507,6 @@ class BulkImportView2(View):
|
|||||||
'return_url': self.default_return_url,
|
'return_url': self.default_return_url,
|
||||||
})
|
})
|
||||||
|
|
||||||
def save_obj(self, obj):
|
|
||||||
obj.save()
|
|
||||||
|
|
||||||
|
|
||||||
class BulkEditView(View):
|
class BulkEditView(View):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user