Close

How to use DBMS_LDAP (part 4: Attributes)

Table of Contents

  1. Introduction
  2. Establishing a Connection
  3. Searching Hierarchical Data
  4. Browsing Attributes (this page)
  5. Timeouts
  6. Modifying Data

Searching Hierarchical Data with Attributes

Returning attributes with search results

The examples in the prior chapter use the “1.1” code to return no attributes.
In this example we’ll return two of the attributes associated with an entry.
Instead of building a collection of one element containing “1.1”, the collection will have the name of each attributes we want to return.
Also note, the attronly => 0 parameter. If attronly is 1 then only the names of attributes will be returned. With 0, the values associated with each attributes are also returned.

DECLARE
    v_result               PLS_INTEGER;
    v_session              DBMS_LDAP.session;
    v_search_attrs         DBMS_LDAP.string_collection;
    v_search_results       DBMS_LDAP.MESSAGE;
    v_entry                DBMS_LDAP.MESSAGE;
    v_distinguished_name   VARCHAR2(256);
    v_values               DBMS_LDAP.string_collection;
BEGIN
    DBMS_LDAP.use_exception := TRUE;

    v_session := DBMS_LDAP.init(hostname => 'db.debian.org', portnum => DBMS_LDAP.port);

    v_search_attrs(1) := 'description';
    v_search_attrs(2) := 'admin';

    v_result :=
        DBMS_LDAP.search_s(ld         => v_session,
                           base       => 'host=beach,ou=hosts,dc=debian,dc=org',
                           scope      => DBMS_LDAP.scope_base,
                           filter     => 'objectclass=*',
                           attrs      => v_search_attrs,
                           attronly   => 0,
                           res        => v_search_results);

    v_entry := DBMS_LDAP.first_entry(v_session, v_search_results);

    v_distinguished_name := DBMS_LDAP.get_dn(v_session, v_entry);
    DBMS_OUTPUT.put_line('DN: ' || v_distinguished_name);

    FOR i IN 1 .. v_search_attrs.COUNT
    LOOP
        v_values := DBMS_LDAP.get_values(v_session, v_entry, v_search_attrs(i));

        IF v_values.COUNT > 0
        THEN
            FOR j IN v_values.FIRST .. v_values.LAST
            LOOP
                DBMS_OUTPUT.put_line(v_search_attrs(i) || ' : ' || v_values(j));
            END LOOP;
        ELSE
            DBMS_OUTPUT.put_line(v_search_attrs(i) || ' not found');
        END IF;
    END LOOP;

    v_result := DBMS_LDAP.msgfree(v_entry);
    v_result := DBMS_LDAP.unbind_s(v_session);
END;
/
DN: host=beach,ou=hosts,dc=debian,dc=org
description : bugs.debian.org web frontend
admin : debian-admin@lists.debian.org

Sometimes you might not know all of the attributes for an entry. Perhaps you’re doing a sub-tree search across different entry types and the attributes might change. In these scenarios you may need to employ a wildcard search and return all of the attributes for a given entry.

To return all of them, simply define an attribute array of one element consisting of the value ‘*’. It is legal syntax to include other attribute names in the array, but there is no functional value in doing so.

In the snippet below we’ll pull all of the attributes for the “host=beach” entry. Because we don’t know their names, in order to retrieve them we use a new data type: “DBMS_LDAP.BER_ELEMENT”. “BER” stands for Basic Encoding Rule. The datatype is a RAW, holding a pointer to the encoded attributes. With attronly set to 1, only the names of the attributes will be returned.

DECLARE
    v_result               PLS_INTEGER;
    v_session              DBMS_LDAP.session;
    v_search_attrs         DBMS_LDAP.string_collection;
    v_search_results       DBMS_LDAP.MESSAGE;
    v_entry                DBMS_LDAP.MESSAGE;
    v_distinguished_name   VARCHAR2(256);
    v_attribute_name       VARCHAR2(256); 
    v_ber                  DBMS_LDAP.ber_element;
BEGIN
    DBMS_LDAP.use_exception := TRUE;

    v_session := DBMS_LDAP.init(hostname => 'db.debian.org', portnum => DBMS_LDAP.port);

    v_search_attrs(1) := '*';    

    v_result :=
        DBMS_LDAP.search_s(ld         => v_session,
                           base       => 'host=beach,ou=hosts,dc=debian,dc=org',
                           scope      => DBMS_LDAP.scope_base,
                           filter     => 'objectclass=*',
                           attrs      => v_search_attrs,
                           attronly   => 1,
                           res        => v_search_results);

    v_entry := DBMS_LDAP.first_entry(v_session, v_search_results);

    v_distinguished_name := DBMS_LDAP.get_dn(v_session, v_entry);
    DBMS_OUTPUT.put_line('DN: ' || v_distinguished_name);

    v_attribute_name := DBMS_LDAP.first_attribute(v_session, v_entry, v_ber);

    WHILE v_attribute_name IS NOT NULL
    LOOP
        DBMS_OUTPUT.put_line(v_attribute_name);

        v_attribute_name := DBMS_LDAP.next_attribute(v_session, v_entry, v_ber);
    END LOOP;

    DBMS_LDAP.ber_free(v_ber, 1);

    v_result := DBMS_LDAP.unbind_s(v_session);
