First commit
This commit is contained in:
614
node_modules/ldapjs/docs/server.md
generated
vendored
Normal file
614
node_modules/ldapjs/docs/server.md
generated
vendored
Normal file
@ -0,0 +1,614 @@
|
||||
---
|
||||
title: Server API | ldapjs
|
||||
---
|
||||
|
||||
# ldapjs Server API
|
||||
|
||||
<div class="intro">
|
||||
|
||||
This document covers the ldapjs server API and assumes that you are familiar
|
||||
with LDAP. If you're not, read the [guide](guide.html) first.
|
||||
|
||||
</div>
|
||||
|
||||
# Create a server
|
||||
|
||||
The code to create a new server looks like:
|
||||
|
||||
```js
|
||||
const server = ldap.createServer();
|
||||
```
|
||||
|
||||
The full list of options is:
|
||||
|
||||
||log||You can optionally pass in a Bunyan compatible logger instance the client will use to acquire a child logger.||
|
||||
||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.||
|
||||
|
||||
### 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)
|
||||
|
||||
## Properties on the server object
|
||||
|
||||
### maxConnections
|
||||
|
||||
Set this property to reject connections when the server's connection count gets
|
||||
high.
|
||||
|
||||
### connections (getter only) - DEPRECATED
|
||||
|
||||
The number of concurrent connections on the server. This property is deprecated,
|
||||
please use server.getConnections() instead.
|
||||
|
||||
### 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
|
||||
|
||||
The LDAP server API wraps up and mirrors the node.js `server.listen` family of
|
||||
APIs.
|
||||
|
||||
After calling `listen`, the property `url` on the server object itself will be
|
||||
available.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
server.listen(389, '127.0.0.1', function() {
|
||||
console.log('LDAP server listening at: ' + server.url);
|
||||
});
|
||||
```
|
||||
|
||||
### Port and Host
|
||||
`listen(port, [host], [callback])`
|
||||
|
||||
Begin accepting connections on the specified port and host. If the host is
|
||||
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.
|
||||
|
||||
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)`.
|
||||
|
||||
## 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.
|
||||
|
||||
# Routes
|
||||
|
||||
The LDAP server API is meant to be the LDAP-equivalent of the express/restify
|
||||
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:
|
||||
|
||||
```js
|
||||
function authorize(req, res, next) {
|
||||
if (!req.connection.ldap.bindDN.equals('cn=root'))
|
||||
return next(new ldap.InsufficientAccessRightsError());
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
server.search('o=example', authorize, function(req, res, next) { ... });
|
||||
```
|
||||
|
||||
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
|
||||
[Riak](https://github.com/basho/riak). Getting an LDAP server up with riak
|
||||
looks like:
|
||||
|
||||
```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());
|
||||
...
|
||||
```
|
||||
|
||||
The first parameter to an ldapjs route is always the point in the
|
||||
tree to mount the handler chain at. The second argument is _optionally_ a
|
||||
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).
|
||||
|
||||
Unlike HTTP, LDAP operations do not have a heterogeneous wire format, so each
|
||||
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":
|
||||
|
||||
```js
|
||||
server.use(function(req, res, next) {
|
||||
console.log('hello world');
|
||||
return next();
|
||||
});
|
||||
```
|
||||
|
||||
## Common Request Elements
|
||||
|
||||
All request objects have the `dn` getter on it, which is "context-sensitive"
|
||||
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
|
||||
is documented at [DN](dn.html).
|
||||
|
||||
All requests have an optional array of `Control` objects. `Control` will have
|
||||
the properties `type` (string), `criticality` (boolean), and optionally, a
|
||||
string `value`.
|
||||
|
||||
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.
|
||||
The most important property to pay attention to is the `bindDN` property
|
||||
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
|
||||
identify the request/connection pair in logs (includes the LDAP messageId).
|
||||
|
||||
## 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
|
||||
(with the exception of `compare` which will return COMPARE\_TRUE (0x06)). You
|
||||
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
|
||||
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
|
||||
`ConstraintViolationError` in ldapjs. Upon calling `next(new LDAPError())`,
|
||||
ldapjs will _stop_ calling your handler chain. For example:
|
||||
|
||||
```js
|
||||
server.search('o=example',
|
||||
(req, res, next) => { return next(); },
|
||||
(req, res, next) => { return next(new ldap.OperationsError()); },
|
||||
(req, res, next) => { res.end(); }
|
||||
);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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();
|
||||
});
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
Adds a mount in the tree to perform LDAP adds with.
|
||||
|
||||
```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();
|
||||
});
|
||||
```
|
||||
|
||||
## AddRequest
|
||||
|
||||
AddRequest objects have the following properties:
|
||||
|
||||
### entry
|
||||
|
||||
The DN the client is attempting to add (this is the same as the `dn`
|
||||
property).
|
||||
|
||||
### attributes
|
||||
|
||||
The set of attributes in this entry. This will be an array of
|
||||
`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:
|
||||
|
||||
```js
|
||||
{
|
||||
dn: 'cn=foo, o=example', // string, not DN object
|
||||
attributes: {
|
||||
cn: ['foo'],
|
||||
sn: ['bar'],
|
||||
objectclass: ['person', 'top']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AddResponse
|
||||
|
||||
No extra methods above an `LDAPResult` API call.
|
||||
|
||||
# Search
|
||||
|
||||
Adds a handler for the LDAP search operation.
|
||||
|
||||
```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();
|
||||
});
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
An integer (defined in the LDAP protocol). Defaults to '0' (meaning
|
||||
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
|
||||
'false'. ldapjs will take care of this for you.
|
||||
|
||||
### filter
|
||||
|
||||
The [filter](filters.html) object that the client requested. Notably this has
|
||||
a `matches()` method on it that you can leverage. For an example of
|
||||
introspecting a filter, take a look at the ldapjs-riak source.
|
||||
|
||||
### attributes
|
||||
|
||||
An optional list of attributes to restrict the returned result sets to. ldapjs
|
||||
will automatically handle this for you.
|
||||
|
||||
## SearchResponse
|
||||
|
||||
### send(entry)
|
||||
|
||||
Allows you to send a `SearchEntry` object. You do not need to
|
||||
explicitly pass in a `SearchEntry` object, and can instead just send a plain
|
||||
JavaScript object that matches the format used from `AddRequest.toObject()`.
|
||||
|
||||
|
||||
```js
|
||||
server.search('o=example', (req, res, next) => {
|
||||
const obj = {
|
||||
dn: 'o=example',
|
||||
attributes: {
|
||||
objectclass: ['top', 'organization'],
|
||||
o: ['example']
|
||||
}
|
||||
};
|
||||
|
||||
if (req.filter.matches(obj))
|
||||
res.send(obj)
|
||||
|
||||
res.end();
|
||||
});
|
||||
```
|
||||
|
||||
# modify
|
||||
|
||||
Allows you to handle an LDAP modify operation.
|
||||
|
||||
```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();
|
||||
});
|
||||
```
|
||||
|
||||
## ModifyRequest
|
||||
|
||||
ModifyRequest objects have the following properties:
|
||||
|
||||
### object
|
||||
|
||||
The DN the client is attempting to update (this is the same as the `dn`
|
||||
property).
|
||||
|
||||
### changes
|
||||
|
||||
An array of `Change` objects the client is attempting to perform. See below for
|
||||
details on the `Change` object.
|
||||
|
||||
## Change
|
||||
|
||||
The `Change` object will have the following properties:
|
||||
|
||||
### operation
|
||||
|
||||
A string, and will be one of: 'add', 'delete', or 'replace'.
|
||||
|
||||
### 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
|
||||
|
||||
Allows you to handle an LDAP delete operation.
|
||||
|
||||
```js
|
||||
server.del('o=example', (req, res, next) => {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
res.end();
|
||||
});
|
||||
```
|
||||
|
||||
## DeleteRequest
|
||||
|
||||
### entry
|
||||
|
||||
The DN the client is attempting to delete (this is the same as the `dn`
|
||||
property).
|
||||
|
||||
## DeleteResponse
|
||||
|
||||
No extra methods above an `LDAPResult` API call.
|
||||
|
||||
# compare
|
||||
|
||||
Allows you to handle an LDAP compare operation.
|
||||
|
||||
```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');
|
||||
});
|
||||
```
|
||||
|
||||
## CompareRequest
|
||||
|
||||
### entry
|
||||
|
||||
The DN the client is attempting to compare (this is the same as the `dn`
|
||||
property).
|
||||
|
||||
### attribute
|
||||
|
||||
The string name of the attribute to compare values of.
|
||||
|
||||
### value
|
||||
|
||||
The string value of the attribute to compare.
|
||||
|
||||
## CompareResponse
|
||||
|
||||
The `end()` method for compare takes a boolean, as opposed to a numeric code
|
||||
(you can still pass in a numeric LDAP status code if you want). Beyond
|
||||
that, there are no extra methods above an `LDAPResult` API call.
|
||||
|
||||
# modifyDN
|
||||
|
||||
Allows you to handle an LDAP modifyDN operation.
|
||||
|
||||
```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() : ''));
|
||||
|
||||
res.end();
|
||||
});
|
||||
```
|
||||
|
||||
## ModifyDNRequest
|
||||
|
||||
### entry
|
||||
|
||||
The DN the client is attempting to rename (this is the same as the `dn`
|
||||
property).
|
||||
|
||||
### newRdn
|
||||
|
||||
The leaf RDN the client wants to rename this entry to. This will be a DN object.
|
||||
|
||||
### deleteOldRdn
|
||||
|
||||
Whether or not to delete the old RDN (i.e., rename vs copy). Defaults to 'true'.
|
||||
|
||||
### newSuperior
|
||||
|
||||
Optional (DN). If the modifyDN operation wishes to relocate the entry in the
|
||||
tree, the `newSuperior` field will contain the new parent.
|
||||
|
||||
## 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
|
||||
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.
|
||||
|
||||
```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();
|
||||
});
|
||||
```
|
||||
|
||||
## ExtendedRequest
|
||||
|
||||
### name
|
||||
|
||||
Will always be a match to the route-defined name. Clients must include this
|
||||
in their requests.
|
||||
|
||||
### 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
|
||||
if you need to clean up any items in your backend, or perform any other cleanup
|
||||
tasks you need to.
|
||||
|
||||
```js
|
||||
server.unbind((req, res, next) => {
|
||||
res.end();
|
||||
});
|
||||
```
|
||||
|
||||
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
|
||||
on either the request or response objects, except, of course, for `end()` on the
|
||||
response.
|
||||
Reference in New Issue
Block a user