Browse Source

Add require_sudo_mode decorator

Planning to use it for sensitive operations (add/remove security keys),
change email, change password, close account.

The decorator sends a six-digit confirmation code to user's email
and renders a form for entering it back. If the user enters the
correct code, the decorators sets a sudo=active marker in
user's session, valid for 30 minutes.
pull/456/head
Pēteris Caune 4 years ago
parent
commit
e3aedd3b03
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
6 changed files with 130 additions and 0 deletions
  1. +47
    -0
      hc/accounts/decorators.py
  2. +4
    -0
      hc/lib/emails.py
  3. +39
    -0
      templates/accounts/sudo.html
  4. +27
    -0
      templates/emails/sudo-code-body-html.html
  5. +12
    -0
      templates/emails/sudo-code-body-text.html
  6. +1
    -0
      templates/emails/sudo-code-subject.html

+ 47
- 0
hc/accounts/decorators.py View File

@ -0,0 +1,47 @@
from functools import wraps
import secrets
from django.core.signing import TimestampSigner, SignatureExpired
from django.shortcuts import redirect, render
from hc.lib import emails
def _session_unsign(request, key, max_age):
if key not in request.session:
return None
try:
return TimestampSigner().unsign(request.session[key], max_age=max_age)
except SignatureExpired:
pass
def require_sudo_mode(f):
@wraps(f)
def wrapper(request, *args, **kwds):
assert request.user.is_authenticated
# is sudo mode active and has not expired yet?
if _session_unsign(request, "sudo", 1800) == "active":
return f(request, *args, **kwds)
# has the user submitted a code to enter sudo mode?
if "sudo_code" in request.POST:
ours = _session_unsign(request, "sudo_code", 900)
if ours and ours == request.POST["sudo_code"]:
request.session.pop("sudo_code")
request.session["sudo"] = TimestampSigner().sign("active")
return redirect(request.path)
if not _session_unsign(request, "sudo_code", 900):
code = "%06d" % secrets.randbelow(1000000)
request.session["sudo_code"] = TimestampSigner().sign(code)
emails.sudo_code(request.user.email, {"sudo_code": code})
ctx = {}
if "sudo_code" in request.POST:
ctx["wrong_code"] = True
return render(request, "accounts/sudo.html", ctx)
return wrapper

+ 4
- 0
hc/lib/emails.py View File

@ -92,3 +92,7 @@ def sms_limit(to, ctx):
def call_limit(to, ctx): def call_limit(to, ctx):
send("phone-call-limit", to, ctx) send("phone-call-limit", to, ctx)
def sudo_code(to, ctx):
send("sudo-code", to, ctx)

+ 39
- 0
templates/accounts/sudo.html View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="hc-dialog">
<h1>Enter a Confirmation Code</h1>
<br />
<p>
We have sent a confirmation code to your email
address. Please enter it below to continue:
</p>
<form method="POST">
{% csrf_token %}
<div class="form-group {% if wrong_code %}has-error{% endif %}">
<input
class="form-control input-lg"
type="text" name="sudo_code" />
{% if wrong_code %}
<div class="help-block">The entered code was not correct.</div>
{% endif %}
</div>
<button
class="btn btn-lg btn-primary btn-block"
type="submit">
Continue
</button>
</form>
</div>
</div>
</div>
{% endblock %}

+ 27
- 0
templates/emails/sudo-code-body-html.html View File

@ -0,0 +1,27 @@
{% extends "emails/base.html" %}
{% load hc_extras %}
{% block content %}
Hello,<br />
<br />
We send a confirmation code before performing sensitive actions on {% site_name%}.<br />
Your confirmation code is:<br />
<br />
<div style="font-family: monospace; font-weight: bold; font-size: 24px">
{{ sudo_code }}
</div>
<br />
Note: this code is only valid for 15 minutes.<br />
<br />
{% endblock %}
{% block content_more %}
All the Best,<br>
The {% site_name %} Team
<br /><br />
{% endblock %}

+ 12
- 0
templates/emails/sudo-code-body-text.html View File

@ -0,0 +1,12 @@
{% load hc_extras %}
Hello,
We send a confirmation code before performing sensitive actions on {% site_name%}.
Your confirmation code is: {{ sudo_code }}
Note: this code is only valid for 15 minutes.
--
All the Best,
The {% site_name %} Team

+ 1
- 0
templates/emails/sudo-code-subject.html View File

@ -0,0 +1 @@
Confirmation code: {{ sudo_code }}

Loading…
Cancel
Save