TRIVIAL-LDAP v0.5 TRIVIAL-LDAP is a one file, all lisp client implementation of parts of RFC 2251 - Lightweight Directory Access Protocol (v3). WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING It should be noted from the start that my intent when I began was not to provide a full featured LDAP library per se; rather, I required some functionality for a different project altogether and was unable to locate a Common Lisp LDAP client implementation. I've implemented more of the protocol than I actually required, but stopped short of a full implementation. The omissions I am aware of are: - UTF-8 is not implemented - the SASL authentication method is not implemented - controls are not implemented - extended DN searches are not implemented - referrals are not followed (searches returning referrals will simply no-op following the referrals). - extended request/response is not implemented, beyond handling a notice of disconnection. If there is any interest I would consider augmenting trivial-ldap with the missing functionality. Further, just enough BER encoding/decoding is implemented to handle LDAP. I'm not sure that it would be all that difficult to implement a stand-alone BER library, but it was tangental to the problem I was looking to solve. Finally, I'm fairly new to programming in Common Lisp. It is entirely possible that this code is really awful, slow, unidiomatic, and unfit for consumption; and worse, i'm not a library writer at all (I spend my days writing glue code and what would have been shell scripts if this were 1987). If there are any suggestions of how I might go about making the code more "lispy" or providing a more useful API drop me a line and give me a clue. CONTACT: kevin montuori email@example.com COPYRIGHT: TRIVIAL-LDAP is Copyright 2005-2006 Kevin Montuori This package is redsitributable under the terms of The Clarified Artistic License, a copy of which should have been included in the distribution. INSTALLATION: TRIVIAL-LDAP comprises one lisp source file, which should be compiled and loaded. Alternatively, the distribution contains an asdf file in case that's more handy. There are two third-party library requirements: trivial-sockets http://www.cliki.net/trivial-sockets cl-ppcre http://www.cliki.net/CL-PPCRE (trivial-sockets is not in any way related to trivial-ldap except for the "trivial-" prefix, which I liked and thought appropriate.) I have tested with openmcl (1.0) and sbcl (0.9.7) on OS X, against an openldap directory server (slapd 2.2.19). USING TRIVIAL-LDAP: There are two classes implemented, LDAP and ENTRY. An instance of the LDAP class represents a LDAP server (and connection) while the latter represents an instance of a directory entry. There are two types of public methods: those prefixed with "ldap/" are the lower-level methods and should be though of as "operating on the directory." Those with an "entry/" prefix are higher level and can be thought of as operating on an entry. Note that if a Notice of Disconnection is received, this client will immediately sever the connection from the LDAP server. Whatever LDAP response code (e.g., protocolError) and message will be returned if it is appropriate. PUBLIC API: ldap/new &key (host "localhost") (port 389) user pass base debug creates a new ldap object. host and port should be self explanatory. :user and :pass are strings representing the DN and password to bind with. :base provides a default search base. If :debug is t debugging messages will be printed to *debug-io*. ldap/host ldap-obj ldap/port ldap-obj ldap/user ldap-obj ldap/pass ldap-obj ldap/base ldap-obj ldap/debug ldap-obj get/set each of the ldap object's attributes ldap/bind ldap-obj bind to the ldap server. ldap/unbind ldap-obj unbind from the ldap server and close the stream ldap/abandon ldap-obj abandon the current request and trash bytes waiting to be read LDAP operations: each of these methods returns three values: first: t or nil: incicates whether of not the operation was successful, from the LDAP directory server's point of view. second: the name of the result code (see +ldap-result-codes+ for a list of code names & numbers). third: any message the LDAP directory server returned. ldap/add ldap-obj entry-obj add entry-obj to the directory ldap/delete ldap-obj entry-obj delete entry-obj from the directory ldap/moddn ldap-obj dn-or-entry new-rdn &key (delete-old nil) modify the RDN of a directory entry. The entry to be modified may be specified as an entry object or as a DN string. If :delete-old is t, the old RDN attribute will be removed. ldap/compare ldap-obj dn-or-entry attribute value send a compare request. The directory entry in question may be specified as an entry object or as a DN string. The attribute may be a symbol or string. Returns t if the response code from the LDAP directory server is compareTrue and nil otherwise. If the request was successful but the assertion failed, the second return value will be compareFalse ldap/modify ldap-obj dn-or-entry list-of-mods modify a directory entry. The directory entry may be specified as an entry object of as a DN string. The list-of-mods will be a list of three element lists: (type attribute value) where type will be one of the symbols ADD, DELETE, or REPLACE. ldap/search ldap-obj filter &key base (scope 'SUB) (deref 'NEVER) (size-limit 0) (time-limit 0) (types-only nil) attributes search a directory server. The filter should be a valid LDAP filter but please note that extended filters are NOT implemented. The parentheses must be balanced however the outer parentheses may be omitted (i.e., "cn=quux" and "(cn=quux)" are the same filter. :base defaults to the value of the ldap-obj's base attribute. :scope may be one of BASE, ONE, or SUB :deref may be one of NEVER, SEARCH, FIND, or ALWAYS :size-limit and :time-limit are integers :types-only should be t or nil :attributes should be a list of attributes to return ldap/search returns three values: first: list of entry objects, or an empty list if no matches are found second: the LDAP response name third: any message sent from the directory server ldap/ldif-search (macro: arguments as for ldap/search, above) returns an LDIF string containing the search results entry/new DN-string &key attrs (infer-rdn t) create a new entry object. Attributes are specified as an alist: :attr '((l . ("Boston" "Cambridge")) (objectclass . organizationalunit)) note that attribute names may be either strings or symbols but will be converted to symbols internally. if :infer-rdn is not nil the RDN attribute will be appended to the attribute alist. entry/rdn entry-obj return the entry's RDN. To change the entry's RDN, however, see entry/chagne-rdn below. entry/change-rdn entry-obj new-rdn change the RDN (and therefore DN) of an entry object, but do not send a moddn message to the LDAP directory. entry/attr-value entry-obj attribute return list of values for the specified attribute. A list is always returned, even if there is only one value associated with the attribute. entry/attr-list entry-obj return a list of an entry's attributes, as symbols. entry/ldif entry-obj return a LDIF string representation of an entry object. ENTRY operations: these methods return t or nil, or throw the error LDAP-RESPONSE-ERROR if the LDAP directory server returns an error status. Arguments are described in the LDAP section above. entry/add entry-obj ldap-obj add an entry to the LDAP directory. entry/delete entry-obj ldap-obj entry/delete DN-string ldap-obj delete an entry from the LDAP directory. entry/moddn entry-obj ldap-obj new-rdn &key delete-old modify a directory entry's RDN. This method (unlike ldap/moddn) also modifies the entry object's attributes accordingly. entry/compare entry-obj ldap-obj attribute value entry/compare DN-string ldap-obj attribute value issuse a compare request. Returns t if compareTrue, nil if compareFalse, and throws an error otherwise. entry/modify entry-obj ldap-obj list-of-mods modifies a directory entry's attributes. This method, (unlike ldap/modify) modifies the entry object's attriibutes if the LDAP request succeeds.