Merge branch 'master' into orgview
This commit is contained in:
commit
5cd500257d
52 changed files with 387 additions and 62 deletions
|
@ -950,6 +950,7 @@ def change_password(user, new_password):
|
|||
pw_hash = hash_password(new_password)
|
||||
user.invalid_login_attempts = 0
|
||||
user.password_hash = pw_hash
|
||||
user.uuid = str(uuid4())
|
||||
user.save()
|
||||
|
||||
# Remove any password required notifications for the user.
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import ldap
|
||||
import logging
|
||||
import json
|
||||
import itertools
|
||||
import uuid
|
||||
import struct
|
||||
|
||||
from util.aes import AESCipher
|
||||
from util.validation import generate_valid_usernames
|
||||
from data import model
|
||||
|
||||
|
@ -106,6 +111,7 @@ class LDAPUsers(object):
|
|||
return found_user is not None
|
||||
|
||||
|
||||
|
||||
class UserAuthentication(object):
|
||||
def __init__(self, app=None):
|
||||
self.app = app
|
||||
|
@ -138,5 +144,81 @@ 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']
|
||||
secret_key = None
|
||||
|
||||
# First try parsing the key as an int.
|
||||
try:
|
||||
big_int = int(app_secret_key)
|
||||
secret_key = str(bytearray.fromhex('{:02x}'.format(big_int)))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Next try parsing it as an UUID.
|
||||
if secret_key is None:
|
||||
try:
|
||||
secret_key = uuid.UUID(app_secret_key).bytes
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if secret_key is None:
|
||||
secret_key = str(bytearray(map(ord, 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:
|
||||
import features
|
||||
|
||||
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 unecrypted passwords is disabled. Please generate an ' +
|
||||
'encrypted password in the user admin panel for use here.')
|
||||
return (None, msg)
|
||||
else:
|
||||
password = decrypted
|
||||
|
||||
result = self.state.verify_user(username_or_email, password)
|
||||
if result:
|
||||
return (result, '')
|
||||
else:
|
||||
return (result, 'Invalid password.')
|
||||
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.state, name, None)
|
||||
|
|
Reference in a new issue