ldap API reference
An LDAP v3 client and a lightweight, read-only directory server (RFC 4511), over TCP with the wire messages built and parsed as ASN.1 BER (the asn1 library) on the net transport. The client binds (simple or SASL SCRAM), searches with RFC 4515 string filters, follows paged results, and unbinds; LDAPS and StartTLS reuse net's TLS. The server answers simple bind and search for an in-memory directory you build in a few lines - enough to back any LDAP client (an authentication portal such as Authelia, or an app that authenticates its users against LDAP): it validates userPassword on bind and evaluates search filters against the entries so a client can find users and read their groups.
The server is read-only over the LDAP protocol (it answers bind and search but rejects add / modify / delete on the wire), yet the in-memory directory is fully mutable from your own code: after building it you can addEntry / modifyEntry / deleteEntry / setAttribute at runtime, so a web interface or a database sync can update the directory a running server is serving. The Directory is shared mutable state (a kv-backed store shared across the serve loop's connection tasks), so a change is visible to the live server immediately. No schema enforcement, no ACLs.
Import with import "ldap.j" as ldap;. See the ldap guide for prose and examples.
Functions
ldap.add(conn as Conn, dn as string, attrs as map of string to list of string)
add creates an entry from a DN and an attribute map, returning the Result (check .code).
Parameters
conn{Conn}- an open, bound connectiondn{string}- the DN of the entry to createattrs{map of string to list of string}- the entry's attributes (name -> values)
Returns {Result} - the operation result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.addEntry(dir as Directory, e as Entry)
addEntry inserts an entry, replacing any existing entry with the same DN.
Parameters
dir{Directory}- the directory to modifye{Entry}- the entry to insert (or replace a same-DN entry with)
ldap.allOf(filters as list of asn1.Value)
allOf builds a conjunction: (&(f1)(f2)...).
Parameters
filters{list of asn1.Value}- the sub-filters that must all match
Returns {asn1.Value} - the combined filter, for search
ldap.anyOf(filters as list of asn1.Value)
anyOf builds a disjunction: (|(f1)(f2)...).
Parameters
filters{list of asn1.Value}- the sub-filters, any of which may match
Returns {asn1.Value} - the combined filter, for search
ldap.approx(attr as string, value as string)
approx builds an approximate-match (attr~=value) filter; the server treats it as equality.
Parameters
attr{string}- the attribute namevalue{string}- the value to match approximately
Returns {asn1.Value} - the encoded filter, for search
ldap.bind(conn as Conn, dn as string, password as string)
bind performs a simple bind and returns the Result (check .code against ldap.SUCCESS / ldap.INVALID_CREDENTIALS). An empty dn and password is an anonymous bind.
Parameters
conn{Conn}- an open connectiondn{string}- the bind DN ("" for anonymous)password{string}- the password ("" for anonymous)
Returns {Result} - the bind result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.bindSasl(conn as Conn, user as string, password as string, algo as string)
bindSasl performs a SASL SCRAM bind (algo "sha1" -> SCRAM-SHA-1, "sha256" -> SCRAM-SHA-256), verifying the server signature. Returns the Result on success or throws on a protocol / verification failure.
Parameters
conn{Conn}- an open connectionuser{string}- the SCRAM authentication usernamepassword{string}- the user's passwordalgo{string}- the SCRAM hash: "sha1" or "sha256"
Returns {Result} - the bind result on success
Throws
{Error}- on a protocol or server-signature-verification failure
ldap.change(operation as int, name as string, values as list of string)
change builds one modify change (operation is ldap.MOD_ADD / MOD_DELETE / MOD_REPLACE).
Parameters
operation{int}- ldap.MOD_ADD, MOD_DELETE, or MOD_REPLACEname{string}- the attribute to changevalues{list of string}- the values for the change (empty deletes the attribute)
Returns {Change} - the change, for modify
ldap.close(conn as Conn)
close closes the connection without sending an unbind.
Parameters
conn{Conn}- the connection to close
ldap.connect(address as string, security as transport.Security)
connect opens an LDAP connection. security selects the transport: transport.Security.None is plaintext (port defaults to 389), transport.Security.Tls is LDAPS / implicit TLS (port defaults to 636), and transport.Security.Starttls connects in plaintext then upgrades in-band via the StartTLS extended operation.
Parameters
address{string}- the server "host:port" (port defaults to 389, or 636 for Tls)security{transport.Security}- the transport: None, Tls, or Starttls
Returns {Conn} - the open connection
Throws
{Error}- on a connection or StartTLS failure
ldap.delete(conn as Conn, dn as string)
delete removes an entry by DN, returning the Result.
Parameters
conn{Conn}- an open, bound connectiondn{string}- the DN of the entry to remove
Returns {Result} - the operation result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.deleteEntry(dir as Directory, dn as string)
deleteEntry removes the entry with the given DN if present.
Parameters
dir{Directory}- the directory to modifydn{string}- the DN of the entry to remove (matched case-insensitively)
ldap.directory(entries as list of Entry)
directory builds a mutable in-memory directory the server answers for - the lightweight, ephemeral default. Use openDirectory for one that persists.
Parameters
entries{list of Entry}- the initial entries to seed the directory with
Returns {Directory} - the in-memory directory
ldap.entry(dn as string, attrs as map of string to list of string)
entry builds a directory entry from a DN and an attribute map (name -> values).
Parameters
dn{string}- the entry's distinguished nameattrs{map of string to list of string}- the entry's attributes (name -> values)
Returns {Entry} - the constructed entry
ldap.equals(attr as string, value as string)
equals builds an equality filter: (attr=value).
Parameters
attr{string}- the attribute namevalue{string}- the value to match exactly
Returns {asn1.Value} - the encoded filter, for search
ldap.firstValue(entry as Entry, name as string)
firstValue returns an entry's first value for an attribute, or "" if absent.
Parameters
entry{Entry}- the entry to readname{string}- the attribute name, matched case-insensitively
Returns {string} - the first value, or "" if the attribute has none
ldap.getEntry(dir as Directory, dn as string)
getEntry returns the entry with the given DN, or an empty Entry (dn == "") if absent.
Parameters
dir{Directory}- the directory to readdn{string}- the DN to look up (matched case-insensitively)
Returns {Entry} - the matching entry, or an empty Entry (dn == "") if absent
ldap.greaterOrEqual(attr as string, value as string)
greaterOrEqual builds an (attr>=value) filter.
Parameters
attr{string}- the attribute namevalue{string}- the lower bound to compare against
Returns {asn1.Value} - the encoded filter, for search
ldap.group(dn as string, members as list of string)
group builds a groupOfNames entry (objectClass + cn from the DN's RDN + member) from members.
Parameters
dn{string}- the group's distinguished name (its RDN value becomes cn)members{list of string}- the member DNs
Returns {Entry} - the constructed groupOfNames entry
ldap.hasEntry(dir as Directory, dn as string)
hasEntry reports whether an entry with the given DN exists.
Parameters
dir{Directory}- the directory to readdn{string}- the DN to test (matched case-insensitively)
Returns {bool} - true if an entry with that DN exists
ldap.lessOrEqual(attr as string, value as string)
lessOrEqual builds an (attr<=value) filter.
Parameters
attr{string}- the attribute namevalue{string}- the upper bound to compare against
Returns {asn1.Value} - the encoded filter, for search
ldap.listEntries(dir as Directory)
listEntries returns a snapshot of every entry currently in the directory.
Parameters
dir{Directory}- the directory to read
Returns {list of Entry} - a snapshot of all current entries
ldap.listen(address as string)
listen opens a listening socket for the server ("host:port"; port defaults to 389).
Parameters
address{string}- the listen address "host:port" (port defaults to 389)
Returns {net.Listener} - the open listener, for serveOn
Throws
{Error}- if the socket cannot be opened
ldap.modify(conn as Conn, dn as string, changes as list of Change)
modify applies a list of changes to an entry, returning the Result.
Parameters
conn{Conn}- an open, bound connectiondn{string}- the DN of the entry to modifychanges{list of Change}- the changes to apply, from change
Returns {Result} - the operation result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.modifyDn(conn as Conn, dn as string, newRdn as string, deleteOldRdn as bool, newSuperior as string)
modifyDn renames or moves an entry: newRdn is the new relative DN, deleteOldRdn drops the old RDN attribute, and newSuperior ("" to keep the parent) moves the entry under a new parent DN.
Parameters
conn{Conn}- an open, bound connectiondn{string}- the DN of the entry to rename or movenewRdn{string}- the new relative DN (e.g. "cn=bob")deleteOldRdn{bool}- whether to drop the old RDN attribute valuenewSuperior{string}- the new parent DN, or "" to keep the current parent
Returns {Result} - the operation result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.modifyEntry(dir as Directory, e as Entry)
modifyEntry replaces the entry with the same DN (an alias for addEntry).
Parameters
dir{Directory}- the directory to modifye{Entry}- the replacement entry, keyed by its DN
ldap.negate(f as asn1.Value)
negate builds a negation: (!(f)).
Parameters
f{asn1.Value}- the sub-filter to invert
Returns {asn1.Value} - the negated filter, for search
ldap.openDirectory(path as string)
openDirectory opens a file-backed directory that persists across restarts (a kv.openFile store at path). A new file starts empty; an existing one restores its entries. Seed a fresh store on first run by checking listEntries and addEntry-ing when it is empty. Edits (addEntry / modifyEntry / ...) persist.
Parameters
path{string}- the backing file path for the kv store
Returns {Directory} - the file-backed directory
ldap.parseFilter(s as string)
parseFilter compiles an RFC 4515 filter string (e.g. "(&(a=1)(b=2))") into a filter value.
Parameters
s{string}- the RFC 4515 filter text
Returns {asn1.Value} - the compiled filter, for search
Throws
{Error}- on a malformed filter string
ldap.password(plain as string, scheme as string)
password hashes a plaintext password into an LDAP userPassword value. scheme is "plain", "sha", "sha256", "ssha", "ssha256" (salted variants use a random 8-byte salt), "pbkdf2" (PBKDF2-SHA256), or "pbkdf2-sha512". Store the result as an entry's userPassword.
Parameters
plain{string}- the plaintext password to hashscheme{string}- one of plain / sha / sha256 / ssha / ssha256 / pbkdf2 / pbkdf2-sha512
Returns {string} - the encoded userPassword value (with its scheme prefix)
Throws
{Error}- on an unknown scheme
ldap.passwordModify(conn as Conn, userDn as string, oldPassword as string, newPassword as string)
passwordModify changes a password via the RFC 3062 extended operation. Pass userDn "" for the currently-bound user, oldPassword "" to skip the old-password check (an administrative reset), and newPassword "" to have the server generate one. Returns the Result.
Parameters
conn{Conn}- an open, bound connectionuserDn{string}- the target user DN, or "" for the currently-bound useroldPassword{string}- the current password, or "" to skip the check (admin reset)newPassword{string}- the new password, or "" to let the server generate one
Returns {Result} - the operation result (check .code)
Throws
{Error}- on a transport or protocol error
ldap.present(attr as string)
present builds a presence filter: (attr=*).
Parameters
attr{string}- the attribute that must be present
Returns {asn1.Value} - the encoded filter, for search
ldap.search(conn as Conn, baseDn as string, scope as int, filter as asn1.Value, attributes as list of string)
search runs a search and returns the matching entries. scope is one of ldap.SCOPE_BASE / SCOPE_ONE / SCOPE_SUB; filter comes from parseFilter or the filter constructors; attributes is the list to return (empty = all user attributes).
Parameters
conn{Conn}- an open, bound connectionbaseDn{string}- the search base DNscope{int}- ldap.SCOPE_BASE, SCOPE_ONE, or SCOPE_SUBfilter{asn1.Value}- the filter, from parseFilter or a filter constructorattributes{list of string}- the attributes to return (empty = all user attributes)
Returns {list of Entry} - the matching entries
Throws
{Error}- on a transport or protocol error
ldap.searchPaged(conn as Conn, baseDn as string, scope as int, filter as asn1.Value, attributes as list of string, pageSize as int)
searchPaged runs a search in pages of pageSize via the simple-paged-results control.
Parameters
conn{Conn}- an open, bound connectionbaseDn{string}- the search base DNscope{int}- ldap.SCOPE_BASE, SCOPE_ONE, or SCOPE_SUBfilter{asn1.Value}- the filter, from parseFilter or a filter constructorattributes{list of string}- the attributes to return (empty = all user attributes)pageSize{int}- the maximum entries per page
Returns {list of Entry} - all matching entries across every page
Throws
{Error}- on a transport or protocol error
ldap.serve(dir as Directory, address as string)
serve binds address and serves dir until the process ends (blocks).
Parameters
dir{Directory}- the directory to answer queries fromaddress{string}- the listen address "host:port" (port defaults to 389)
Throws
{Error}- if the socket cannot be opened
ldap.serveOn(dir as Directory, listener as net.Listener)
serveOn serves dir on an already-open listener, one spawn per connection, until the listener is closed (which ends the accept loop). Close the listener from another task for a graceful stop.
Parameters
dir{Directory}- the directory to answer queries fromlistener{net.Listener}- an open listener from listen
ldap.setAttribute(dir as Directory, dn as string, name as string, vals as list of string)
setAttribute creates or replaces one attribute's values on an existing entry.
Parameters
dir{Directory}- the directory to modifydn{string}- the DN of the entry to updatename{string}- the attribute to create or replacevals{list of string}- the new values for the attribute
Throws
{Error}- if no entry with dn exists
ldap.startTls(conn as Conn)
startTls upgrades a plaintext connection to TLS via the StartTLS extended operation, then hands the same handle back (now encrypted).
Parameters
conn{Conn}- an open plaintext connection from connect
Returns {Conn} - the same connection, now encrypted
Throws
{Error}- if the server refuses StartTLS
ldap.substrings(attr as string, initial as string, anyParts as list of string, final as string)
substrings builds a substring filter, e.g. (cn=abc): initial "a", any ["b"], final "c". Pass "" for an absent initial or final and an empty list for no interior parts.
Parameters
attr{string}- the attribute nameinitial{string}- the required leading substring, or "" for noneanyParts{list of string}- interior substrings in order, or empty for nonefinal{string}- the required trailing substring, or "" for none
Returns {asn1.Value} - the encoded filter, for search
ldap.unbind(conn as Conn)
unbind sends an unbind request and closes the connection.
Parameters
conn{Conn}- the connection to unbind and close
ldap.values(entry as Entry, name as string)
values returns an entry's values for an attribute (case-insensitive name), or an empty list.
Parameters
entry{Entry}- the entry to readname{string}- the attribute name, matched case-insensitively
Returns {list of string} - the attribute's values, or an empty list if absent
Structs
ldap.Attribute
One attribute of a directory entry.
| Field | Type | Description |
|---|---|---|
name | string | the attribute type (e.g. "mail", "objectClass") |
values | list of string | its values (LDAP attributes are multi-valued) |
ldap.Change
One change in a modify request.
| Field | Type | Description |
|---|---|---|
operation | int | ldap.MOD_ADD / MOD_DELETE / MOD_REPLACE |
name | string | the attribute to change |
values | list of string | the values (empty deletes the whole attribute for MOD_DELETE) |
ldap.Conn
An open LDAP connection.
| Field | Type | Description |
|---|---|---|
handle | net.Conn | the underlying TCP (or TLS) connection |
timeoutMs | int | the per-read timeout in ms (0 blocks with no deadline) |
ldap.Directory
A mutable in-memory directory the server answers for. It is backed by a shared kv store, so edits made with addEntry / modifyEntry / deleteEntry / setAttribute are visible to a running server across its connection tasks.
| Field | Type | Description |
|---|---|---|
store | kv.Store | the shared backing store holding every entry |
ldap.Entry
A directory entry.
| Field | Type | Description |
|---|---|---|
dn | string | the distinguished name |
attributes | list of ldap.Attribute | the entry's attributes |
ldap.Result
The outcome of a bind (or any LDAPResult).
| Field | Type | Description |
|---|---|---|
code | int | the result code (ldap.SUCCESS, or ldap.INVALID_CREDENTIALS on a bad bind) |
matchedDn | string | the matched DN the server reports, if any |
message | string | the server's diagnostic message |
Constants
| Constant | Type | Description |
|---|---|---|
ldap.INVALID_CREDENTIALS | int | LDAP result code: invalid credentials (a failed bind). |
ldap.MOD_ADD | int | modify() change type: add the given values to the attribute. |
ldap.MOD_DELETE | int | modify() change type: delete the given values (or the whole attribute if none). |
ldap.MOD_REPLACE | int | modify() change type: replace the attribute with the given values. |
ldap.SCOPE_BASE | int | Search scope: the base entry only. |
ldap.SCOPE_ONE | int | Search scope: the base's immediate children. |
ldap.SCOPE_SUB | int | Search scope: the base and its whole subtree. |
ldap.SUCCESS | int | LDAP result code: success. |