1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Secrets UI work

This commit is contained in:
Jeremy Stretch
2017-03-14 12:32:08 -04:00
parent dd27950fae
commit 105d17748e
5 changed files with 62 additions and 53 deletions

View File

@ -1,38 +1,20 @@
$(document).ready(function() { $(document).ready(function() {
// Unlocking a secret // Unlocking a secret
$('button.unlock-secret').click(function (event) { $('button.unlock-secret').click(function() {
var secret_id = $(this).attr('secret-id'); var secret_id = $(this).attr('secret-id');
// If we have an active cookie containing a session key, send the API request.
if (document.cookie.indexOf('session_key') > 0) {
console.log("Retrieving secret...");
unlock_secret(secret_id); unlock_secret(secret_id);
// Otherwise, prompt the user for a private key so we can request a session key.
} else {
console.log("No session key found. Prompt user for private key.");
$('#privkey_modal').modal('show');
}
}); });
// Locking a secret // Locking a secret
$('button.lock-secret').click(function (event) { $('button.lock-secret').click(function() {
var secret_id = $(this).attr('secret-id'); var secret_id = $(this).attr('secret-id');
var secret_div = $('#secret_' + secret_id); lock_secret(secret_id);
// Delete the plaintext from the DOM element.
secret_div.html('********');
$(this).hide();
$(this).siblings('button.unlock-secret').show();
}); });
// Retrieve a session key // Retrieve a session key
$('#request_session_key').click(function() { $('#request_session_key').click(function() {
var private_key = $('#user_privkey').val(); var private_key = $('#user_privkey').val();
// POST the user's private key to request a temporary session key.
console.log("Requesting a session key...");
get_session_key(private_key); get_session_key(private_key);
}); });
@ -43,23 +25,35 @@ $(document).ready(function() {
type: 'GET', type: 'GET',
dataType: 'json', dataType: 'json',
success: function (response, status) { success: function (response, status) {
if (response.plaintext) {
console.log("Secret retrieved successfully"); console.log("Secret retrieved successfully");
$('#secret_' + secret_id).html(response.plaintext); $('#secret_' + secret_id).html(response.plaintext);
$('button.unlock-secret[secret-id=' + secret_id + ']').hide(); $('button.unlock-secret[secret-id=' + secret_id + ']').hide();
$('button.lock-secret[secret-id=' + secret_id + ']').show(); $('button.lock-secret[secret-id=' + secret_id + ']').show();
} else {
console.log("Secret was not decrypted. Prompt user for private key.");
$('#privkey_modal').modal('show');
}
}, },
error: function (xhr, ajaxOptions, thrownError) { error: function (xhr, ajaxOptions, thrownError) {
console.log("Error: " + xhr.responseText); console.log("Error: " + xhr.responseText);
if (xhr.status == 403) { if (xhr.status == 403) {
alert("Permission denied"); alert("Permission denied");
} else { } else {
var json = jQuery.parseJSON(xhr.responseText); alert(xhr.responseText);
alert("Secret retrieval failed: " + json['error']);
} }
} }
}); });
} }
// Remove secret data from the DOM
function lock_secret(secret_id) {
var secret_div = $('#secret_' + secret_id);
secret_div.html('********');
$('button.lock-secret[secret-id=' + secret_id + ']').hide();
$('button.unlock-secret[secret-id=' + secret_id + ']').show();
}
// Request a session key via the API // Request a session key via the API
function get_session_key(private_key) { function get_session_key(private_key) {
var csrf_token = $('input[name=csrfmiddlewaretoken]').val(); var csrf_token = $('input[name=csrfmiddlewaretoken]').val();
@ -74,7 +68,7 @@ $(document).ready(function() {
xhr.setRequestHeader("X-CSRFToken", csrf_token); xhr.setRequestHeader("X-CSRFToken", csrf_token);
}, },
success: function (response, status) { success: function (response, status) {
console.log("Received a new session key; valid until " + response.expiration_time); console.log("Received a new session key");
alert('Session key received! You may now unlock secrets.'); alert('Session key received! You may now unlock secrets.');
}, },
error: function (xhr, ajaxOptions, thrownError) { error: function (xhr, ajaxOptions, thrownError) {

View File

@ -11,6 +11,7 @@ from rest_framework.response import Response
from rest_framework.viewsets import ViewSet, ModelViewSet from rest_framework.viewsets import ViewSet, ModelViewSet
from extras.api.renderers import FormlessBrowsableAPIRenderer, FreeRADIUSClientsRenderer from extras.api.renderers import FormlessBrowsableAPIRenderer, FreeRADIUSClientsRenderer
from secrets.exceptions import InvalidSessionKey
from secrets.filters import SecretFilter from secrets.filters import SecretFilter
from secrets.models import Secret, SecretRole, SessionKey, UserKey from secrets.models import Secret, SecretRole, SessionKey, UserKey
from utilities.api import WritableSerializerMixin from utilities.api import WritableSerializerMixin
@ -53,42 +54,50 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
authentication_classes = [BasicAuthentication, SessionAuthentication] authentication_classes = [BasicAuthentication, SessionAuthentication]
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def _get_master_key(self, request): def _read_session_key(self, request):
# Check for a session key provided as a cookie or header # Check for a session key provided as a cookie or header
if 'session_key' in request.COOKIES: if 'session_key' in request.COOKIES:
session_key = base64.b64decode(request.COOKIES['session_key']) return base64.b64decode(request.COOKIES['session_key'])
elif 'HTTP_X_SESSION_KEY' in request.META: elif 'HTTP_X_SESSION_KEY' in request.META:
session_key = base64.b64decode(request.META['HTTP_X_SESSION_KEY']) return base64.b64decode(request.META['HTTP_X_SESSION_KEY'])
else:
return None return None
# Retrieve session key cipher (if any) for the current user
try:
sk = SessionKey.objects.get(user=request.user)
except SessionKey.DoesNotExist:
return None
# Recover master key
# TODO: Exception handling
master_key = sk.get_master_key(session_key)
return master_key
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
master_key = self._get_master_key(request)
secret = self.get_object()
if master_key is not None: secret = self.get_object()
session_key = self._read_session_key(request)
# Retrieve session key cipher (if any) for the current user
if session_key is not None:
try:
sk = SessionKey.objects.get(user=request.user)
master_key = sk.get_master_key(session_key)
secret.decrypt(master_key) secret.decrypt(master_key)
except SessionKey.DoesNotExist:
return HttpResponseBadRequest("No active session key for current user.")
except InvalidSessionKey:
return HttpResponseBadRequest("Invalid session key.")
serializer = self.get_serializer(secret) serializer = self.get_serializer(secret)
return Response(serializer.data) return Response(serializer.data)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
master_key = self._get_master_key(request)
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
# Attempt to retrieve the master key for decryption
session_key = self._read_session_key(request)
master_key = None
if session_key is not None:
try:
sk = SessionKey.objects.get(user=request.user)
master_key = sk.get_master_key(session_key)
except SessionKey.DoesNotExist:
return HttpResponseBadRequest("No active session key for current user.")
except InvalidSessionKey:
return HttpResponseBadRequest("Invalid session key.")
# Pagination # Pagination
page = self.paginate_queryset(queryset) page = self.paginate_queryset(queryset)
if page is not None: if page is not None:

View File

@ -0,0 +1,5 @@
class InvalidSessionKey(Exception):
"""
Raised when the a provided session key is invalid.
"""
pass

View File

@ -13,6 +13,7 @@ from django.utils.encoding import force_bytes, python_2_unicode_compatible
from dcim.models import Device from dcim.models import Device
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
from .exceptions import InvalidSessionKey
from .hashers import SecretValidationHasher from .hashers import SecretValidationHasher
@ -220,7 +221,7 @@ class SessionKey(models.Model):
# Validate the provided session key # Validate the provided session key
if not check_password(session_key, self.hash): if not check_password(session_key, self.hash):
raise Exception("Invalid session key") raise InvalidSessionKey()
# Decrypt master key using provided session key # Decrypt master key using provided session key
master_key = xor_keys(session_key, self.cipher) master_key = xor_keys(session_key, self.cipher)

View File

@ -17,7 +17,7 @@
<textarea class="form-control" id="user_privkey" style="height: 300px;"></textarea> <textarea class="form-control" id="user_privkey" style="height: 300px;"></textarea>
</div> </div>
<div class="form-group text-right"> <div class="form-group text-right">
<button id="request_session_key" class="btn btn-primary unlock-secret" data-dismiss="modal"> <button id="request_session_key" class="btn btn-primary" data-dismiss="modal">
Request session key Request session key
</button> </button>
</div> </div>