2011-08-23 23:25:07 +00:00
---
2011-08-26 18:00:21 +00:00
title: Server API | ldapjs
2011-08-23 23:25:07 +00:00
---
2011-08-26 18:00:21 +00:00
# ldapjs Server API
2011-08-23 23:25:07 +00:00
2020-12-13 08:06:45 +00:00
< div class = "intro" >
2011-08-24 23:49:58 +00:00
This document covers the ldapjs server API and assumes that you are familiar
2015-10-16 05:25:49 +00:00
with LDAP. If you're not, read the [guide ](guide.html ) first.
2011-08-23 23:25:07 +00:00
2020-12-13 08:06:45 +00:00
< / div >
2011-08-23 23:25:07 +00:00
# Create a server
The code to create a new server looks like:
2021-02-24 22:03:35 +00:00
```js
2021-02-24 22:21:06 +00:00
const server = ldap.createServer();
2021-02-24 22:03:35 +00:00
```
2011-08-23 23:25:07 +00:00
2011-08-25 04:36:48 +00:00
The full list of options is:
2011-08-23 23:25:07 +00:00
2016-11-03 17:59:40 +00:00
||log||You can optionally pass in a Bunyan compatible logger instance the client will use to acquire a child logger.||
2011-08-25 04:36:48 +00:00
||certificate||A PEM-encoded X.509 certificate; will cause this server to run in TLS mode.||
||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.||
2011-08-23 23:25:07 +00:00
2016-11-03 17:59:40 +00:00
### Note On Logger
The passed in logger is expected to conform to the Log4j standard API.
Internally, [abstract-logging ](https://www.npmjs.com/packages/abstract-logging ) is
used to implement the interface. As a result, no log messages will be generated
unless an external logger is supplied.
Known compatible loggers are:
+ [Bunyan ](https://www.npmjs.com/package/bunyan )
+ [Pino ](https://www.npmjs.com/package/pino )
2011-08-25 04:36:48 +00:00
## Properties on the server object
2011-08-23 23:25:07 +00:00
### maxConnections
Set this property to reject connections when the server's connection count gets
high.
2020-02-15 16:22:00 +00:00
### connections (getter only) - DEPRECATED
2011-08-23 23:25:07 +00:00
2020-02-15 16:22:00 +00:00
The number of concurrent connections on the server. This property is deprecated,
please use server.getConnections() instead.
2011-08-23 23:25:07 +00:00
### url
Returns the fully qualified URL this server is listening on. For example:
`ldaps://10.1.2.3:1636` . If you haven't yet called `listen` , it will always
return `ldap://localhost:389` .
### Event: 'close'
`function() {}`
Emitted when the server closes.
## Listening for requests
2015-10-16 05:25:49 +00:00
The LDAP server API wraps up and mirrors the node.js `server.listen` family of
2011-08-23 23:25:07 +00:00
APIs.
After calling `listen` , the property `url` on the server object itself will be
available.
Example:
2021-02-24 22:03:35 +00:00
```js
2021-02-24 22:21:06 +00:00
server.listen(389, '127.0.0.1', function() {
console.log('LDAP server listening at: ' + server.url);
});
2021-02-24 22:03:35 +00:00
```
2011-08-23 23:25:07 +00:00
### Port and Host
`listen(port, [host], [callback])`
Begin accepting connections on the specified port and host. If the host is
2023-02-22 17:35:07 +00:00
omitted, the server will accept connections directed to the IPv4 address
`127.0.0.1` . To listen on any other address, supply said address as the `host`
parameter. For example, to listen on all available IPv6 addresses supply
`::` as the `host` (note, this _may_ also result in listening on all
available IPv4 addresses, depending on operating system behavior).
We highly recommend being as explicit as possible with the `host` parameter.
Listening on all available addresses (through `::` or `0.0.0.0` ) can lead
to potential security issues.
2011-08-23 23:25:07 +00:00
This function is asynchronous. The last parameter callback will be called when
the server has been bound.
### Unix Domain Socket
`listen(path, [callback])`
Start a UNIX socket server listening for connections on the given path.
This function is asynchronous. The last parameter callback will be called when
the server has been bound.
### File descriptor
`listenFD(fd)`
Start a server listening for connections on the given file descriptor.
This file descriptor must have already had the `bind(2)` and `listen(2)` system
calls invoked on it. Additionally, it must be set non-blocking; try
`fcntl(fd, F_SETFL, O_NONBLOCK)` .
2020-02-15 16:22:00 +00:00
## Inspecting server state
### server.getConnections(callback)
The LDAP server API mirrors the [Node.js `server.getConnections` API ](https://nodejs.org/dist/latest-v12.x/docs/api/net.html#net_server_getconnections_callback ). Callback
should take two arguments err and count.
2011-08-23 23:25:07 +00:00
# Routes
2015-10-16 05:25:49 +00:00
The LDAP server API is meant to be the LDAP-equivalent of the express/restify
2011-08-23 23:25:07 +00:00
paradigm of programming. Essentially every method is of the form
`OP(req, res, next)` where OP is one of bind, add, del, etc. You can chain
handlers together by calling `next()` and ordering your functions in the
definition of the route. For example:
2021-02-24 22:03:35 +00:00
```js
function authorize(req, res, next) {
if (!req.connection.ldap.bindDN.equals('cn=root'))
return next(new ldap.InsufficientAccessRightsError());
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
return next();
}
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
server.search('o=example', authorize, function(req, res, next) { ... });
```
2011-08-23 23:25:07 +00:00
Note that ldapjs is also slightly different, since it's often going to be backed
to a DB-like entity, in that it also has an API where you can pass in a
'backend' object. This is necessary if there are persistent connection pools,
caching, etc. that need to be placed in an object.
For example [ldapjs-riak ](https://github.com/mcavage/node-ldapjs-riak ) is a
complete implementation of the LDAP protocol over
2015-10-16 05:25:49 +00:00
[Riak ](https://github.com/basho/riak ). Getting an LDAP server up with riak
looks like:
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
const ldap = require('ldapjs');
const ldapRiak = require('ldapjs-riak');
const server = ldap.createServer();
const backend = ldapRiak.createBackend({
"host": "localhost",
"port": 8098,
"bucket": "example",
"indexes": ["l", "cn"],
"uniqueIndexes": ["uid"],
"numConnections": 5
});
server.add("o=example",
backend,
backend.add());
...
```
2011-08-23 23:25:07 +00:00
2011-08-25 04:36:48 +00:00
The first parameter to an ldapjs route is always the point in the
2011-08-23 23:25:07 +00:00
tree to mount the handler chain at. The second argument is _optionally_ a
2011-08-25 04:36:48 +00:00
backend object. After that you can pass in an arbitrary combination of
functions in the form `f(req, res, next)` or arrays of functions of the same
signature (ldapjs will unroll them).
2011-08-23 23:25:07 +00:00
2011-08-25 04:36:48 +00:00
Unlike HTTP, LDAP operations do not have a heterogeneous wire format, so each
2012-01-06 23:10:46 +00:00
operation requires specific methods/fields on the request/response
objects. However, there is a `.use()` method availabe, similar to
that on express/connect, allowing you to chain up "middleware":
2021-02-24 22:03:35 +00:00
```js
server.use(function(req, res, next) {
console.log('hello world');
return next();
});
```
2011-08-23 23:25:07 +00:00
## Common Request Elements
2011-08-25 04:36:48 +00:00
All request objects have the `dn` getter on it, which is "context-sensitive"
2011-08-23 23:25:07 +00:00
and returns the point in the tree that the operation wants to operate on. The
LDAP protocol itself sadly doesn't define operations this way, and has a unique
name for just about every op. So, ldapjs calls it `dn` . The DN object itself
2015-10-16 05:25:49 +00:00
is documented at [DN ](dn.html ).
2011-08-23 23:25:07 +00:00
All requests have an optional array of `Control` objects. `Control` will have
2011-08-25 04:36:48 +00:00
the properties `type` (string), `criticality` (boolean), and optionally, a
string `value` .
2011-08-23 23:25:07 +00:00
All request objects will have a `connection` object, which is the `net.Socket`
associated to this request. Off the `connection` object is an `ldap` object.
2011-08-25 04:36:48 +00:00
The most important property to pay attention to is the `bindDN` property
2011-08-23 23:25:07 +00:00
which will be an instance of an `ldap.DN` object. This is what the client
authenticated as on this connection. If the client didn't bind, then a DN object
will be there defaulted to `cn=anonymous` .
Additionally, request will have a `logId` parameter you can use to uniquely
2023-02-21 18:28:58 +00:00
identify the request/connection pair in logs (includes the LDAP messageId).
2011-08-23 23:25:07 +00:00
## Common Response Elements
All response objects will have an `end` method on them. By default, calling
`res.end()` with no arguments will return SUCCESS (0x00) to the client
2015-10-16 05:25:49 +00:00
(with the exception of `compare` which will return COMPARE\_TRUE (0x06)). You
2011-08-23 23:25:07 +00:00
can pass in a status code to the `end()` method to return an alternate status
code.
However, it's more common/easier to use the `return next(new LDAPError())`
pattern, since ldapjs will fill in the extra LDAPResult fields like matchedDN
and error message for you.
## Errors
ldapjs includes an exception hierarchy that directly corresponds to the RFC list
2015-10-16 05:25:49 +00:00
of error codes. The complete list is documented in [errors ](errors.html ). But
the paradigm is something defined like CONSTRAINT\_VIOLATION in the RFC would be
2011-08-23 23:25:07 +00:00
`ConstraintViolationError` in ldapjs. Upon calling `next(new LDAPError())` ,
ldapjs will _stop_ calling your handler chain. For example:
2021-02-24 22:03:35 +00:00
```js
server.search('o=example',
(req, res, next) => { return next(); },
(req, res, next) => { return next(new ldap.OperationsError()); },
(req, res, next) => { res.end(); }
);
```
2011-08-23 23:25:07 +00:00
In the code snipped above, the third handler would never get invoked.
# Bind
Adds a mount in the tree to perform LDAP binds with. Example:
2021-02-24 22:03:35 +00:00
```js
server.bind('ou=people, o=example', (req, res, next) => {
console.log('bind DN: ' + req.dn.toString());
console.log('bind PW: ' + req.credentials);
res.end();
});
```
2011-08-23 23:25:07 +00:00
## BindRequest
BindRequest objects have the following properties:
### version
The LDAP protocol version the client is requesting to run this connection on.
Note that ldapjs only supports LDAP version 3.
### name
The DN the client is attempting to bind as (note this is the same as the `dn`
property).
### authentication
The method of authentication. Right now only `simple` is supported.
### credentials
The credentials to go with the `name/authentication` pair. For `simple` , this
will be the plain-text password.
## BindResponse
No extra methods above an `LDAPResult` API call.
# Add
2011-08-25 04:36:48 +00:00
Adds a mount in the tree to perform LDAP adds with.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.add('ou=people, o=example', (req, res, next) => {
console.log('DN: ' + req.dn.toString());
console.log('Entry attributes: ' + req.toObject().attributes);
res.end();
});
```
2011-08-23 23:25:07 +00:00
## AddRequest
AddRequest objects have the following properties:
### entry
2011-08-25 04:36:48 +00:00
The DN the client is attempting to add (this is the same as the `dn`
2011-08-23 23:25:07 +00:00
property).
### attributes
2011-08-25 04:36:48 +00:00
The set of attributes in this entry. This will be an array of
2011-08-23 23:25:07 +00:00
`Attribute` objects (which have a type and an array of values). This directly
maps to how the request came in off the wire. It's likely you'll want to use
`toObject()` and iterate that way, since that will transform an AddRequest into
a standard JavaScript object.
### toObject()
This operation will return a plain JavaScript object from the request that looks
like:
2021-02-24 22:03:35 +00:00
```js
{
dn: 'cn=foo, o=example', // string, not DN object
attributes: {
cn: ['foo'],
sn: ['bar'],
objectclass: ['person', 'top']
}
}
```
2011-08-23 23:25:07 +00:00
## AddResponse
No extra methods above an `LDAPResult` API call.
# Search
2011-08-25 04:36:48 +00:00
Adds a handler for the LDAP search operation.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.search('o=example', (req, res, next) => {
console.log('base object: ' + req.dn.toString());
console.log('scope: ' + req.scope);
console.log('filter: ' + req.filter.toString());
res.end();
});
```
2011-08-23 23:25:07 +00:00
## SearchRequest
SearchRequest objects have the following properties:
### baseObject
The DN the client is attempting to start the search at (equivalent to `dn` ).
### scope
(string) one of:
* base
* one
* sub
### derefAliases
2011-08-25 04:36:48 +00:00
An integer (defined in the LDAP protocol). Defaults to '0' (meaning
2011-08-23 23:25:07 +00:00
never deref).
### sizeLimit
The number of entries to return. Defaults to '0' (unlimited). ldapjs doesn't
currently automatically enforce this, but probably will at some point.
### timeLimit
Maximum amount of time the server should take in sending search entries.
Defaults to '0' (unlimited).
### typesOnly
Whether to return only the names of attributes, and not the values. Defaults to
2011-08-25 04:36:48 +00:00
'false'. ldapjs will take care of this for you.
2011-08-23 23:25:07 +00:00
### filter
2015-10-16 05:25:49 +00:00
The [filter ](filters.html ) object that the client requested. Notably this has
2011-08-25 04:36:48 +00:00
a `matches()` method on it that you can leverage. For an example of
introspecting a filter, take a look at the ldapjs-riak source.
2011-08-23 23:25:07 +00:00
### attributes
An optional list of attributes to restrict the returned result sets to. ldapjs
will automatically handle this for you.
## SearchResponse
### send(entry)
2011-08-25 04:36:48 +00:00
Allows you to send a `SearchEntry` object. You do not need to
2011-08-23 23:25:07 +00:00
explicitly pass in a `SearchEntry` object, and can instead just send a plain
JavaScript object that matches the format used from `AddRequest.toObject()` .
2011-08-25 04:36:48 +00:00
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.search('o=example', (req, res, next) => {
const obj = {
dn: 'o=example',
attributes: {
objectclass: ['top', 'organization'],
o: ['example']
}
};
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
if (req.filter.matches(obj))
res.send(obj)
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
res.end();
});
```
2011-08-23 23:25:07 +00:00
# modify
2011-08-25 04:36:48 +00:00
Allows you to handle an LDAP modify operation.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.modify('o=example', (req, res, next) => {
console.log('DN: ' + req.dn.toString());
console.log('changes:');
for (const c of req.changes) {
console.log(' operation: ' + c.operation);
console.log(' modification: ' + c.modification.toString());
}
res.end();
});
```
2011-08-23 23:25:07 +00:00
## ModifyRequest
ModifyRequest objects have the following properties:
### object
2011-08-25 04:36:48 +00:00
The DN the client is attempting to update (this is the same as the `dn`
2011-08-23 23:25:07 +00:00
property).
### changes
An array of `Change` objects the client is attempting to perform. See below for
details on the `Change` object.
## Change
2011-08-25 04:36:48 +00:00
The `Change` object will have the following properties:
2011-08-23 23:25:07 +00:00
### operation
2011-08-25 04:36:48 +00:00
A string, and will be one of: 'add', 'delete', or 'replace'.
2011-08-23 23:25:07 +00:00
### modification
Will be an `Attribute` object, which will have a 'type' (string) field, and
'vals', which will be an array of string values.
## ModifyResponse
No extra methods above an `LDAPResult` API call.
# del
2011-08-25 04:36:48 +00:00
Allows you to handle an LDAP delete operation.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.del('o=example', (req, res, next) => {
console.log('DN: ' + req.dn.toString());
res.end();
});
```
2011-08-23 23:25:07 +00:00
## DeleteRequest
### entry
2011-08-25 04:36:48 +00:00
The DN the client is attempting to delete (this is the same as the `dn`
2011-08-23 23:25:07 +00:00
property).
## DeleteResponse
No extra methods above an `LDAPResult` API call.
# compare
2011-08-25 04:36:48 +00:00
Allows you to handle an LDAP compare operation.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.compare('o=example', (req, res, next) => {
console.log('DN: ' + req.dn.toString());
console.log('attribute name: ' + req.attribute);
console.log('attribute value: ' + req.value);
res.end(req.value === 'foo');
});
```
2011-08-23 23:25:07 +00:00
## CompareRequest
### entry
2011-08-25 04:36:48 +00:00
The DN the client is attempting to compare (this is the same as the `dn`
2011-08-23 23:25:07 +00:00
property).
### attribute
2011-08-25 04:36:48 +00:00
The string name of the attribute to compare values of.
2011-08-23 23:25:07 +00:00
### value
The string value of the attribute to compare.
## CompareResponse
The `end()` method for compare takes a boolean, as opposed to a numeric code
2011-08-25 04:36:48 +00:00
(you can still pass in a numeric LDAP status code if you want). Beyond
2011-08-23 23:25:07 +00:00
that, there are no extra methods above an `LDAPResult` API call.
# modifyDN
2011-08-25 04:36:48 +00:00
Allows you to handle an LDAP modifyDN operation.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.modifyDN('o=example', (req, res, next) => {
console.log('DN: ' + req.dn.toString());
console.log('new RDN: ' + req.newRdn.toString());
console.log('deleteOldRDN: ' + req.deleteOldRdn);
console.log('new superior: ' +
(req.newSuperior ? req.newSuperior.toString() : ''));
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
res.end();
});
```
2011-08-23 23:25:07 +00:00
## ModifyDNRequest
### entry
2011-08-25 04:36:48 +00:00
The DN the client is attempting to rename (this is the same as the `dn`
2011-08-23 23:25:07 +00:00
property).
### newRdn
The leaf RDN the client wants to rename this entry to. This will be a DN object.
### deleteOldRdn
2011-08-25 04:36:48 +00:00
Whether or not to delete the old RDN (i.e., rename vs copy). Defaults to 'true'.
2011-08-23 23:25:07 +00:00
### newSuperior
Optional (DN). If the modifyDN operation wishes to relocate the entry in the
2011-08-25 04:36:48 +00:00
tree, the `newSuperior` field will contain the new parent.
2011-08-23 23:25:07 +00:00
## ModifyDNResponse
No extra methods above an `LDAPResult` API call.
# exop
Allows you to handle an LDAP extended operation. Extended operations are pretty
much arbitrary extensions, by definition. Typically the extended 'name' is an
2011-08-25 04:36:48 +00:00
OID, but ldapjs makes no such restrictions; it just needs to be a string.
Unlike the other operations, extended operations don't map to any location in
the tree, so routing here will be exact match, as opposed to subtree.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
// LDAP whoami
server.exop('1.3.6.1.4.1.4203.1.11.3', (req, res, next) => {
console.log('name: ' + req.name);
console.log('value: ' + req.value);
res.value = 'u:xxyyz@EXAMPLE.NET';
res.end();
return next();
});
```
2011-08-23 23:25:07 +00:00
## ExtendedRequest
### name
2011-08-25 04:36:48 +00:00
Will always be a match to the route-defined name. Clients must include this
in their requests.
2011-08-23 23:25:07 +00:00
### value
Optional string. The arbitrary blob the client sends for this extended
operation.
## ExtendedResponse
### name
The name of the extended operation. ldapjs will automatically set this.
### value
The arbitrary (string) value to send back as part of the response.
# unbind
ldapjs by default provides an unbind handler that just disconnects the client
and cleans up any internals (in ldapjs core). You can override this handler
2011-08-25 04:36:48 +00:00
if you need to clean up any items in your backend, or perform any other cleanup
tasks you need to.
2011-08-23 23:25:07 +00:00
2021-02-24 22:03:35 +00:00
```js
server.unbind((req, res, next) => {
res.end();
});
```
2011-08-23 23:25:07 +00:00
Note that the LDAP unbind operation actually doesn't send any response (by
definition in the RFC), so the UnbindResponse is really just a stub that
ultimately calls `net.Socket.end()` for you. There are no properties available
2011-08-25 04:36:48 +00:00
on either the request or response objects, except, of course, for `end()` on the
2011-08-23 23:25:07 +00:00
response.