diff --git a/hshetl/loaders.py b/hshetl/loaders.py index bbf7ad7fa81c9ab92bbd683fdddd36c70bf02476..0668aeb8720da0c962ee722852f5db2a0f63c1d4 100644 --- a/hshetl/loaders.py +++ b/hshetl/loaders.py @@ -562,3 +562,96 @@ class LdapLoader(AbstractLoader): for attribute in changed_attributes: output += attribute + ': ' + old[attribute] + ' -> ' + new[attribute] + ', ' return output[:-2] + + def modifyModlist(self, old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None): + """ + Build differential modify list for calling LDAPObject.modify()/modify_s() + + old_entry + Dictionary holding the old entry + new_entry + Dictionary holding what the new entry should be + ignore_attr_types + List of attribute type names to be ignored completely + ignore_oldexistent + If non-zero attribute type names which are in old_entry + but are not found in new_entry at all are not deleted. + This is handy for situations where your application + sets attribute value to '' for deleting an attribute. + In most cases leave zero. + case_ignore_attr_types + List of attribute type names for which comparison will be made + case-insensitive + """ + import string, ldap + ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or []))) + case_ignore_attr_types = list_dict(map(string.lower,(case_ignore_attr_types or []))) + modlist = [] + attrtype_lower_map = {} + for a in old_entry.keys(): + attrtype_lower_map[string.lower(a)]=a + for attrtype in new_entry.keys(): + attrtype_lower = string.lower(attrtype) + if ignore_attr_types.has_key(attrtype_lower): + # This attribute type is ignored + continue + # Filter away null-strings + new_value = filter(lambda x:x!=None,new_entry[attrtype]) + if attrtype_lower_map.has_key(attrtype_lower): + old_value = old_entry.get(attrtype_lower_map[attrtype_lower],[]) + old_value = filter(lambda x:x!=None,old_value) + del attrtype_lower_map[attrtype_lower] + else: + old_value = [] + if not old_value and new_value: + # Add a new attribute to entry + modlist.append((ldap.MOD_ADD,attrtype,new_value)) + elif old_value and new_value: + # Replace existing attribute + replace_attr_value = old_value!=new_value + if not replace_attr_value: + case_insensitive = case_ignore_attr_types.has_key(attrtype_lower) + old_value_dict=list_dict(old_value,case_insensitive) + new_value_dict=list_dict(new_value,case_insensitive) + delete_values = [] + for v in old_value: + if not new_value_dict.has_key(v): + replace_attr_value = 1 + break + add_values = [] + if not replace_attr_value: + for v in new_value: + if not old_value_dict.has_key(v): + replace_attr_value = 1 + break + if replace_attr_value: + modlist.append((ldap.MOD_DELETE,attrtype,None)) + modlist.append((ldap.MOD_ADD,attrtype,new_value)) + elif old_value and not new_value: + # Completely delete an existing attribute + modlist.append((ldap.MOD_DELETE,attrtype,None)) + if not ignore_oldexistent: + # Remove all attributes of old_entry which are not present + # in new_entry at all + for a in attrtype_lower_map.keys(): + if ignore_attr_types.has_key(a): + # This attribute type is ignored + continue + attrtype = attrtype_lower_map[a] + modlist.append((ldap.MOD_DELETE,attrtype,None)) + return modlist # modifyModlist() + +def list_dict(l,case_insensitive=0): + """ + return a dictionary with all items of l being the keys of the dictionary + + If argument case_insensitive is non-zero ldap.cidict.cidict will be + used for case-insensitive string keys + """ + if case_insensitive: + d = ldap.cidict.cidict() + else: + d = {} + for i in l: + d[i]=None + return d