2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-28 20:39:40 +00:00

Flesh out user password management

This commit is contained in:
Jarrod Johnson 2013-10-08 17:49:38 -04:00
parent 96ca623520
commit b0855f7c88
2 changed files with 97 additions and 13 deletions

View File

@ -1,8 +1,39 @@
# authentication and authorization routines for confluent
# authentication scheme caches password values to help HTTP Basic auth
# the PBKDF2 transform is skipped if a user has been idle for sufficient time
import confluent.config as config
import eventlet
import Crypto.Protocol.KDF as kdf
import Crypto.Hash as hash
def authorize(name, element, tenant=None, access='rw'):
_passcache = {}
_passchecking = {}
def _get_usertenant(name, tenant=False):
"""_get_usertenant
Convenience function to parse name into username and tenant.
If tenant is explicitly passed in, then name must be the username
tenant name with '/' is forbidden. If '/' is seen in name, tenant
is assumed to preface the /.
If the username is a tenant name, then it is to be the implied
administrator account a tenant gets.
Otherwise, just assume a user in the default tenant
"""
if isinstance(tenant,bool):
user = name
tenant = None
elif '/' in name:
tenant, user = name.split('/', 1)
elif config.is_tenant(name):
user = name
tenant = name
else:
user = name
tenant = None
def authorize(name, element, tenant=False, access='rw'):
#TODO: actually use the element to ascertain if this user is good enough
"""Determine whether the given authenticated name is authorized.
@ -16,20 +47,61 @@ def authorize(name, element, tenant=None, access='rw'):
and the relevant ConfigManager object for the context of the
request.
"""
if tenant is not None:
user = name
elif '/' in name:
tenant, user = name.split('/', 1)
elif config.is_tenant(name):
user = name
tenant = name
else:
user = name
tenant = 0
if not config.is_tenant(tenant):
user, tenant = _get_usertenant(name, tenant)
if tenant is not None and not config.is_tenant(tenant):
return None
configmanager = config.ConfigManager(tenant)
userobj = configmanager.get_user(user)
if userobj: #returning
return (userobj, configmanager)
return None
def set_user_password(name, password, tenant=None):
"""Set user password
:param name: The unique shortname of the user
:param password: The password to set for given user
:param tenant: The tenant to which the user belongs.
"""
# TODO(jbjohnso): WORKERPOOL
# When worker pool implemented, hand off the
# PBKDF2 to a worker instead of blocking
user, tenant = _get_usertenant(name, tenant)
_passcache[(user, tenant)] = password
salt = os.urandom(8)
crypted = kdf.PBKDF2(passphrase, salt, 32, 10000,
lambda p, s: hash.HMAC.new(p, s, hash.SHA256).digest())
cfm = config.ConfigManager(tenant)
cfm.set_user(name, { 'cryptpass': (salt, crypted) })
def check_user_passphrase(name, passphrase, tenant=None):
user, tenant = _get_usertenant(name, tenant)
if (user,tenant) in _passcache:
if passphrase == passcache[(user,tenant)]:
return True
else:
# In case of someone trying to guess,
# while someone is legitimately logged in
# invalidate cache and force the slower check
del _passchache[(user, tenant)]
return False
eventlet.sleep(0.1) # limit throughput of remote guessing
while (user,tenant) in _passchecking:
# Want to serialize passphrase checking activity
# by a user, which might be malicious
# would normally make an event and wait
# but here there's no need for that
eventlet.sleep(0.5)
cfm = config.ConfigManager(tenant)
ucfg = cfm.get_user(user)
if ucfg is None or 'cryptpass' not in ucfg:
return False
_passchecking[(user, tenant)] = True
# TODO(jbjohnso): WORKERPOOL
# PBKDF2 is, by design, cpu intensive
# throw it at the worker pool when implemented
salt, crypt = ucfg['cryptpass']
crypted = kdf.PBKDF2(passphrase, salt, 32, 10000,
lambda p, s: hash.HMAC.new(p, s, hash.SHA256).digest())

View File

@ -235,7 +235,19 @@ class ConfigManager(object):
return None
def create_user(self, name, role="Administrator", id=None, displayname=None):
def set_user(self, name, attributemap):
"""Set user attribute(s)
:param name: The login name of the user
:param attributemap: A dict of key values to set
"""
user = self._cfgstore['users'][name]
for attribute in attributemap:
user[attribute] = attributemap[attribute]
self._bg_sync_to_file()
def create_user(self, name,
role="Administrator", id=None, displayname=None):
"""Create a new user
:param name: The login name of the user