Address CL concerns and switch to a real encryption system

This commit is contained in:
Joseph Schorr 2015-03-26 15:10:58 -04:00
parent d23bb6616d
commit aaf1b23e98
6 changed files with 124 additions and 37 deletions

View file

@ -1,7 +1,10 @@
import ldap
import logging
import json
import itertools
import uuid
from flask.sessions import SecureCookieSessionInterface, BadSignature
from util.aes import AESCipher
from util.validation import generate_valid_usernames
from data import model
@ -107,6 +110,7 @@ class LDAPUsers(object):
return found_user is not None
class UserAuthentication(object):
def __init__(self, app=None):
self.app = app
@ -139,23 +143,68 @@ class UserAuthentication(object):
app.extensions['authentication'] = users
return users
def _get_secret_key(self):
""" Returns the secret key to use for encrypting and decrypting. """
from app import app
app_secret_key = app.config['SECRET_KEY']
# First try parsing the key as a float.
try:
secret_key = float(app_secret_key)
except ValueError:
secret_key = app_secret_key
# Next try parsing it as an UUID.
try:
secret_key = uuid.UUID(app_secret_key).bytes
except ValueError:
secret_key = app_secret_key
# Otherwise, use the bytes directly.
return ''.join(itertools.islice(itertools.cycle(secret_key), 32))
def encrypt_user_password(self, password):
""" Returns an encrypted version of the user's password. """
data = {
'password': password
}
message = json.dumps(data)
cipher = AESCipher(self._get_secret_key())
return cipher.encrypt(message)
def _decrypt_user_password(self, encrypted):
""" Attempts to decrypt the given password and returns it. """
cipher = AESCipher(self._get_secret_key())
try:
message = cipher.decrypt(encrypted)
except ValueError:
return None
except TypeError:
return None
try:
data = json.loads(message)
except ValueError:
return None
return data.get('password', encrypted)
def verify_user(self, username_or_email, password, basic_auth=False):
# First try to decode the password as a signed token.
if basic_auth:
from app import app
import features
ser = SecureCookieSessionInterface().get_signing_serializer(app)
try:
token_data = ser.loads(password)
password = token_data.get('password', password)
except BadSignature:
decrypted = self._decrypt_user_password(password)
if decrypted is None:
# This is a normal password.
if features.REQUIRE_ENCRYPTED_BASIC_AUTH:
msg = ('Client login with passwords is disabled. Please generate a client token ' +
'and use it in place of your password.')
return (None, msg)
else:
password = decrypted
result = self.state.verify_user(username_or_email, password)
if result: