Example #1
0
    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)
Example #2
0
    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)