def modrdn_s(self, dn, new_rdn, *ign): elems = explode_dn(dn) rdn = elems[0] tree_pos = TREE.getElementByDN(elems[1:]) if rdn not in tree_pos: raise ldap.NO_SUCH_OBJECT(rdn) if new_rdn in tree_pos: raise ldap.ALREADY_EXISTS(new_rdn) rec = tree_pos.get(rdn) del tree_pos[rdn] tree_pos[new_rdn] = rec
def addTreeItems(self, dn): """ Add structure directly to the tree given a DN returns the last added tree position for convenience """ elems = explode_dn(dn) elems.reverse() tree_pos = self for elem in elems: if elem not in tree_pos: tree_pos[elem] = {} tree_pos = tree_pos[elem] return tree_pos
def modify_s(self, dn, mod_list): elems = explode_dn(dn) rdn = elems[0] tree_pos = TREE.getElementByDN(elems[1:]) if rdn not in tree_pos: raise ldap.NO_SUCH_OBJECT(rdn) rec = deepcopy(tree_pos.get(rdn)) for mod in mod_list: if mod[0] == ldap.MOD_REPLACE: rec[mod[1]] = mod[2] elif mod[0] == ldap.MOD_ADD: cur_val = rec.get(mod[1], []) cur_val.extend(mod[2]) rec[mod[1]] = cur_val else: if mod[1] in rec: if mod[2] is None: del rec[mod[1]] else: cur_vals = rec[mod[1]] for removed in mod[2]: if removed in cur_vals: cur_vals.remove(removed) rec[mod[1]] = cur_vals tree_pos[rdn] = rec # Maintain memberOf if self.maintain_memberof: for mod in mod_list: if mod[1] == self.member_attr: if mod[0] == ldap.MOD_ADD: for v in mod[2]: self.modify_s(v, [(ldap.MOD_ADD, self.memberof_attr, [dn])]) elif mod[0] == ldap.MOD_DELETE: for v in mod[2]: self.modify_s(v, [(ldap.MOD_DELETE, self.memberof_attr, [dn])])
def getElementByDN(self, dn): """ Get a tree element by DN Returns None if the path cannot be found """ if not isinstance(dn, list): elems = explode_dn(dn) else: elems = dn elems.reverse() tree_pos = self for elem in elems: if elem not in tree_pos: raise ldap.NO_SUCH_OBJECT(elem) else: tree_pos = tree_pos[elem] return tree_pos
def delete_s(self, dn): elems = explode_dn(dn) rdn = elems[0] tree_pos = TREE.getElementByDN(elems[1:]) if rdn not in tree_pos: raise ldap.NO_SUCH_OBJECT(rdn) # Maintain memberOf if self.maintain_memberof: record = tree_pos[rdn] if self.member_attr in record: for value in record[self.member_attr]: self.modify_s(value, [(ldap.MOD_DELETE, self.memberof_attr, [dn])]) if self.memberof_attr in record: for value in record[self.memberof_attr]: self.modify_s(value, [(ldap.MOD_DELETE, self.member_attr, [dn])]) del tree_pos[rdn]
def add_s(self, dn, attr_list): elems = explode_dn(dn) rdn = elems[0] tree_pos = TREE.getElementByDN(elems[1:]) if rdn in tree_pos: raise ldap.ALREADY_EXISTS(rdn) # Add rdn to attributes as well. rdn_key, rdn_value = rdn.split(b'=') tree_pos[rdn] = {rdn_key: [rdn_value]} record = tree_pos[rdn] for key, value in attr_list: record[key] = value # Maintain memberOf if self.maintain_memberof: if key == self.member_attr: for v in value: self.modify_s(v, [(ldap.MOD_ADD, self.memberof_attr, [dn])])
def search_s(self, base, scope=ldap.SCOPE_SUBTREE, query=b'(objectClass=*)', attrs=()): parsed_query = self.parser.parse_query(query) tree_pos = TREE.getElementByDN(base) if self.parser.cmp_query(parsed_query, ANY, strict=True): # Return all objects, no matter what class if scope == ldap.SCOPE_BASE: # Only if dn matches 'base' return [(base, deepcopy(self._filter_attrs(tree_pos, attrs)))] else: return [(k, deepcopy(self._filter_attrs(v, attrs))) for k, v in tree_pos.items()] if scope == ldap.SCOPE_BASE: # At this stage tree_pos will be a leaf record. We need to # "re-wrap" it. rdn = explode_dn(base)[0] tree_pos = {rdn: tree_pos} by_level = {} enumerated = enumerate(self.parser.explode_query(parsed_query)) for idx, (operation, filters) in enumerated: lvl = by_level[idx] = [] by_filter = {} for fltr in filters: sub = fltr(tree_pos, base) by_filter[fltr] = sub # Optimization: If it's an AND query bail out on # the first empty value, but still set the empty # value on by_filter so it gets caught in the # operations below. if not sub and operation.op in (b'&',): break if filters: values = by_filter.values() else: # If there are no filters, it's an operation on # all the previous levels. values = by_level.values() if operation.op in (b'|',): # Do an union lvl_vals = dict(lvl) lvl_keys = set(lvl_vals.keys()) for sub in values: sub_vals = dict(sub) sub_keys = set(sub_vals.keys()) for k in sub_keys - lvl_keys: lvl.append((k, sub_vals[k])) lvl_keys = sub_keys | lvl_keys elif operation.op in (b'&',): # Do an intersection for sub in values: # Optimization: If it's an AND query bail out on # the first empty value. if not sub: lvl[:] = [] break if not lvl: lvl[:] = sub else: new_lvl = [] lvl_vals = dict(lvl) sub_vals = dict(sub) lvl_keys = set(lvl_vals.keys()) sub_keys = set(sub_vals.keys()) for k in sub_keys & lvl_keys: new_lvl.append((k, lvl_vals[k])) lvl[:] = new_lvl if by_level: # Return the last one. return [(k, deepcopy(self._filter_attrs(v, attrs))) for k, v in by_level[idx]] return []