mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-11 15:51:19 +00:00
Implement a "Remove Security Key" feature
This commit is contained in:
parent
42497fe91a
commit
2ac0f87560
9 changed files with 118 additions and 12 deletions
hc/accounts
static/js
templates/accounts
|
@ -119,7 +119,7 @@ class TransferForm(forms.Form):
|
|||
|
||||
|
||||
class AddCredentialForm(forms.Form):
|
||||
name = forms.CharField(max_length=100, required=False)
|
||||
name = forms.CharField(max_length=100)
|
||||
client_data_json = forms.CharField(required=True)
|
||||
attestation_object = forms.CharField(required=True)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.1.2 on 2020-11-12 15:29
|
||||
# Generated by Django 3.1.2 on 2020-11-14 09:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('code', models.UUIDField(default=uuid.uuid4, unique=True)),
|
||||
('name', models.CharField(blank=True, max_length=200)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('data', models.BinaryField()),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='credentials', to=settings.AUTH_USER_MODEL)),
|
||||
|
|
|
@ -395,7 +395,7 @@ class Member(models.Model):
|
|||
|
||||
class Credential(models.Model):
|
||||
code = models.UUIDField(default=uuid.uuid4, unique=True)
|
||||
name = models.CharField(max_length=200, blank=True)
|
||||
name = models.CharField(max_length=100)
|
||||
user = models.ForeignKey(User, models.CASCADE, related_name="credentials")
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
data = models.BinaryField()
|
||||
|
|
|
@ -24,4 +24,9 @@ urlpatterns = [
|
|||
path("change_email/done/", views.change_email_done, name="hc-change-email-done"),
|
||||
path("change_email/<slug:token>/", views.change_email, name="hc-change-email"),
|
||||
path("two_factor/add/", views.add_credential, name="hc-add-credential"),
|
||||
path(
|
||||
"two_factor/<uuid:code>/remove/",
|
||||
views.remove_credential,
|
||||
name="hc-remove-credential",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -203,7 +203,21 @@ def check_token(request, username, token):
|
|||
def profile(request):
|
||||
profile = request.profile
|
||||
|
||||
ctx = {"page": "profile", "profile": profile, "my_projects_status": "default"}
|
||||
ctx = {
|
||||
"page": "profile",
|
||||
"profile": profile,
|
||||
"my_projects_status": "default",
|
||||
"tfa_status": "default",
|
||||
"added_credential_name": request.session.pop("added_credential_name", ""),
|
||||
"removed_credential_name": request.session.pop("removed_credential_name", ""),
|
||||
"credentials": request.user.credentials.order_by("id"),
|
||||
}
|
||||
|
||||
if ctx["added_credential_name"]:
|
||||
ctx["tfa_status"] = "success"
|
||||
|
||||
if ctx["removed_credential_name"]:
|
||||
ctx["tfa_status"] = "info"
|
||||
|
||||
if request.method == "POST":
|
||||
if "change_email" in request.POST:
|
||||
|
@ -575,6 +589,7 @@ def add_credential(request):
|
|||
c.data = auth_data.credential_data
|
||||
c.save()
|
||||
|
||||
request.session["added_credential_name"] = c.name
|
||||
return redirect("hc-profile")
|
||||
|
||||
credentials = [c.unpack() for c in request.user.credentials.all()]
|
||||
|
@ -591,3 +606,20 @@ def add_credential(request):
|
|||
|
||||
ctx = {"options": base64.b64encode(cbor.encode(options)).decode()}
|
||||
return render(request, "accounts/add_credential.html", ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_sudo_mode
|
||||
def remove_credential(request, code):
|
||||
try:
|
||||
credential = Credential.objects.get(user=request.user, code=code)
|
||||
except Credential.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if request.method == "POST" and "remove_credential" in request.POST:
|
||||
request.session["removed_credential_name"] = credential.name
|
||||
credential.delete()
|
||||
return redirect("hc-profile")
|
||||
|
||||
ctx = {"credential": credential}
|
||||
return render(request, "accounts/remove_credential.html", ctx)
|
||||
|
|
|
@ -30,7 +30,14 @@ $(function() {
|
|||
});
|
||||
}
|
||||
|
||||
$("#name").on('keypress',function(e) {
|
||||
if (e.which == 13) {
|
||||
e.preventDefault();
|
||||
requestCredentials();
|
||||
}
|
||||
});
|
||||
|
||||
$("#name-next").click(requestCredentials);
|
||||
$("#retry").click(requestCredentials);
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,12 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
required>
|
||||
<div class="help-block">
|
||||
Give this credential a descriptive name. Example: "My primary Yubikey"
|
||||
</div>
|
||||
|
|
|
@ -59,30 +59,33 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel panel-{{ tfa_status }}">
|
||||
<div class="panel-body settings-block">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<h2>Two-factor Authentication</h2>
|
||||
{% if profile.user.credentials.exists %}
|
||||
{% if credentials.exists %}
|
||||
|
||||
<table id="my-keys" class="table">
|
||||
<tr>
|
||||
<th>Security keys</th>
|
||||
</tr>
|
||||
{% for credential in profile.user.credentials.all %}
|
||||
{% for credential in credentials %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ credential.name|default:"unnamed" }}</strong>
|
||||
– registered on {{ credential.created|date:"M j, Y" }}
|
||||
</td>
|
||||
<td class="text-right"><a href="#">Remove</a></td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'hc-remove-credential' credential.code %}">Remove</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% else %}
|
||||
<p>
|
||||
Your account has no registered security keys.
|
||||
Your account has no registered security keys.<br />
|
||||
Two-factor authentication is disabled.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
@ -93,6 +96,18 @@
|
|||
</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if added_credential_name %}
|
||||
<div class="panel-footer">
|
||||
Added security key <strong>{{ added_credential_name }}</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if removed_credential_name %}
|
||||
<div class="panel-footer">
|
||||
Removed security key <strong>{{ removed_credential_name }}</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
|
|
42
templates/accounts/remove_credential.html
Normal file
42
templates/accounts/remove_credential.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
{% load compress static hc_extras %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ registration_dict|json_script:"registration" }}
|
||||
<div class="row">
|
||||
<form class="col-sm-6 col-sm-offset-3" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body settings-block">
|
||||
<h2>Remove Security Key?</h2>
|
||||
<p></p>
|
||||
<p>You are about to remove
|
||||
the security key <strong>{{ credential.name|default:'unnamed' }}</strong>
|
||||
from your two-factor authentication methods. Are you sure?
|
||||
</p>
|
||||
<div class="text-right">
|
||||
<a
|
||||
href="{% url 'hc-profile' %}"
|
||||
class="btn btn-default">Cancel</a>
|
||||
<button
|
||||
type="submit"
|
||||
name="remove_credential"
|
||||
class="btn btn-danger">Remove Security Key</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% compress js %}
|
||||
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'js/cbor.js' %}"></script>
|
||||
<script src="{% static 'js/add_credential.js' %}"></script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
Loading…
Add table
Reference in a new issue