From ec2c11c9b21f7fcbf79bdf2b57eff911d8c66bd9 Mon Sep 17 00:00:00 2001 From: Marian Sigler Date: Fri, 21 Sep 2012 06:18:38 +0200 Subject: 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. --- utils.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'utils.py') 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) -- cgit v1.2.3-1-g7c22