import re import ldap from webapp import config def is_valid_name(name): """`true` if `name` is a safe ldap uid/cn""" return re.match(r'^[a-zA-Z_][a-zA-Z0-9-_\.]*\Z', name) is not None def validate_name(name): """Raises `RuntimeError` if `name` is not a safe ldap uid/cn""" if not is_valid_name(name): raise RuntimeError('Invalid name') def user_dn(uid): validate_name(uid) return config.ldap_user_dn_format.format(uid) def group_dn(cn): validate_name(cn) return config.ldap_group_dn_format.format(cn) def wrap(filter): if len(filter) and filter[0] == '(' and filter[-1] == ')': return filter else: return f'({filter})' def _or(*filters): wrapped = ''.join(wrap(f) for f in filters) return f'(|{wrapped})' def _and(*filters): wrapped = ''.join(wrap(f) for f in filters) return f'(&{wrapped})' def _not(filter): wrapped = wrap(filter) return f'(!{wrapped})' def member_of_any(groups): """Returns a filter that matches users that are a member of any of the given group names""" return _or(*(f'memberOf={group_dn(group)}' for group in groups)) def groups_of_user(uid): """Returns a filter that matches groups that have the given user as a member""" return f'(&(objectClass=groupOfUniqueNames)(uniqueMember={user_dn(uid)}))' def normalized_entries(entries): """ Converts ldap entries from python-ldap format into a more convenient List[Tuple[ dn, List[tuple[attr_name, attr_value]] ]] """ normalized = [] for dn, attrs in entries: normalized_attrs = [] for attr, values in attrs.items(): for value in values: normalized_attrs.append((attr, value.decode())) normalized.append((dn, normalized_attrs)) return normalized