class Db: def __init__(self, dburi): self.db = DBany(dburi) def _simple_and_cond(self, cond): if not cond: return None cnd = ['and', 1] for c in cond: cnd.append(('=', c[0], c[1])) return cnd def show(self, tab, cols, cond, limit=0): desc = self.db.describe(tab) if not cols: cols = desc.keys() head = [ desc[i] for i in cols ] cond = self._simple_and_cond(cond) return self.db.select(tab, cols, cond, limit), head def add(self, tab, ins): self.db.insert(tab, ins) def rm(self, tab, cond, limit=0): cond = self._simple_and_cond(cond) self.db.delete(tab, cond, limit)
class Cred: TABLE = 'credentials' COLUMNS = ('auth_username', 'did', 'realm', 'uid', 'password', 'ha1', \ 'ha1b', 'flags') COLIDXS = idx_dict(COLUMNS) FLAGIDX = COLIDXS['flags'] def __init__(self, dburi, db=None): self.Domain_attrs = serctl.ctlattr.Domain_attrs self.Domain = serctl.ctldomain.Domain self.User = serctl.ctluser.User self.dburi = dburi if db is not None: self.db = db else: self.db = DBany(dburi) def default_flags(self): return str(LOAD_SER | FOR_SERWEB) def get_uid(self, username, realm): cnd, err = cond(CND_NO_DELETED, auth_username=username, realm=realm) rows = self.db.select(self.TABLE, 'uid', cnd) if not rows: raise Error (ENOREC, err) uids = uniq([ i[0] for i in rows ]) if len(uids) > 1: raise Error (EDB, '%s@%s=%s' % (username, realm, str(uids))) uid = uids[0] return uid def get_uids_for_username(self, username): cnd, err = cond(CND_NO_DELETED, auth_username=username) rows = self.db.select(self.TABLE, 'uid', cnd) if not rows: raise Error (ENOREC, err) uids = [ i[0] for i in rows ] return uniq(uids) def exist(self, username, realm): cnd, err = cond(CND_NO_DELETED, auth_username=username, realm=realm) rows = self.db.select(self.TABLE, 'uid', cnd, limit=1) return rows != [] def exist_uid(self, uid): cnd, err = cond(CND_NO_DELETED, uid=uid) rows = self.db.select(self.TABLE, 'uid', cnd, limit=1) return rows != [] def exist_realm(self, realm): cnd, err = cond(CND_NO_DELETED, realm=realm) rows = self.db.select(self.TABLE, 'realm', cnd, limit=1) return rows != [] def _show(self, cnd, err, cols, fformat, limit): if not cols: cols = self.COLUMNS cidx = col_idx(self.COLIDXS, cols) rows = self.db.select(self.TABLE, self.COLUMNS, cnd, limit) new_rows = [] for row in rows: row[self.FLAGIDX] = cv_flags(fformat, row[self.FLAGIDX]) new_row = [] for i in cidx: new_row.append(row[i]) new_rows.append(new_row) desc = self.db.describe(self.TABLE) desc = [ desc[i] for i in cols ] return new_rows, desc def show(self, realm=None, username=None, cols=None, fformat='raw', limit=0): cnd, err = cond(auth_username=username, realm=realm) return self._show(cnd, err, cols, fformat, limit) def show_uid(self, uid, cols=None, fformat='raw', limit=0): cnd, err = cond(uid=uid) return self._show(cnd, err, cols, fformat, limit) def hashes(self, username, realm, password): uri = '@'.join((username, realm)) hash_src1 = ':'.join((username, realm, password)) hash_src1b = ':'.join((uri, realm, password)) ha1 = md5.new(hash_src1).hexdigest() ha1b = md5.new(hash_src1b).hexdigest() return (ha1, ha1b) def add(self, uid, username, did, realm, password, flags=None, force=False): dflags = self.default_flags() fmask = parse_flags(flags) flags = new_flags(dflags, fmask) if not did: do = self.Domain(self.dburi, self.db) try: did = do.get_did(realm) except: if force: return raise us = self.User(self.dburi, self.db) if not us.exist(uid) and not force: raise Error (ENOUSER, 'uid='+uid) if self.exist(username, realm): if force: return raise Error (EDUPL, ) # compute hashes ha1, ha1b = self.hashes(username, realm, password) # add new cred ins = { 'uid' : uid, 'auth_username' : username, \ 'did' : did, 'realm' : realm, 'password' : password, \ 'ha1' : ha1, 'ha1b' : ha1b, 'flags' : flags \ } self.db.insert(self.TABLE, ins) def _rm(self, cnd, err, force): rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows and not force: raise Error (ENOREC, err) for row in rows: nf = set_deleted(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags': nf}, cnd) def rm(self, username, realm, force=False): try: uid = self.get_uid(username, realm) except: if force: return raise cnd, err = cond(CND_NO_DELETED, uid=uid, realm=realm) return self._rm(cnd, err, force) def rm_realm(self, realm, force=False): cnd, err = cond(CND_NO_DELETED, realm=realm) return self._rm(cnd, err, force) def rm_uid(self, uid, force=False): cnd, err = cond(CND_NO_DELETED, uid=uid) return self._rm(cnd, err, force) def change(self, username, realm, password=None, flags=None, force=False): upd = {} # username & realm is required for password change if password is not None: # compute hashes ha1, ha1b = self.hashes(username, realm, password) upd = {'ha1': ha1, 'ha1b': ha1b, 'password': password} fmask = parse_flags(flags) # update cnd, err = cond(CND_NO_DELETED, auth_username=username, realm=realm) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows and not force: raise Error (ENOREC, err) for row in rows: cnd = full_cond(self.COLUMNS, row) if flags is not None: nf = new_flags(row[self.FLAGIDX], fmask) upd['flags'] = nf self.db.update(self.TABLE, upd, cnd) def purge(self): self.db.delete(self.TABLE, CND_DELETED)
class User: TABLE = 'user_attrs' COLUMNS = ('uid', 'name', 'value', 'type', 'flags') COLIDXS = idx_dict(COLUMNS) FLAGIDX = COLIDXS['flags'] def __init__(self, dburi, db=None): self.Uri = serctl.ctluri.Uri self.Cred = serctl.ctlcred.Cred self.dburi = dburi if db is not None: self.db = db else: self.db = DBany(dburi) def default_flags(self): return '0' def is_used(self, uid): ur = self.Uri(self.dburi, self.db) cr = self.Cred(self.dburi, self.db) return ur.exist_uid(uid) or cr.exist_uid(uid) def get(self, uid): cnd, err = cond(CND_NO_DELETED, uid=uid) rows = self.db.select(self.TABLE, 'uid', cnd) if not rows: raise Error (ENOREC, err) return [ i[0] for i in rows ] def exist(self, uid): cnd, err = cond(CND_NO_DELETED, uid=uid) rows = self.db.select(self.TABLE, 'uid', cnd, limit=1) return rows != [] def _try_rm_orphans(self, uid): ur = self.Uri(self.dburi, self.db) cr = self.Cred(self.dburi, self.db) try: ur.rm_uid(uid) except: pass try: cr.rm_realm(uid) except: pass def show(self, uid=None, cols=None, fformat='raw', limit=0): if not cols: cols = self.COLUMNS cidx = col_idx(self.COLIDXS, cols) cnd, err = cond(uid=uid) rows = self.db.select(self.TABLE, self.COLUMNS, cnd, limit) new_rows = [] for row in rows: row[self.FLAGIDX] = cv_flags(fformat, row[self.FLAGIDX]) new_row = [] for i in cidx: new_row.append(row[i]) new_rows.append(new_row) desc = self.db.describe(self.TABLE) desc = [ desc[i] for i in cols ] return new_rows, desc def add(self, uid, flags=None, force=False): dflags = self.default_flags() fmask = parse_flags(flags) flags = new_flags(dflags, fmask) # exist uid? cnd, err = cond(CND_NO_DELETED, uid=uid) rows = self.db.select(self.TABLE, 'uid', cnd, limit=1) if rows: if force: return raise Error (EDUPL, uid) # user add ins = {'uid': uid, 'name': 'datetime_created', \ 'value': timestamp(), 'type': 2, 'flags': flags } self.db.insert(self.TABLE, ins) def rm(self, uid=None, force=False): if self.is_used(uid): if force: self._try_rm_orphans(uid) else: raise Error (EUSER, uid) # rm all uids (FIX: or only datetime_created?) cnd, err = cond(CND_NO_DELETED, uid=uid) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows and not force: raise Error (ENOREC, err) for row in rows: nf = set_deleted(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags': nf}, cnd) def change(self, uid=None, flags=None, force=False): fmask = parse_flags(flags) cnd, err = cond(CND_NO_DELETED, uid=uid) # update flags rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows and not force: raise Error (ENOREC, err) for row in rows: nf = new_flags(row[self.FLAGIDX], fmask) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags':nf}, cnd) def purge(self): self.db.delete(self.TABLE, CND_DELETED)
class Domain: TABLE = 'domain' COLUMNS = ('did', 'domain', 'flags') COLIDXS = idx_dict(COLUMNS) # column index dict FLAGIDX = COLIDXS['flags'] # flags column index def __init__(self, dburi, db=None): self.Uri = serctl.ctluri.Uri self.Cred = serctl.ctlcred.Cred self.Domain_attrs = serctl.ctlattr.Domain_attrs self.dburi = dburi if db is not None: self.db = db else: self.db = DBany(dburi) def default_flags(self): return str(LOAD_SER | FOR_SERWEB) def get_did(self, domain): cnd, err = cond(CND_NO_DELETED, domain=domain) rows = self.db.select(self.TABLE, 'did', cnd, limit=1) if not rows: raise Error (ENOREC, err) return rows[0][0] def get_domains(self, did): cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, 'domain', cnd) if not rows: raise Error (ENOREC, err) return [ row[0] for row in rows ] def get_domain(self, did): cnd, err = cond(CND_NO_DELETED, CND_CANONICAL, did=did) rows = self.db.select(self.TABLE, 'domain', cnd, limit=1) if not rows: cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, 'domain', cnd, limit=1) if not rows: raise Error (ENOREC, err) return rows[0][0] def exist(self, did, domain): cnd, err = cond(CND_NO_DELETED, did=did, domain=domain) rows = self.db.select(self.TABLE, 'did', cnd, limit=1) return rows != [] def exist_domain(self, domain): cnd, err = cond(CND_NO_DELETED, domain=domain) rows = self.db.select(self.TABLE, 'domain', cnd, limit=1) return rows != [] def exist_did(self, did): cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, 'did', cnd, limit=1) return rows != [] def is_last_domain(self, did, domain): cnd, err = cond(CND_NO_DELETED, did=did) cnd.append(('!=', 'domain', domain)) rows = self.db.select(self.TABLE, 'did', cnd, limit=1) return rows == [] def is_used(self, did): ur = self.Uri(self.dburi, self.db) cr = self.Cred(self.dburi, self.db) return ur.exist_did(did) or cr.exist_realm(did) def _show(self, cnd, err, cols, fformat, limit): if not cols: cols = self.COLUMNS cidx = col_idx(self.COLIDXS, cols) rows = self.db.select(self.TABLE, self.COLUMNS, cnd, limit) new_rows = [] for row in rows: row[self.FLAGIDX] = cv_flags(fformat, row[self.FLAGIDX]) new_row = [] for i in cidx: new_row.append(row[i]) new_rows.append(new_row) desc = self.db.describe(self.TABLE) desc = [ desc[i] for i in cols ] return new_rows, desc def show_domain(self, domain=None, cols=None, fformat='raw', limit=0): cnd, err = cond(domain=domain) rows, desc = self._show(cnd, err, cols, fformat, limit) if domain and not rows: raise Error(ENODOMAIN, domain) return rows, desc def show_did(self, did=None, cols=None, fformat='raw', limit=0): cnd, err = cond(did=did) rows, desc = self._show(cnd, err, cols, fformat, limit) if did and not rows: raise Error(ENODID, did) return rows, desc def show_did_for_domain(self, domain=None, cols=None, fformat='raw', limit=0): if domain is None: return self.show_did(None, cols, fformat, limit) try: did = self.get_did(domain) except: if not cols: cols = self.COLUMNS desc = self.db.describe(self.TABLE) desc = [ desc[i] for i in cols ] return [], desc rows, desc = self.show_did(did, cols, fformat, limit) return rows, desc def add(self, did, domain, flags=None, force=False): dflags = self.default_flags() fmask = parse_flags(flags) flags = new_flags(dflags, fmask) canonical = is_canonical(flags) if self.exist(did, domain): if force: return raise Error (EDUPL, errstr(did=did, domain=domain)) # set digest realm attr da = self.Domain_attrs(self.dburi, self.db) da.set_default(did, 'digest_realm', domain) # update canonical flag cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) canon_exist = False for row in rows: if not is_canonical(row[self.FLAGIDX]): continue if not canonical: canon_exist = True break f = clear_canonical(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags':f}, cnd) if not canonical and not canon_exist: flags = set_canonical(flags) # add new domain ins = { 'did' : did, 'domain' : domain, 'flags' : flags } self.db.insert(self.TABLE, ins) def rm(self, did, domain, force=False): if self.is_last_domain(did, domain): return self.rm_did(did, force) # is canonical? canon_deleted = True cnd, err = cond(CND_NO_DELETED, CND_CANONICAL, did=did, domain=domain) rows = self.db.select(self.TABLE, 'did', cnd, limit=1) if not rows: canon_deleted = False # remove cnd, err = cond(CND_NO_DELETED, did=did, domain=domain) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows: if force: return raise Error (ENOREC, err) for row in rows: nf = set_deleted(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags': nf}, cnd) # set new canon flag if canon_deleted: cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, self.COLUMNS, cnd, limit=1) if rows: nf = set_canonical(rows[0][self.FLAGIDX]) cnd = full_cond(self.COLUMNS, rows[0]) upd = {'flags': nf} self.db.update(self.TABLE, upd, cnd) def rm_did(self, did, force=False): da = self.Domain_attrs(self.dburi, self.db) if self.is_used(did): if force: self._try_rm_orphans(did) else: raise Error (EDOMAIN, 'did=%s' % did) da.rm_exist(did, 'digest_realm') # remove did cnd, err = cond(CND_NO_DELETED, did=did) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows: if force: return raise Error (ENOREC, err) for row in rows: nf = set_deleted(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags': nf}, cnd) def rm_domain(self, domain, force=False): try: did = self.get_did(domain) except: if force: return raise self.rm(did, domain, force) def rm_did_for_domain(self, domain, force): try: did = self.get_did(domain) except: if force: return raise if self.is_used(did) and not force: raise Error (EDOMAIN, errstr(did=did)) self.rm_did(did, force) def _try_rm_orphans(self, did): ur = self.Uri(self.dburi, self.db) cr = self.Cred(self.dburi, self.db) try: ur.rm_did(did) except: pass try: cr.rm_realm(did) except: pass def change_domain(self, domain, flags=None, force=False): upd = {} fmask = parse_flags(flags) nflags = new_flags(0, fmask) canonical = is_canonical(nflags) # get did try: did = self.get_did(domain) except: if force: return raise # clear canon if canonical: cnd, err = cond(CND_NO_DELETED, did=did) cnd.append(CND_CANONICAL) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) for row in rows: nf = clear_canonical(row[self.FLAGIDX]) cnd = full_cond(self.COLUMNS, row) upd = {'flags':nf} self.db.update(self.TABLE, upd, cnd) # update flags cnd, err = cond(CND_NO_DELETED, domain=domain) rows = self.db.select(self.TABLE, self.COLUMNS, cnd) if not rows and not force: raise Error (ENOREC, err) for row in rows: nf = new_flags(row[self.FLAGIDX], fmask) cnd = full_cond(self.COLUMNS, row) self.db.update(self.TABLE, {'flags':nf}, cnd) def purge(self): self.db.delete(self.TABLE, CND_DELETED)