import ldap import hmac, hashlib class User: def __init__(self, username, groups): self.username = username self.groups = groups def summarize(self): return "{}:{}".format(self.username, ",".join(self.groups)) @staticmethod def from_summary(summary): name, groupstring = summary.split(":", 1) groups = groupstring.split(",") return User(name, groups) @staticmethod def from_hashstring(secure_string): summary, hash = secure_string.split("=", 1) return User.from_summary(summary) class LdapManager: def __init__(self, url, base): self.connection = ldap.initialize(url) self.base = base def login(self, username, password): if not self.authenticate(username, password): return None groups = list(map(lambda g: g.decode("utf-8"), self.groups(username))) return User(username, groups) def authenticate(self, username, password): try: self.connection.simple_bind_s("uid={},ou=users,{}".format(username, self.base), password) return True except ldap.INVALID_CREDENTIALS: return False return False def groups(self, username): result = [] # use username.lower() since memberUid is case sensitive here for _, result_dict in self.connection.search_s(self.base, ldap.SCOPE_SUBTREE, "(memberUid={})".format(username.lower()), ["cn"]): result.append(result_dict["cn"][0]) return result class SecurityManager: def __init__(self, key): self.maccer = hmac.new(key.encode("utf-8"), digestmod=hashlib.sha512) def hash_user(self, user): maccer = self.maccer.copy() summary = user.summarize() maccer.update(summary.encode("utf-8")) return "{}={}".format(summary, maccer.hexdigest()) def check_user(self, string): parts = string.split("=", 1) if len(parts) != 2: # wrong format, expecting summary:hash return False summary, hash = map(lambda s: s.encode("utf-8"), parts) maccer = self.maccer.copy() maccer.update(summary) return hmac.compare_digest(maccer.hexdigest().encode("utf-8"), hash)