def save(self, entry): """Save the given entry and its attribute values to the LDAP server. :param entry: The entry to save. :type entry: ldapom.LDAPEntry """ if self._read_only: raise error.LDAPomReadOnlyError entry_exists = entry.exists() # Refuse to save if attributes have not been fetched or set explicitly. if entry._attributes is None: raise error.LDAPomError("Cannot save without attributes " "previously fetched or set.") if entry_exists: assert entry._fetched_attributes is not None changed_attributes = entry._attributes - entry._fetched_attributes # Deleted attributes are represented as empty attributes to the LDAP server. deleted_attribute_names = (frozenset(a.name for a in entry._fetched_attributes) - frozenset(a.name for a in entry._attributes)) for deleted_name in deleted_attribute_names: deleted_attribute_type = self.get_attribute_type(deleted_name) changed_attributes.add(deleted_attribute_type(deleted_name)) else: # Don't try to save empty attributes as this fails if the entry does # not exist on the server yet. changed_attributes = set(filter(lambda attr: len(attr._values) > 0, entry._attributes)) # Don't try to save an empty modification set if not changed_attributes: return # Keep around references to pointers to owned memory with data that is # still needed. prevent_garbage_collection = [] mods = ffi.new("LDAPMod*[{}]".format(len(changed_attributes) + 1)) for i, attribute in enumerate(changed_attributes): mod = ffi.new("LDAPMod *") prevent_garbage_collection.append(mod) mod.mod_op = libldap.LDAP_MOD_REPLACE | libldap.LDAP_MOD_BVALUES mod_type = ffi.new("char[]", compat._encode_utf8(attribute.name)) prevent_garbage_collection.append(mod_type) mod.mod_type = mod_type modv_bvals = ffi.new("BerValue*[{}]".format(len(attribute._values) + 1)) prevent_garbage_collection.append(modv_bvals) for j, value in enumerate(attribute._get_ldap_values()): modv_berval = ffi.new("BerValue *") prevent_garbage_collection.append(modv_berval) modv_berval.bv_len = len(value) bval = ffi.new("char[]", len(value)) prevent_garbage_collection.append(bval) ffi.buffer(bval)[:] = value modv_berval.bv_val = bval modv_bvals[j] = modv_berval modv_bvals[len(attribute._values)] = ffi.NULL mod.mod_vals = {"modv_bvals": modv_bvals} mods[i] = mod mods[len(changed_attributes)] = ffi.NULL if entry_exists: err = libldap.ldap_modify_ext_s(self._ld, compat._encode_utf8(entry.dn), mods, ffi.NULL, ffi.NULL) else: err = libldap.ldap_add_ext_s(self._ld, compat._encode_utf8(entry.dn), mods, ffi.NULL, ffi.NULL) handle_ldap_error(err) entry._fetched_attributes = copy.deepcopy(entry._attributes)
def _raw_search(self, search_filter=None, retrieve_attributes=None, base=None, scope=libldap.LDAP_SCOPE_SUBTREE, retrieve_operational_attributes=False): """ Raw wrapper around OpenLDAP ldap_search_ext_s. :param search_filter: Filter expression to use. OpenLDAP default used if None is given. :type search_filter: List of str :param retrieve_attributes: List of attributes to retrieve. If None is given, all user attributes are retrieved. :type retrieve_attributes: List of str :param base: Search base for the query. :type base: str :param scope: The search scope in the LDAP tree :param retrieve_operational_attributes: Retrieve operational attributes of entries in addition to user attributes if retrieve_attributes is not set. """ search_result_p = ffi.new("LDAPMessage **") # Keep around references to pointers to owned memory with data that is # still needed. prevent_garbage_collection = [] if retrieve_attributes is None: retrieve_attributes = [ compat._decode_utf8(ffi.string(libldap.LDAP_ALL_USER_ATTRIBUTES))] if retrieve_operational_attributes: retrieve_attributes.append( compat._decode_utf8(ffi.string(libldap.LDAP_ALL_OPERATIONAL_ATTRIBUTES))) attrs_p = ffi.new("char*[{}]".format(len(retrieve_attributes) + 1)) for i, a in enumerate(retrieve_attributes): attr_p = ffi.new("char[]", compat._encode_utf8(a)) prevent_garbage_collection.append(attr_p) attrs_p[i] = attr_p attrs_p[len(retrieve_attributes)] = ffi.NULL err = libldap.ldap_search_ext_s( self._ld, compat._encode_utf8(base or self._base), scope, (compat._encode_utf8(search_filter) if search_filter is not None else ffi.NULL), attrs_p, 0, ffi.NULL, ffi.NULL, ffi.NULL, # TODO: Implement timeouts 0,#libldap.LDAP_NO_LIMIT, search_result_p) handle_ldap_error(err) search_result = search_result_p[0] current_entry = libldap.ldap_first_entry(self._ld, search_result) while current_entry != ffi.NULL: dn = ffi.string(libldap.ldap_get_dn(self._ld, current_entry)) attribute_dict = {} ber_p = ffi.new("BerElement **") current_attribute = libldap.ldap_first_attribute(self._ld, current_entry, ber_p) while current_attribute != ffi.NULL: current_attribute_str = ffi.string(current_attribute) attribute_dict[current_attribute_str] = [] values_p = libldap.ldap_get_values_len(self._ld, current_entry, current_attribute) for i in range(0, libldap.ldap_count_values_len(values_p)): val = ffi.buffer(values_p[i].bv_val, values_p[i].bv_len)[:] attribute_dict[current_attribute_str].append(val) libldap.ldap_memfree(current_attribute) current_attribute = libldap.ldap_next_attribute(self._ld, current_entry, ber_p[0]) libldap.ber_free(ber_p[0], 0) yield (dn, attribute_dict) current_entry = libldap.ldap_next_entry(self._ld, current_entry) libldap.ldap_msgfree(search_result)