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 utilities.paginator import EnhancedPaginator
|
||||
from utilities.views import (
|
||||
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView, BulkImportView2, ObjectDeleteView, ObjectEditView,
|
||||
ObjectListView,
|
||||
BulkAddView, BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
)
|
||||
from . import filters, forms, tables
|
||||
from .models import (
|
||||
|
@ -7,7 +7,7 @@ from django import forms
|
||||
from django.db.models import Count
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -65,27 +65,37 @@ class SecretForm(BootstrapMixin, forms.ModelForm):
|
||||
})
|
||||
|
||||
|
||||
class SecretFromCSVForm(forms.ModelForm):
|
||||
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Device not found.'})
|
||||
role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Invalid secret role.'})
|
||||
plaintext = forms.CharField()
|
||||
class SecretCSVForm(forms.ModelForm):
|
||||
device = forms.ModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Device name',
|
||||
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:
|
||||
model = Secret
|
||||
fields = ['device', 'role', 'name', 'plaintext']
|
||||
|
||||
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'])
|
||||
return s
|
||||
|
||||
|
||||
class SecretImportForm(BootstrapMixin, BulkImportForm):
|
||||
csv = CSVDataField(csv_form=SecretFromCSVForm, widget=forms.Textarea(attrs={'class': 'requires-session-key'}))
|
||||
|
||||
|
||||
class SecretBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput)
|
||||
role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), required=False)
|
||||
|
@ -16,7 +16,7 @@ urlpatterns = [
|
||||
|
||||
# Secrets
|
||||
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/delete/$', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
||||
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 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 .decorators import userkey_required
|
||||
from .models import SecretRole, Secret, SessionKey
|
||||
@ -185,58 +187,50 @@ class SecretDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||
default_return_url = 'secrets:secret_list'
|
||||
|
||||
|
||||
@permission_required('secrets.add_secret')
|
||||
@userkey_required()
|
||||
def secret_import(request):
|
||||
class SecretBulkImportView(BulkImportView2):
|
||||
permission_required = 'ipam.add_vlan'
|
||||
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':
|
||||
form = forms.SecretImportForm(request.POST)
|
||||
def _save_obj(self, obj_form):
|
||||
"""
|
||||
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:
|
||||
form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
|
||||
def post(self, request):
|
||||
|
||||
if form.is_valid():
|
||||
# Grab the session key from cookies.
|
||||
session_key = request.COOKIES.get('session_key')
|
||||
if session_key:
|
||||
|
||||
new_secrets = []
|
||||
|
||||
session_key = base64.b64decode(session_key)
|
||||
master_key = None
|
||||
# Attempt to derive the master key using the provided session key.
|
||||
try:
|
||||
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:
|
||||
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:
|
||||
form.add_error(None, "Invalid private key! Unable to encrypt secret data.")
|
||||
if self.master_key is not None:
|
||||
return super(SecretBulkImportView, self).post(request)
|
||||
else:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
for secret in form.cleaned_data['csv']:
|
||||
secret.encrypt(master_key)
|
||||
secret.save()
|
||||
new_secrets.append(secret)
|
||||
messages.error(request, "Invalid private key! Unable to encrypt secret data.")
|
||||
|
||||
table = tables.SecretTable(new_secrets)
|
||||
messages.success(request, "Imported {} new secrets.".format(len(new_secrets)))
|
||||
else:
|
||||
messages.error(request, "No session key was provided with the request. Unable to encrypt secret data.")
|
||||
|
||||
return render(request, 'import_success.html', {
|
||||
'table': table,
|
||||
'return_url': 'secrets:secret_list',
|
||||
})
|
||||
|
||||
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',
|
||||
})
|
||||
return render(request, self.template_name, {
|
||||
'form': self._import_form(request.POST),
|
||||
'fields': self.model_form().fields,
|
||||
'obj_type': self.model_form._meta.model._meta.verbose_name,
|
||||
'return_url': self.default_return_url,
|
||||
})
|
||||
|
||||
|
||||
class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
|
@ -10,7 +10,7 @@ from circuits.models import Circuit
|
||||
from dcim.models import Site, Rack, Device
|
||||
from ipam.models import IPAddress, Prefix, VLAN, VRF
|
||||
from utilities.views import (
|
||||
BulkDeleteView, BulkEditView, BulkImportView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
BulkDeleteView, BulkEditView, BulkImportView2, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||
)
|
||||
from .models import Tenant, TenantGroup
|
||||
from . import filters, forms, tables
|
||||
|
@ -448,6 +448,12 @@ class BulkImportView2(View):
|
||||
|
||||
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):
|
||||
|
||||
return render(request, self.template_name, {
|
||||
@ -471,7 +477,7 @@ class BulkImportView2(View):
|
||||
for row, data in enumerate(form.cleaned_data['csv'], start=1):
|
||||
obj_form = self.model_form(data)
|
||||
if obj_form.is_valid():
|
||||
obj = obj_form.save()
|
||||
obj = self._save_obj(obj_form)
|
||||
new_objs.append(obj)
|
||||
else:
|
||||
for field, err in obj_form.errors.items():
|
||||
@ -501,9 +507,6 @@ class BulkImportView2(View):
|
||||
'return_url': self.default_return_url,
|
||||
})
|
||||
|
||||
def save_obj(self, obj):
|
||||
obj.save()
|
||||
|
||||
|
||||
class BulkEditView(View):
|
||||
"""
|
||||
|
Reference in New Issue
Block a user