summaryrefslogtreecommitdiffstats
path: root/utils.py
diff options
context:
space:
mode:
authorMarian Sigler <m@qjym.de>2012-09-21 06:18:38 +0200
committerMarian Sigler <m@qjym.de>2012-09-21 06:18:38 +0200
commitec2c11c9b21f7fcbf79bdf2b57eff911d8c66bd9 (patch)
tree480fca283d4a37f45fa49d3bc66da48c8080e027 /utils.py
parent67dbc8ad19e6ee1cc7f919ea837dd497a7e15bf9 (diff)
downloadweb-ec2c11c9b21f7fcbf79bdf2b57eff911d8c66bd9.tar.gz
web-ec2c11c9b21f7fcbf79bdf2b57eff911d8c66bd9.tar.bz2
web-ec2c11c9b21f7fcbf79bdf2b57eff911d8c66bd9.zip
Add confirmation to registration.
First, only ask for username and email, then send out a confirmation mail. When the link therein is clicked, ask for a password and create the account in LDAP.
Diffstat (limited to 'utils.py')
-rw-r--r--utils.py34
1 files changed, 24 insertions, 10 deletions
diff --git a/utils.py b/utils.py
index b749444..49a69d3 100644
--- a/utils.py
+++ b/utils.py
@@ -3,12 +3,16 @@ import hmac
import ldap
import pickle
import re
+import struct
+import subprocess
+from base64 import urlsafe_b64encode, urlsafe_b64decode
from Crypto.Cipher import AES
from email.mime.text import MIMEText
from functools import wraps
from flask import current_app, flash, g, redirect, render_template, request, session, url_for
from hashlib import sha1
from random import randint
+from time import time
from werkzeug.exceptions import Forbidden
@@ -86,7 +90,7 @@ def decrypt_password(ciphertext):
return encryptor.decrypt(ciphertext[16:]).rstrip('\0')
-def create_confirmation(realm, data):
+def make_confirmation(realm, data):
"""
Create a confirmation token e.g. for confirmation mails.
@@ -94,26 +98,36 @@ def create_confirmation(realm, data):
some data (pickle-able).
"""
key = '\0'.join((current_app.config['SECRET_KEY'], realm))
- payload = pickle.dumps(data)
+ payload = ''.join((struct.pack('>i', time()), pickle.dumps(data)))
mac = hmac.new(key, payload, sha1)
- return ''.join((mac.digest(), payload)).encode('base64').strip()
+ return urlsafe_b64encode(''.join((mac.digest(), payload))).rstrip('=')
-class InvalidConfirmation(ValueError):
+class ConfirmationInvalid(ValueError):
"""Raised by `verify_confirmation` on invalid input data"""
-def verify_confirmation(realm, token):
+class ConfirmationTimeout(ValueError):
+ """Raised by `verify_confirmation` when the input data is too old"""
+
+def verify_confirmation(realm, token, timeout=None):
"""
- Verify a confirmation created by `create_confirmation` and, if it is
+ Verify a confirmation token created by `make_confirmation` and, if it is
valid, return the original data.
+ If `timeout` is given, only accept the token if it is less than `timeout`
+ seconds old.
"""
key = '\0'.join((current_app.config['SECRET_KEY'], realm))
- token = token.decode('base64')
+ token = urlsafe_b64decode(token + '=' * (4 - len(token) % 4))
mac = token[:20]
- payload = token[20:]
+ tokentime = struct.unpack('>i', token[20:24])[0]
+ payload = token[24:]
+
+ if mac != hmac.new(key, token[20:], sha1).digest():
+ raise ConfirmationInvalid('MAC does not match')
- if mac != hmac.new(key, payload, sha1).digest():
- raise InvalidConfirmation('MAC does not match')
+ print '%d+%d=%d <> %d' % (tokentime, timeout, tokentime+timeout, time())
+ if timeout is not None and time() > tokentime + timeout:
+ raise ConfirmationTimeout('Token is too old')
return pickle.loads(payload)