forked from GithubBackups/healthchecks
Password strength meter and length check in the "Set Password" form
This commit is contained in:
parent
afaa8767cd
commit
23b197526c
@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Can configure the email integration to only report the "down" events (#231)
|
- Can configure the email integration to only report the "down" events (#231)
|
||||||
- Add "Test!" function in the Integrations page (#207)
|
- Add "Test!" function in the Integrations page (#207)
|
||||||
- Rate limiting for the log in attempts
|
- Rate limiting for the log in attempts
|
||||||
|
- Password strength meter and length check in the "Set Password" form
|
||||||
|
|
||||||
## 1.6.0 - 2019-04-01
|
## 1.6.0 - 2019-04-01
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class ReportSettingsForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class SetPasswordForm(forms.Form):
|
class SetPasswordForm(forms.Form):
|
||||||
password = forms.CharField()
|
password = forms.CharField(min_length=8)
|
||||||
|
|
||||||
|
|
||||||
class ChangeEmailForm(forms.Form):
|
class ChangeEmailForm(forms.Form):
|
||||||
|
49
hc/accounts/tests/test_set_password.py
Normal file
49
hc/accounts/tests/test_set_password.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class SetPasswordTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def test_it_shows_form(self):
|
||||||
|
token = self.profile.prepare_token("set-password")
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get("/accounts/set_password/%s/" % token)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
self.assertContains(r, "Please pick a password")
|
||||||
|
|
||||||
|
def test_it_checks_token(self):
|
||||||
|
self.profile.prepare_token("set-password")
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
# GET
|
||||||
|
r = self.client.get("/accounts/set_password/invalid-token/")
|
||||||
|
self.assertEqual(r.status_code, 400)
|
||||||
|
|
||||||
|
# POST
|
||||||
|
r = self.client.post("/accounts/set_password/invalid-token/")
|
||||||
|
self.assertEqual(r.status_code, 400)
|
||||||
|
|
||||||
|
def test_it_sets_password(self):
|
||||||
|
token = self.profile.prepare_token("set-password")
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
payload = {"password": "correct horse battery staple"}
|
||||||
|
r = self.client.post("/accounts/set_password/%s/" % token, payload)
|
||||||
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
|
old_password = self.alice.password
|
||||||
|
self.alice.refresh_from_db()
|
||||||
|
self.assertNotEqual(self.alice.password, old_password)
|
||||||
|
|
||||||
|
def test_post_checks_length(self):
|
||||||
|
token = self.profile.prepare_token("set-password")
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
payload = {"password": "abc"}
|
||||||
|
r = self.client.post("/accounts/set_password/%s/" % token, payload)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
old_password = self.alice.password
|
||||||
|
self.alice.refresh_from_db()
|
||||||
|
self.assertEqual(self.alice.password, old_password)
|
37
static/css/set_password.css
Normal file
37
static/css/set_password.css
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#set-password-group #password {
|
||||||
|
border-color: #ddd;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter {
|
||||||
|
margin: 3px 0 20px 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter div {
|
||||||
|
background: #ddd;
|
||||||
|
height: 5px;
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter div:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter.score-1 .s1 {
|
||||||
|
background: #FF5252;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter.score-2 .s2 {
|
||||||
|
background: #FFAB40;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter.score-3 .s3 {
|
||||||
|
background: #9CCC65;
|
||||||
|
}
|
||||||
|
|
||||||
|
#meter.score-4 .s4 {
|
||||||
|
background: #22bc66;;
|
||||||
|
}
|
8
static/js/set-password.js
Normal file
8
static/js/set-password.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
$(function () {
|
||||||
|
$pw = $("#password");
|
||||||
|
$meter = $("#meter");
|
||||||
|
$pw.on("input", function() {
|
||||||
|
var result = zxcvbn($pw.val());
|
||||||
|
$meter.attr("class", "score-" + result.score);
|
||||||
|
});
|
||||||
|
});
|
28
static/js/zxcvbn.js
Normal file
28
static/js/zxcvbn.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load hc_extras %}
|
{% load compress hc_extras static %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -15,17 +15,20 @@
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div id="set-password-group" class="form-group">
|
||||||
<div class="input-group input-group-lg">
|
<input
|
||||||
<div class="input-group-addon">
|
id="password"
|
||||||
<span class="icon-dots"></span>
|
type="password"
|
||||||
</div>
|
minlength="8"
|
||||||
<input
|
class="form-control input-lg"
|
||||||
type="password"
|
name="password"
|
||||||
class="form-control"
|
placeholder="pick a password (at least 8 characters)">
|
||||||
name="password"
|
<div id="meter">
|
||||||
placeholder="pick a password">
|
<div class="s1 s2 s3 s4"></div>
|
||||||
</div>
|
<div class="s2 s3 s4"></div>
|
||||||
|
<div class="s3 s4"></div>
|
||||||
|
<div class="s4"></div>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
@ -38,3 +41,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% 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/zxcvbn.js' %}"></script>
|
||||||
|
<script src="{% static 'js/set-password.js' %}"></script>
|
||||||
|
{% endcompress %}
|
||||||
|
{% endblock %}
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/snippet-copy.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/snippet-copy.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/syntax.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/syntax.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/welcome.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/welcome.css' %}" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{% static 'css/set_password.css' %}" type="text/css">
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
</head>
|
</head>
|
||||||
<body class="page-{{ page }}">
|
<body class="page-{{ page }}">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user