def authenticate(user=None, password=None): def _format_key(key): try: if isinstance(key, list): kk = key else: kk = key.split(',') if len(kk) == 1: return kk[0] else: return apikey.create_combined_key(kk) except Exception as e: logging.error(e) raise if user is None or password is None: raise AccessDenied('No login/password provided') dbconn = userdb() try: r = dbconn.execute(sql('select k from users where u = :u and p = :p'), u=user, p=crypt_password(password)).fetchone() except: eva.core.report_userdb_error() if r: return _format_key(r.k), None else: k = msad_authenticate(user, password) if k is None: raise AccessDenied('Authentication failure') else: logging.debug( f'user {user} authenticated via active directory, key id: {k}') return _format_key(k), 'msad'
def create_user(user=None, password=None, key=None): if user is None or password is None or key is None: return False kk = key if isinstance(key, list) else key.split(',') for k in kk: if k not in apikey.keys_by_id: raise ResourceNotFound(f'API key {k}') try: dbconn = userdb() row = dbconn.execute(sql('select k from users where u = :u'), u=user).fetchone() except: eva.core.report_userdb_error() if row: raise ResourceAlreadyExists try: dbconn.execute(sql('insert into users(u, p, k) values (:u, :p, :k)'), u=user, p=crypt_password(password), k=','.join(kk)) logging.info('User {} created, key: {}'.format(user, ','.join(kk))) except: eva.core.report_userdb_error() return None run_hook('create', user, password) return {'user': user, 'key_id': key}
def api_log_insert(call_id, gw=None, ip=None, auth=None, u=None, utp=None, ki=None, func=None, params=None): dbconn = userdb() dbt = dbconn.begin() try: dbconn.execute(sql( 'insert into api_log(id, t, gw, ip, auth, u, utp, ki, func, params)' ' values (:i, :t, :gw, :ip, :auth, :u, :utp, :ki, :func, :params)' ), i=call_id, t=time.time(), gw=gw, ip=ip, auth=auth, u=u, utp=utp, ki=ki, func=func, params=rapidjson.dumps(params)[:512]) dbt.commit() except: dbt.rollback() logging.error('Unable to insert API call info into DB') eva.core.log_traceback()
def init(): dbconn = userdb() meta = sa.MetaData() t_users = sa.Table('users', meta, sa.Column('u', sa.String(64), primary_key=True), sa.Column('p', sa.String(64)), sa.Column('k', sa.String(2048))) t_api_log = sa.Table('api_log', meta, sa.Column('id', sa.String(36), primary_key=True), sa.Column('t', sa.Numeric(20, 8), nullable=False), sa.Column('tf', sa.Numeric(20, 8)), sa.Column('gw', sa.String(128)), sa.Column('ip', sa.String(45)), sa.Column('auth', sa.String(128)), sa.Column('u', sa.String(128)), sa.Column('utp', sa.String(32)), sa.Column('ki', sa.String(2048)), sa.Column('func', sa.String(128)), sa.Column('params', sa.String(512)), sa.Column('status', sa.String(32))) try: meta.create_all(dbconn) except: eva.core.log_traceback() logging.critical('unable to create users table in db') try: dbconn.close() except: pass
def load_keys_from_db(): _keys = {} _keys_by_id = {} dbconn = userdb() meta = sa.MetaData() t_apikeys = sa.Table('apikeys', meta, sa.Column('k_id', sa.String(64), primary_key=True), sa.Column('k', sa.String(64)), sa.Column('m', sa.Integer), sa.Column('s', sa.Integer), sa.Column('i', sa.String(1024)), sa.Column('g', sa.String(1024)), sa.Column('i_ro', sa.String(1024)), sa.Column('g_ro', sa.String(1024)), sa.Column('a', sa.String(256)), sa.Column('hal', sa.String(1024)), sa.Column('has', sa.String(1024)), sa.Column('pvt', sa.String(1024)), sa.Column('rpvt', sa.String(1024))) try: meta.create_all(dbconn) except: logging.critical('unable to create apikeys table in db') return _keys, _keys_by_id try: result = dbconn.execute(sql('select * from apikeys')) while True: r = result.fetchone() if not r: break key = APIKey(r.k, r.k_id) key.sysfunc = True if val_to_boolean(r.s) else False for i, v in { 'item_ids': 'i', 'groups': 'g', 'item_ids_ro': 'i_ro', 'groups_ro': 'g_ro', 'allow': 'a', 'pvt_files': 'pvt', 'rpvt_uris': 'rpvt' }.items(): setattr( key, i, list(filter(None, [j.strip() for j in r[v].split(',')]))) _hosts_allow = list( filter(None, [j.strip() for j in r.hal.split(',')])) key.hosts_allow = [IPNetwork(h) for h in _hosts_allow] _hosts_assign = list( filter(None, [x.strip() for x in r.has.split(',')])) key.hosts_assign = \ [ IPNetwork(h) for h in _hosts_assign ] key.dynamic = True key.in_db = True _keys[key.key] = key _keys_by_id[key.key_id] = key except: eva.core.report_userdb_error(raise_exeption=False) return _keys, _keys_by_id
def save(self): if not self.dynamic: return False data = self.serialize() for d in [ 'items', 'groups', 'items_ro', 'groups_ro', 'allow', 'hosts_allow', 'hosts_assign', 'pvt', 'rpvt' ]: data[d] = ','.join(data[d]) dbconn = userdb() try: if not self.in_db: # if save on exit is set, deleted key with the same name could # still be present in the database dbconn.execute(sql('delete from apikeys where k_id=:k_id'), k_id=data['id']) dbconn.execute( sql('insert into apikeys(k_id, k, m, s, i,' + ' g, i_ro, g_ro, a,hal, has, pvt, rpvt) values ' + '(:k_id, :k, :m, :s, :i, :g, :i_ro, :g_ro, :a, ' + ':hal, :has, :pvt, :rpvt)'), k_id=data['id'], k=data['key'], m=1 if data['master'] else 0, s=1 if data['sysfunc'] else 0, i=data['items'], g=data['groups'], i_ro=data['items_ro'], g_ro=data['groups_ro'], a=data['allow'], hal=data['hosts_allow'], has=data['hosts_assign'], pvt=data['pvt'], rpvt=data['rpvt']) else: dbconn.execute( sql('update apikeys set k=:k, s=:s, i=:i, g=:g, ' + 'i_ro=:i_ro, g_ro=:g_ro, a=:a, ' + 'hal=:hal, has=:has, pvt=:pvt, rpvt=:rpvt where ' + 'k_id=:k_id'), k=self.key, s=1 if data['sysfunc'] else 0, i=data['items'], g=data['groups'], i_ro=data['items_ro'], g_ro=data['groups_ro'], a=data['allow'], hal=data['hosts_allow'], has=data['hosts_assign'], pvt=data['pvt'], rpvt=data['rpvt'], k_id=data['id']) except: eva.core.report_userdb_error() self.in_db = True return True
def msad_get_cached_credentials(username, password): if _d.msad_cache_time <= 0: return logging.debug(f'getting cached credentials for {username}') r = userdb().execute( sql('select cn from msad_cache where u=:u and p=:p and t>=:t'), u=username, p=crypt_password(password), t=time.time() - _d.msad_cache_time).fetchone() return r.cn if r else None
def msad_cache_cleaner(**kwargs): logging.debug('cleaning MSAD cache') dbconn = userdb() dbt = dbconn.begin() try: dbconn.execute(sql('delete from msad_cache where t < :t'), t=time.time() - _d.msad_cache_time) dbt.commit() except: dbt.rollback() raise
def get_user(user=None): if not user: return None try: dbconn = userdb() result = [] row = dbconn.execute(sql('select u, k from users where u=:u'), u=user).fetchone() except: eva.core.report_userdb_error() if not row: raise ResourceNotFound return {'user': row.u, 'key_id': row.k}
def save(): for i, k in keys_by_id.copy().items(): if k.config_changed and not k.save(): return False dbconn = userdb() try: for k in keys_to_delete: dbconn.execute(sql('delete from apikeys where k_id=:k_id'), k_id=k) return True except: eva.core.report_db_error(raise_exeption=False)
def init(): dbconn = userdb() meta = sa.MetaData() t_users = sa.Table('users', meta, sa.Column('u', sa.String(64), primary_key=True), sa.Column('p', sa.String(64)), sa.Column('k', sa.String(64))) try: meta.create_all(dbconn) except: eva.core.log_traceback() logging.critical('unable to create apikeys table in db')
def authenticate(user=None, password=None): if user is None or password is None: raise AccessDenied('No login/password provided') dbconn = userdb() try: r = dbconn.execute(sql('select k from users where u = :u and p = :p'), u=user, p=crypt_password(password)).fetchone() except: eva.core.report_userdb_error() if not r: raise AccessDenied('Authentication failure') return r.k
def set_user_key(user=None, key=None): if user is None or key is None or key not in apikey.keys_by_id: return None try: dbconn = userdb() if dbconn.execute(sql('update users set k = :k where u = :u'), k=key, u=user).rowcount: logging.info('user {} key {} is set'.format(user, key)) return True except: eva.core.report_userdb_error() raise ResourceNotFound
def api_log_set_status(call_id, status=None): dbconn = userdb() dbt = dbconn.begin() try: dbconn.execute( sql('update api_log set tf=:tf, status=:status where id=:i'), i=call_id, tf=time.time(), status=status) dbt.commit() except: dbt.rollback() logging.error('Unable to update API call info in DB') eva.core.log_traceback()
def list_users(): try: dbconn = userdb() result = [] r = dbconn.execute(sql('select u, k from users order by u')) while 1: row = r.fetchone() if not row: break u = {} u['user'] = row.u u['key_id'] = row.k result.append(u) return sorted(result, key=lambda k: k['user']) except: eva.core.report_userdb_error()
def api_log_cleaner(**kwargs): logging.debug('cleaning API log') dbconn = userdb() dbt = dbconn.begin() try: dbconn.execute(sql('delete from api_log where t < :t'), t=time.time() - eva.core.config.keep_api_log) dbt.commit() except: dbt.rollback() raise try: dbconn.close() except: pass
def destroy_user(user=None): if user is None: raise FunctionFailed try: dbconn = userdb() if dbconn.execute(sql('delete from users where u = :u'), u=user).rowcount: logging.info('User {} deleted'.format(user)) else: raise ResourceNotFound except ResourceNotFound: raise except: eva.core.report_userdb_error() return False run_hook('destroy', user) return True
def delete_api_key(key_id): if key_id is None or key_id not in keys_by_id: raise ResourceNotFound if keys_by_id[key_id].master or not keys_by_id[key_id].dynamic: raise FunctionFailed('Master and static keys can not be deleted') del keys[keys_by_id[key_id].key] del keys_by_id[key_id] if eva.core.config.db_update == 1: dbconn = userdb() try: dbconn.execute(sql('delete from apikeys where k_id=:key_id'), key_id=key_id) except: eva.core.report_userdb_error() else: keys_to_delete.add(key_id) return True
def set_user_key(user=None, key=None): if user is None or key is None: return None kk = key if isinstance(key, list) else key.split(',') for k in kk: if k not in apikey.keys_by_id: raise ResourceNotFound(f'API key {k}') try: dbconn = userdb() if dbconn.execute(sql('update users set k = :k where u = :u'), k=','.join(kk), u=user).rowcount: logging.info('user {} key {} is set'.format(user, ','.join(kk))) return True except: eva.core.report_userdb_error() raise ResourceNotFound
def set_user_password(user=None, password=None): if user is None or password is None: return None try: dbconn = userdb() if dbconn.execute(sql('update users set p = :p where u = :u'), p=crypt_password(password), u=user).rowcount: logging.info('user {} new password is set'.format(user)) else: raise ResourceNotFound except ResourceNotFound: raise except: eva.core.report_userdb_error() return False run_hook('set_password', user, password) return True
def api_log_update(call_id, **kwargs): # unsafe, make sure kwargs keys are not coming from outside cond = '' qkw = {'i': call_id} for k, v in kwargs.items(): if cond: cond += ',' cond += f'{k}=:{k}' qkw[k] = v if cond: dbconn = userdb() dbt = dbconn.begin() try: dbconn.execute(sql(f'update api_log set {cond} where id=:i'), **qkw) dbt.commit() except: dbt.rollback() logging.error('Unable to update API call info in DB') eva.core.log_traceback()
def msad_init(host, domain, ca=None, key_prefix=None, ou=None, cache_time=86400, cache_first=False): try: from easyad import EasyAD except: logging.error('unable to import easyad module') return dbconn = userdb() meta = sa.MetaData() t_users = sa.Table( 'msad_cache', meta, sa.Column('u', sa.String(64), primary_key=True), sa.Column('p', sa.String(64)), sa.Column('cn', sa.String(2048)), sa.Column('t', sa.Numeric(20, 8)), ) try: meta.create_all(dbconn) except: eva.core.log_traceback() logging.critical('unable to create msad_cache table in db') _d.msad_host = dict_from_str(host) if '=' in host else host _d.msad_default_domain = domain _d.msad_ca = ca _d.msad_key_prefix = key_prefix if key_prefix else '' _d.msad_cache_time = cache_time _d.msad_cache_first = cache_first if ou is not None: _d.msad_ou = ou try: dbconn.close() except: pass
def delete_api_key(key_id): with key_lock: if key_id is None or key_id not in keys_by_id: raise ResourceNotFound if keys_by_id[key_id].master or not keys_by_id[key_id].dynamic: raise FunctionFailed('Master and static keys can not be deleted') del keys[keys_by_id[key_id].key] del keys_by_id[key_id] if eva.core.config.auto_save: dbconn = userdb() try: dbconn.execute(sql('delete from apikeys where k_id=:key_id'), key_id=key_id) except: eva.core.report_userdb_error() else: keys_to_delete.add(key_id) for k, v in combined_keys_cache.items(): ckey = keys_by_id[v] if key_id in ckey.combined_from: ckey.need_recombine = True return True
def msad_cache_credentials(username, password, cn): if _d.msad_cache_time <= 0: return dbconn = userdb() dbt = dbconn.begin() params = { 'u': username, 'p': crypt_password(password), 'cn': cn, 't': time.time() } try: if dbconn.execute( sql('update msad_cache set p=:p, cn=:cn, t=:t where u=:u'), ** params).rowcount < 1: dbconn.execute( sql('insert into msad_cache(u, p, cn, t) ' 'values (:u, :p, :cn, :t)'), **params) dbt.commit() logging.debug(f'MSAD credentials for {username} cached') except: dbt.rollback() raise
def api_log_get(t_start=None, t_end=None, limit=None, time_format=None, f=None): t_start = fmt_time(t_start) t_end = fmt_time(t_end) qkw = {} if t_start or t_end: cond = 'where (' if t_start is not None: try: t_start = float(t_start) except: try: t_start = dateutil.parser.parse(t_start).timestamp() except: raise ValueError('start time format is uknown') cond += 't >= :t_start' qkw['t_start'] = t_start if t_end is not None: try: t_end = float(t_end) except: try: t_end = dateutil.parser.parse(t_end).timestamp() except: raise ValueError('end time format is uknown') if t_start is not None: cond += ' and ' cond += 't <= :t_end' qkw['t_end'] = t_end cond += ')' else: cond = '' if f: # make sure some empty fields are null for z in ('u', 'utp', 'status'): if z in f and f[z] == '': f[z] = None if 'params' in f: condp = 'params like :params' qkw['params'] = f'%{f["params"]}%' del f['params'] else: condp = None try: cond, qkw = format_sql_condition(f, qkw, fields=('gw', 'ip', 'auth', 'u', 'utp', 'ki', 'func', 'status'), cond=cond) except ValueError as e: raise ValueError(f'Invalid filter: {e}') if condp: if cond: cond += ' and ' else: cond = 'where ' cond += condp if limit is None: cond += ' order by t asc' else: cond += f' order by t desc limit {limit}' result = [ dict(r) for r in userdb().execute( sql('select id, t, tf, gw, ip, auth, u, utp, ki,' f' func, params, status from api_log {cond}'), ** qkw).fetchall() ] if limit is not None: result = sorted(result, key=lambda k: k['t']) if time_format == 'iso': import pytz tz = pytz.timezone(time.tzname[0]) for r in result: r['t'] = datetime.fromtimestamp(r['t'], tz).isoformat() r['tf'] = datetime.fromtimestamp(r['tf'], tz).isoformat() return result