Browse Source

Improve the "add security key" UX, require sudo mode

pull/456/head
Pēteris Caune 4 years ago
parent
commit
2c3286c280
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
5 changed files with 81 additions and 26 deletions
  1. +2
    -0
      hc/accounts/views.py
  2. +8
    -0
      static/css/add_credential.css
  3. +23
    -12
      static/js/add_credential.js
  4. +47
    -14
      templates/accounts/add_credential.html
  5. +1
    -0
      templates/base.html

+ 2
- 0
hc/accounts/views.py View File

@ -22,6 +22,7 @@ from fido2.server import Fido2Server
from fido2.webauthn import PublicKeyCredentialRpEntity from fido2.webauthn import PublicKeyCredentialRpEntity
from fido2 import cbor from fido2 import cbor
from hc.accounts import forms from hc.accounts import forms
from hc.accounts.decorators import require_sudo_mode
from hc.accounts.models import Credential, Profile, Project, Member from hc.accounts.models import Credential, Profile, Project, Member
from hc.api.models import Channel, Check, TokenBucket from hc.api.models import Channel, Check, TokenBucket
from hc.lib.date import choose_next_report_date from hc.lib.date import choose_next_report_date
@ -552,6 +553,7 @@ def _verify_origin(aaa):
@login_required @login_required
@require_sudo_mode
def add_credential(request): def add_credential(request):
rp = PublicKeyCredentialRpEntity("localhost", "Healthchecks") rp = PublicKeyCredentialRpEntity("localhost", "Healthchecks")
# FIXME use HTTPS, remove the verify_origin hack # FIXME use HTTPS, remove the verify_origin hack


+ 8
- 0
static/css/add_credential.css View File

@ -0,0 +1,8 @@
#add-credential-waiting .spinner {
margin: 0;
}
#add-credential-error-text {
font-family: "Lucida Console", Monaco, monospace;
margin: 16px 0;
}

+ 23
- 12
static/js/add_credential.js View File

@ -3,23 +3,34 @@ $(function() {
var optionsBytes = Uint8Array.from(atob(form.dataset.options), c => c.charCodeAt(0)); var optionsBytes = Uint8Array.from(atob(form.dataset.options), c => c.charCodeAt(0));
// cbor.js expects ArrayBuffer as input when decoding // cbor.js expects ArrayBuffer as input when decoding
var options = CBOR.decode(optionsBytes.buffer); var options = CBOR.decode(optionsBytes.buffer);
console.log("decoded options:", options);
function b64(arraybuffer) { function b64(arraybuffer) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(arraybuffer))); return btoa(String.fromCharCode.apply(null, new Uint8Array(arraybuffer)));
} }
navigator.credentials.create(options).then(function(attestation) {
console.log("got attestation: ", attestation);
function requestCredentials() {
// Hide error & success messages, show the "waiting" message
$("#name-next").addClass("hide");
$("#add-credential-waiting").removeClass("hide");
$("#add-credential-error").addClass("hide");
$("#add-credential-success").addClass("hide");
$("#attestation_object").val(b64(attestation.response.attestationObject));
$("#client_data_json").val(b64(attestation.response.clientDataJSON));
console.log("form updated, all is well");
navigator.credentials.create(options).then(function(attestation) {
$("#attestation_object").val(b64(attestation.response.attestationObject));
$("#client_data_json").val(b64(attestation.response.clientDataJSON));
// Show the success message and save button
$("#add-credential-waiting").addClass("hide");
$("#add-credential-success").removeClass("hide");
}).catch(function(err) {
// Show the error message
$("#add-credential-waiting").addClass("hide");
$("#add-credential-error-text").text(err);
$("#add-credential-error").removeClass("hide");
});
}
$("#name-next").click(requestCredentials);
$("#retry").click(requestCredentials);
$("#add-credential-submit").prop("disabled", "");
$("#add-credential-success").removeClass("hide");
}).catch(function(err) {
$("#add-credential-error span").text(err);
$("#add-credential-error").removeClass("hide");
});
}); });

+ 47
- 14
templates/accounts/add_credential.html View File

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load compress static %}
{% load compress static hc_extras %}
{% block content %} {% block content %}
@ -11,7 +11,7 @@
data-options="{{ options }}" data-options="{{ options }}"
method="post" method="post"
encrypt="multipart/form-data"> encrypt="multipart/form-data">
<h1>Add Credential</h1>
<h1>Add Security Key</h1>
{% csrf_token %} {% csrf_token %}
<input id="attestation_object" type="hidden" name="attestation_object"> <input id="attestation_object" type="hidden" name="attestation_object">
@ -25,23 +25,56 @@
</div> </div>
</div> </div>
<div id="add-credential-error" class="alert alert-danger hide">
<strong>Something went wrong.</strong>
<span></span>
<div class="form-group text-right">
<button
id="name-next"
class="btn btn-default" type="button">
Confirm Name and Continue
</button>
</div> </div>
<div id="add-credential-success" class="alert alert-success hide">
<strong>Success!</strong>
Credential acquired.
<div id="add-credential-waiting" class="hide">
<h2>Waiting for security key</h2>
<p>
Follow your browser's steps to register your security key
with {% site_name %}.
</p>
<div class="spinner started">
<div class="d1"></div>
<div class="d2"></div>
<div class="d3"></div>
</div>
</div> </div>
<div id="add-credential-error" class="alert alert-danger hide">
<p>
<strong>Something went wrong.</strong>
</p>
<p id="add-credential-error-text"></p>
<input
id="add-credential-submit"
class="btn btn-default pull-right"
type="submit"
name=""
value="Save Credential" disabled>
<div class="text-right">
<button id="retry" type="button" class="btn btn-danger">
Try Again
</button>
</div>
</div>
<div id="add-credential-success" class="hide">
<div class="alert alert-success">
<strong>Success!</strong>
Credential acquired.
</div>
<div class="form-group text-right">
<input
id="add-credential-submit"
class="btn btn-primary"
type="submit"
name=""
value="Save Security Key">
</div>
</div>
</form> </form>
</div> </div>
{% endblock %} {% endblock %}


+ 1
- 0
templates/base.html View File

@ -20,6 +20,7 @@
<link rel="stylesheet" href="{% static 'css/bootstrap-select.min.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/bootstrap-select.min.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/selectize.hc.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/selectize.hc.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_credential.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_project_modal.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/add_project_modal.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/webhook_form.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/webhook_form.css' %}" type="text/css">


Loading…
Cancel
Save