Browse Source

Set and revoke API key in Settings page.

pull/46/head
Pēteris Caune 9 years ago
parent
commit
5d2cc0b0fc
7 changed files with 181 additions and 12 deletions
  1. +20
    -0
      hc/accounts/migrations/0004_profile_api_key.py
  2. +9
    -1
      hc/accounts/models.py
  3. +43
    -0
      hc/accounts/tests/test_profile.py
  4. +18
    -6
      hc/accounts/views.py
  5. +20
    -0
      hc/api/migrations/0025_auto_20160216_1214.py
  6. +0
    -4
      static/js/checks.js
  7. +71
    -1
      templates/accounts/profile.html

+ 20
- 0
hc/accounts/migrations/0004_profile_api_key.py View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-02-16 12:14
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0003_profile_token'),
]
operations = [
migrations.AddField(
model_name='profile',
name='api_key',
field=models.CharField(blank=True, max_length=128),
),
]

+ 9
- 1
hc/accounts/models.py View File

@ -1,4 +1,8 @@
import base64
import os
import uuid
from datetime import timedelta from datetime import timedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -7,7 +11,6 @@ from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from hc.lib import emails from hc.lib import emails
import uuid
class ProfileManager(models.Manager): class ProfileManager(models.Manager):
@ -28,6 +31,7 @@ class Profile(models.Model):
reports_allowed = models.BooleanField(default=True) reports_allowed = models.BooleanField(default=True)
ping_log_limit = models.IntegerField(default=100) ping_log_limit = models.IntegerField(default=100)
token = models.CharField(max_length=128, blank=True) token = models.CharField(max_length=128, blank=True)
api_key = models.CharField(max_length=128, blank=True)
objects = ProfileManager() objects = ProfileManager()
@ -49,6 +53,10 @@ class Profile(models.Model):
ctx = {"set_password_link": settings.SITE_ROOT + path} ctx = {"set_password_link": settings.SITE_ROOT + path}
emails.set_password(self.user.email, ctx) emails.set_password(self.user.email, ctx)
def set_api_key(self):
self.api_key = base64.urlsafe_b64encode(os.urandom(24))
self.save()
def send_report(self): def send_report(self):
# reset next report date first: # reset next report date first:
now = timezone.now() now = timezone.now()


+ 43
- 0
hc/accounts/tests/test_profile.py View File

@ -0,0 +1,43 @@
from django.core import mail
from hc.test import BaseTestCase
from hc.accounts.models import Profile
class LoginTestCase(BaseTestCase):
def test_it_sends_set_password_link(self):
self.client.login(username="[email protected]", password="password")
form = {"set_password": "1"}
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 302
# profile.token should be set now
profile = Profile.objects.for_user(self.alice)
self.assertTrue(len(profile.token) > 10)
# And an email should have been sent
self.assertEqual(len(mail.outbox), 1)
expected_subject = 'Set password on healthchecks.io'
self.assertEqual(mail.outbox[0].subject, expected_subject)
def test_it_creates_api_key(self):
self.client.login(username="[email protected]", password="password")
form = {"create_api_key": "1"}
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 200
profile = Profile.objects.for_user(self.alice)
self.assertTrue(len(profile.api_key) > 10)
def test_it_revokes_api_key(self):
self.client.login(username="[email protected]", password="password")
form = {"revoke_api_key": "1"}
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 200
profile = Profile.objects.for_user(self.alice)
self.assertEqual(profile.api_key, "")

+ 18
- 6
hc/accounts/views.py View File

@ -120,19 +120,31 @@ def check_token(request, username, token):
def profile(request): def profile(request):
profile = Profile.objects.for_user(request.user) profile = Profile.objects.for_user(request.user)
show_api_key = False
if request.method == "POST": if request.method == "POST":
if "set_password" in request.POST: if "set_password" in request.POST:
profile.send_set_password_link() profile.send_set_password_link()
return redirect("hc-set-password-link-sent") return redirect("hc-set-password-link-sent")
form = ReportSettingsForm(request.POST)
if form.is_valid():
profile.reports_allowed = form.cleaned_data["reports_allowed"]
elif "create_api_key" in request.POST:
profile.set_api_key()
show_api_key = True
messages.info(request, "The API key has been created!")
elif "revoke_api_key" in request.POST:
profile.api_key = ""
profile.save() profile.save()
messages.info(request, "Your settings have been updated!")
messages.info(request, "The API key has been revoked!")
elif "show_api_key" in request.POST:
show_api_key = True
elif "update_reports_allowed" in request.POST:
form = ReportSettingsForm(request.POST)
if form.is_valid():
profile.reports_allowed = form.cleaned_data["reports_allowed"]
profile.save()
messages.info(request, "Your settings have been updated!")
ctx = { ctx = {
"profile": profile
"profile": profile,
"show_api_key": show_api_key
} }
return render(request, "accounts/profile.html", ctx) return render(request, "accounts/profile.html", ctx)


+ 20
- 0
hc/api/migrations/0025_auto_20160216_1214.py View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-02-16 12:14
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0024_auto_20160203_2227'),
]
operations = [
migrations.AlterField(
model_name='channel',
name='kind',
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('po', 'Pushover'), ('victorops', 'VictorOps')], max_length=20),
),
]

+ 0
- 4
static/js/checks.js View File

@ -165,8 +165,4 @@ $(function () {
}); });
}); });

+ 71
- 1
templates/accounts/profile.html View File

@ -33,6 +33,7 @@
Each month send me a summary of my checks Each month send me a summary of my checks
</label> </label>
<button <button
name="update_reports_allowed"
type="submit" type="submit"
class="btn btn-default pull-right">Save</button> class="btn btn-default pull-right">Save</button>
</form> </form>
@ -47,15 +48,84 @@
{% csrf_token %} {% csrf_token %}
<h2>Set Password</h2> <h2>Set Password</h2>
Attach a password to your healthchecks.io account Attach a password to your healthchecks.io account
<input type="hidden" name="set_password" value="1" />
<button <button
type="submit" type="submit"
value="set_password"
class="btn btn-default pull-right">Set Password</button> class="btn btn-default pull-right">Set Password</button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-body settings-block">
<h2>API Access</h2>
{% if profile.api_key %}
{% if show_api_key %}
API key: <code>{{ profile.api_key }}</code>
<button
data-toggle="modal"
data-target="#revoke-api-key-modal"
class="btn btn-danger pull-right">Revoke</button>
{% else %}
<span class="text-success glyphicon glyphicon-ok"></span>
API access is enabled.
<form method="post">
{% csrf_token %}
<button
type="submit"
name="show_api_key"
class="btn btn-default pull-right">Show API key</button>
</form>
{% endif %}
{% else %}
<span class="glyphicon glyphicon-remove"></span>
API access is disabled.
<form method="post">
{% csrf_token %}
<button
type="submit"
name="create_api_key"
class="btn btn-default pull-right">Create API key</button>
</form>
{% endif %}
</div>
</div>
</div>
</div>
<div id="revoke-api-key-modal" class="modal">
<div class="modal-dialog">
<form id="revoke-api-key-form" method="post">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</span></button>
<h4 class="remove-check-title">Revoke API Key</h4>
</div>
<div class="modal-body">
<p>You are about to revoke the current API key.</p>
<p>Afterwards, you can create a new API key, but there will
be <strong>no way of getting the current API
key back</strong>.
</p>
<p>Are you sure?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button
type="submit"
name="revoke_api_key"
class="btn btn-danger">Revoke API Key</button>
</div>
</div>
</form>
</div>
</div> </div>
{% endblock %} {% endblock %}

Loading…
Cancel
Save