END;
/
DN: host=beach,ou=hosts,dc=debian,dc=org
objectClass
distribution
access
admin
architecture
host
purpose
allowedGroups
description
hostname
sshRSAHostKey
mXRecord
rebootPolicy
physicalHost
sponsor
ipHostNumber

Once you have the names you could then go back and pull values for specific attributes as shown in the first example above. Or, we can return the attributes from a wildcard along with their attributes.

DECLARE
    v_result               PLS_INTEGER;
    v_session              DBMS_LDAP.session;
    v_search_attrs         DBMS_LDAP.string_collection;
    v_search_results       DBMS_LDAP.MESSAGE;
    v_entry                DBMS_LDAP.MESSAGE;
    v_distinguished_name   VARCHAR2(256);
    v_attribute_name       VARCHAR2(256);
    v_attribute_values     DBMS_LDAP.string_collection;
    v_ber                  DBMS_LDAP.ber_element; -- Basic Encoding Rule
BEGIN
    DBMS_LDAP.use_exception := TRUE;

    v_session := DBMS_LDAP.init(hostname => 'db.debian.org', portnum => DBMS_LDAP.port);

    v_search_attrs(1) := '*';

    v_result :=
        DBMS_LDAP.search_s(ld         => v_session,
                           base       => 'host=beach,ou=hosts,dc=debian,dc=org',
                           scope      => DBMS_LDAP.scope_base,
                           filter     => 'objectclass=*',
                           attrs      => v_search_attrs,
                           attronly   => 0,
                           res        => v_search_results);

    v_entry := DBMS_LDAP.first_entry(v_session, v_search_results);

    v_distinguished_name := DBMS_LDAP.get_dn(v_session, v_entry);
    DBMS_OUTPUT.put_line('DN: ' || v_distinguished_name);

    v_attribute_name := DBMS_LDAP.first_attribute(v_session, v_entry, v_ber);

    WHILE v_attribute_name IS NOT NULL
    LOOP
        v_attribute_values := DBMS_LDAP.get_values(v_session, v_entry, v_attribute_name);

        CASE v_attribute_values.COUNT
            WHEN 0
            THEN
                NULL;
            WHEN 1
            THEN
                DBMS_OUTPUT.put_line(v_attribute_name || ' : ' || v_attribute_values(0));
            ELSE
                DBMS_OUTPUT.put_line(v_attribute_name || ' :');

                FOR i IN v_attribute_values.FIRST .. v_attribute_values.LAST
                LOOP
                    DBMS_OUTPUT.put_line('...' || v_attribute_values(i));
                END LOOP;
        END CASE;

        v_attribute_name := DBMS_LDAP.next_attribute(v_session, v_entry, v_ber);
    END LOOP;

    DBMS_LDAP.ber_free(v_ber, 1);

    v_result := DBMS_LDAP.unbind_s(v_session);
END;
/
DN: host=beach,ou=hosts,dc=debian,dc=org
objectClass :
...top
...debianServer
distribution : Debian GNU/Linux
access : restricted
admin : debian-admin@lists.debian.org
architecture : amd64
host : beach
purpose : [[*bugs.debian.org|bugs web frontend]]
allowedGroups :
...debbugs
...Debian
description : bugs.debian.org web frontend
hostname : beach.debian.org

Note, some of the attributes can have more than one value. The “objectClass” attribute is a common one to have multiple values. This is important to remember when retrieving values for an entry that there isn’t a 1:1 distribution.

Also note, while you can wildcard the entirety of an attribute set, the “attrs” parameter collection does not support wildcards within an attribute name.
For example there is no syntax to request all attributes starting with the letter “a”. Trying something like “v_search_attrs(1) := ‘a*’;” won’t produce an error, it will simply fail to find an attribute with the exact name “a*”.

If you wanted to simulate such functionality you would need to return the list of all attribute names, then use that list to generate just the subset of attributes in which you were interested and build a collection of those. Then search again using just those names, as in the first example, but with attrsonly=>0 to return their values.

In the next chapter we’ll explore setting timeouts on searches.

2 thoughts on “How to use DBMS_LDAP (part 4: Attributes)

  1. Great article!
    I have a question :
    Is it possible (using dbms_ldap package) to update/add attributes that support multiple values (for example a Distribution list/group with multiple “member” values) ?
    Thanks,
    Alex

    1. Yes it is possible. In part 6 of my series I show how to modify data including adding, updating, and deleting records.
      When inserting or updating you populate an array of values. In my examples I only use one attribute, but it’s an array so you can insert or modify more than one value at a time.

      There is no api for modifying more than one dn entries value at a time though.
      If you had multiple entries to insert/update/delete you’d have to loop through them calling the add_s, modify_s, delete_s, or delete function for each one.

Leave a Reply