Doc guide cleanup
This commit is contained in:
parent
cffaab7730
commit
d9b6a07be9
|
@ -378,3 +378,25 @@ a#homelink:hover {
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
#indextagline {
|
||||||
|
font-size: 24px;
|
||||||
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
a#indextaglink:link {
|
||||||
|
color: #008000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a#indextaglink:visited {
|
||||||
|
color: #008000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a#indextaglink:active {
|
||||||
|
color: #008000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a#indextaglink:hover {
|
||||||
|
color: #008000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
title: ldapjs
|
title: ldapjs
|
||||||
brand: spartan
|
|
||||||
markdown2extras: wiki-tables
|
markdown2extras: wiki-tables
|
||||||
logo-color: green
|
logo-color: green
|
||||||
logo-font-family: google:Aldrich, Verdana, sans-serif
|
logo-font-family: google:Aldrich, Verdana, sans-serif
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
title: ldapjs
|
title: ldapjs
|
||||||
brand: spartan
|
|
||||||
markdown2extras: wiki-tables
|
markdown2extras: wiki-tables
|
||||||
logo-color: green
|
logo-color: green
|
||||||
logo-font-family: google:Aldrich, Verdana, sans-serif
|
logo-font-family: google:Aldrich, Verdana, sans-serif
|
||||||
|
@ -18,8 +17,8 @@ lets you introspect them if you want to write a "query planner". For reference,
|
||||||
make sure to read over [RFC2254](http://www.ietf.org/rfc/rfc2254.txt), as this
|
make sure to read over [RFC2254](http://www.ietf.org/rfc/rfc2254.txt), as this
|
||||||
explains the LDAPv3 text filter representation.
|
explains the LDAPv3 text filter representation.
|
||||||
|
|
||||||
Basically, ldapjs gives you a distinct object type mapping to each filter that
|
ldapjs gives you a distinct object type mapping to each filter that is
|
||||||
is context-sensitive. However, _all_ filters have a `matches()` api on them, if
|
context-sensitive. However, _all_ filters have a `matches()` method on them, if
|
||||||
that's all you need. Most filters will have an `attribute` property on them,
|
that's all you need. Most filters will have an `attribute` property on them,
|
||||||
since "simple" filters all operate on an attribute/value assertion. The
|
since "simple" filters all operate on an attribute/value assertion. The
|
||||||
"complex" filters are really aggregations of other filters (i.e. 'and'), and so
|
"complex" filters are really aggregations of other filters (i.e. 'and'), and so
|
||||||
|
@ -57,8 +56,8 @@ Is a "simple" filter, and would just return a `PresenceFilter` object. However,
|
||||||
Would return an `AndFilter`, which would have a `filters` array of two
|
Would return an `AndFilter`, which would have a `filters` array of two
|
||||||
`EqualityFilter` objects.
|
`EqualityFilter` objects.
|
||||||
|
|
||||||
Note that `parseFilter` will throw if an invalid string is passed in
|
`parseFilter` will throw if an invalid string is passed in (that is, a
|
||||||
(that is, a syntactically invalid string). All filter objects in th
|
syntactically invalid string). All filter objects in th
|
||||||
|
|
||||||
# EqualityFilter
|
# EqualityFilter
|
||||||
|
|
||||||
|
@ -79,10 +78,10 @@ key matching `attribute` and a value matching `value`.
|
||||||
f.matches({cn: 'foo'}); => true
|
f.matches({cn: 'foo'}); => true
|
||||||
f.matches({cn: 'bar'}); => false
|
f.matches({cn: 'bar'}); => false
|
||||||
|
|
||||||
Note that "strict" equality matching is used, and by default everything in
|
Equality matching uses "strict" type JavaScript comparison, and by default
|
||||||
ldapjs (and LDAP) is a UTF-8 string. If you want comparison of numbers, or
|
everything in ldapjs (and LDAP) is a UTF-8 string. If you want comparison
|
||||||
something else, you'll need to use a middleware interceptor that transforms
|
of numbers, or something else, you'll need to use a middleware interceptor
|
||||||
values of objects.
|
that transforms values of objects.
|
||||||
|
|
||||||
# PresenceFilter
|
# PresenceFilter
|
||||||
|
|
||||||
|
@ -113,6 +112,7 @@ optional. The `name` property will be `substring`.
|
||||||
|
|
||||||
The string syntax for a presence filter is `(attr=foo*bar*cat*dog)`, which would
|
The string syntax for a presence filter is `(attr=foo*bar*cat*dog)`, which would
|
||||||
map to:
|
map to:
|
||||||
|
|
||||||
{
|
{
|
||||||
initial: 'foo',
|
initial: 'foo',
|
||||||
any: ['bar', 'cat'],
|
any: ['bar', 'cat'],
|
||||||
|
|
166
docs/guide.md
166
docs/guide.md
|
@ -10,26 +10,27 @@ header-font-family: google:Aldrich, Verdana, sans-serif
|
||||||
# This guide
|
# This guide
|
||||||
|
|
||||||
This guide was written assuming that you (1) don't know anything about ldapjs,
|
This guide was written assuming that you (1) don't know anything about ldapjs,
|
||||||
and perhaps more importantly (2) know little if anything about LDAP. If you're
|
and perhaps more importantly (2) know little, if anything about LDAP. If you're
|
||||||
already an LDAP whiz, please don't read this and feel it's condescending. Most
|
already an LDAP whiz, please don't read this and feel it's condescending. Most
|
||||||
people don't know how LDAP works, other than that "it's that thing that has my
|
people don't know how LDAP works, other than that "it's that thing that has my
|
||||||
password".
|
password."
|
||||||
|
|
||||||
By the end of this guide, we'll have a simple LDAP server that accomplishes a
|
By the end of this guide, we'll have a simple LDAP server that accomplishes a
|
||||||
"real" task.
|
"real" task.
|
||||||
|
|
||||||
# What exactly is LDAP?
|
# What exactly is LDAP?
|
||||||
|
|
||||||
If you haven't already read the [wikipedia](http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
|
If you haven't already read the
|
||||||
entry, LDAP is the "Lightweight Directory Access Protocol". A directory service
|
[wikipedia](http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
|
||||||
basically breaks down as follows:
|
entry (which you should go do right now), LDAP is the "Lightweight Directory
|
||||||
|
Access Protocol". A directory service basically breaks down as follows:
|
||||||
|
|
||||||
* A directory is a tree of entries (similar to but different than a FS).
|
* A directory is a tree of entries (similar to but different than an FS).
|
||||||
* Every entry has a unique name in the tree.
|
* Every entry has a unique name in the tree.
|
||||||
* An entry is a set of attributes.
|
* An entry is a set of attributes.
|
||||||
* An attribute is a key/value(s) pairing (multival is natural).
|
* An attribute is a key/value(s) pairing (multivalue is natural).
|
||||||
|
|
||||||
It might be helpful to visualize that:
|
It might be helpful to visualize:
|
||||||
|
|
||||||
o=example
|
o=example
|
||||||
/ \
|
/ \
|
||||||
|
@ -40,7 +41,7 @@ It might be helpful to visualize that:
|
||||||
keyid=foo
|
keyid=foo
|
||||||
|
|
||||||
|
|
||||||
And let's say we wanted to look at the record cn=john in that tree:
|
Let's say we wanted to look at the record cn=john:
|
||||||
|
|
||||||
dn: cn=john, ou=users, o=example
|
dn: cn=john, ou=users, o=example
|
||||||
cn: john
|
cn: john
|
||||||
|
@ -49,7 +50,7 @@ And let's say we wanted to look at the record cn=john in that tree:
|
||||||
email: john.smith@example.com
|
email: john.smith@example.com
|
||||||
objectClass: person
|
objectClass: person
|
||||||
|
|
||||||
Then there's a few things to note:
|
A few things to note:
|
||||||
|
|
||||||
* All names in a directory tree are actually referred to as a _distinguished
|
* All names in a directory tree are actually referred to as a _distinguished
|
||||||
name_, or _dn_ for short. A dn is comprised of attributes that lead to that
|
name_, or _dn_ for short. A dn is comprised of attributes that lead to that
|
||||||
|
@ -62,47 +63,46 @@ traditional ORM.
|
||||||
* An _objectclass_ defines what _attributes_ an entry can have (on the ORM
|
* An _objectclass_ defines what _attributes_ an entry can have (on the ORM
|
||||||
analogy, an _attribute_ would be like a column).
|
analogy, an _attribute_ would be like a column).
|
||||||
|
|
||||||
That's really it. LDAP really then is the protocol for interacting with the
|
That's it. LDAP, then, is the protocol for interacting with the directory tree,
|
||||||
directory tree, and it's pretty comprehensively specified for common operations,
|
and it's comprehensively specified for common operations, like
|
||||||
like add/update/delete and importantly, search. Really, the power of LDAP
|
add/update/delete and importantly, search. Really, the power of LDAP comes
|
||||||
really comes through the search operations defined in the protocol, which are
|
through the search operations defined in the protocol, which are richer
|
||||||
richer than HTTP query string filtering, but less powerful than full SQL. If it
|
than HTTP query string filtering, but less powerful than full SQL. You can
|
||||||
helps, you can think of LDAP as a NoSQL/document store with a well-defined query
|
think of LDAP as a NoSQL/document store with a well-defined query syntax.
|
||||||
syntax.
|
|
||||||
|
|
||||||
So, why isn't LDAP more popular for a lot of applications? Like anything else
|
So, why isn't LDAP more popular for a lot of applications? Like anything else
|
||||||
that has "simple" or "lightweight" in the name, it's not really that
|
that has "simple" or "lightweight" in the name, it's not really that
|
||||||
lightweight, and in particular, almost all of the implementations of LDAP stem
|
lightweight. In particular, almost all of the implementations of LDAP stem
|
||||||
from the original University of Michigan codebase written in 1996. At that
|
from the original University of Michigan codebase written in 1996. At that
|
||||||
time, the original intention of LDAP was to be an IP-accessible gateway to the
|
time, the original intention of LDAP was to be an IP-accessible gateway to the
|
||||||
much more complex X.500 directories, which really means that a lot of that
|
much more complex X.500 directories, which means that a lot of that
|
||||||
baggage has carried through to today. That makes for a high barrier to entry,
|
baggage has carried through to today. That makes for a high barrier to entry,
|
||||||
when really most applications just don't need most of those features.
|
when most applications just don't need most of those features.
|
||||||
|
|
||||||
## How is ldapjs any different?
|
## How is ldapjs any different?
|
||||||
|
|
||||||
Well, on the one hand, since ldapjs has to be 100% wire compatible with LDAP to
|
Well, on the one hand, since ldapjs has to be 100% wire compatible with LDAP to
|
||||||
be useful, it's not, but on the other hand, there are no forced assumptions
|
be useful, it's not. On the other hand, there are no forced assumptions about
|
||||||
about what you need and don't for your use of a directory system. For example,
|
what you need and don't need for your use of a directory system. For example,
|
||||||
want to run with no-schema in OpenLDAP/389DS/et al? Good luck. Most of the
|
want to run with no-schema in OpenLDAP/389DS/et al? Good luck. Most of the
|
||||||
server implementations support arbitrary "backends" for persistence, but really
|
server implementations support arbitrary "backends" for persistence, but really
|
||||||
you'll be using [BDB](http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html).
|
you'll be using [BDB](http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html).
|
||||||
|
|
||||||
Want to run schemaless in ldapjs, or wire it up with some mongoose models? No
|
Want to run schema-less in ldapjs, or wire it up with some mongoose models? No
|
||||||
problem. Want to back it to redis? Should be able to get some basics up in a
|
problem. Want to back it to redis? Should be able to get some basics up in a
|
||||||
day or two.
|
day or two.
|
||||||
|
|
||||||
Basically, the ldapjs philospohy is to deal with the "muck" of LDAP, and then
|
Basically, the ldapjs philospohy is to deal with the "muck" of LDAP, and then
|
||||||
get out of the way so you can just use the "good parts".
|
get out of the way so you can just use the "good parts."
|
||||||
|
|
||||||
# Ok, cool. Learn me some LDAP!
|
# Ok, cool. Learn me some LDAP!
|
||||||
|
|
||||||
Ok, so with the initial fluff out of the way, let's do something crazy to teach
|
With the initial fluff out of the way, let's do something crazy to teach
|
||||||
you some LDAP. Let's put an LDAP server up over the top of your (Linux) host's
|
you some LDAP. Let's put an LDAP server up over the top of your (Linux) host's
|
||||||
/etc/passwd and /etc/group files. Usually sysadmins "go the other way", and
|
/etc/passwd and /etc/group files. Usually sysadmins "go the other way," and
|
||||||
replace /etc/passwd with a PAM module to LDAP, so while this is probably not
|
replace /etc/passwd with a PAM module to LDAP. While this is probably not
|
||||||
a super useful real-world use case, it will teach you some of the basics.
|
a super useful real-world use case, it will teach you some of the basics.
|
||||||
Oh, and if it is useful to you, then that's gravy.
|
If it is useful to you, then that's gravy.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
@ -112,9 +112,10 @@ respectively. After that, run:
|
||||||
|
|
||||||
$ npm install ldapjs
|
$ npm install ldapjs
|
||||||
|
|
||||||
Also, rather than overload you with client-side programming for now, we'll use
|
Rather than overload you with client-side programming for now, we'll use
|
||||||
the OpenLDAP CLI to interact with our server. It's almost certainly already
|
the OpenLDAP CLI to interact with our server. It's almost certainly already
|
||||||
installed on your system, but if not, you can get it from brew/apt/yum/...
|
installed on your system, but if not, you can get it from brew/apt/yum/your
|
||||||
|
package manager here.
|
||||||
|
|
||||||
To get started, open some file, and let's get the library loaded and a server
|
To get started, open some file, and let's get the library loaded and a server
|
||||||
created:
|
created:
|
||||||
|
@ -147,9 +148,9 @@ LDAP clients will initiate a bind anyway (OpenLDAP will), so let's add it in
|
||||||
and get it out of our way.
|
and get it out of our way.
|
||||||
|
|
||||||
What we're going to do is add a "root" user to our LDAP server. This root user
|
What we're going to do is add a "root" user to our LDAP server. This root user
|
||||||
has no correspondance to our Unix root user, it's just something we're making up
|
has no correspondence to our Unix root user, it's just something we're making up
|
||||||
and going to use for allowing an (LDAP) admin to do anything. Great, so go
|
and going to use for allowing an (LDAP) admin to do anything. To do so, add
|
||||||
ahead and add this code into your file:
|
this code into your file:
|
||||||
|
|
||||||
server.bind('cn=root', function(req, res, next) {
|
server.bind('cn=root', function(req, res, next) {
|
||||||
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
|
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
|
||||||
|
@ -168,8 +169,8 @@ On to the meat of the method. What's up with this?
|
||||||
|
|
||||||
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
|
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
|
||||||
|
|
||||||
So, the first part `req.dn.toString() !== 'cn=root'`: you're probably thinking
|
The first part `req.dn.toString() !== 'cn=root'`: you're probably thinking
|
||||||
"wtf?!? does ldapjs allow something other than cn=root into this handler?" Sort
|
"WTF?!? Does ldapjs allow something other than cn=root into this handler?" Sort
|
||||||
of. It allows cn=root *and any children* into that handler. So the entries
|
of. It allows cn=root *and any children* into that handler. So the entries
|
||||||
`cn=root` and `cn=evil, cn=root` would both match and flow into this handler.
|
`cn=root` and `cn=evil, cn=root` would both match and flow into this handler.
|
||||||
Hence that check. The second check `req.credentials` is probably obvious, but
|
Hence that check. The second check `req.credentials` is probably obvious, but
|
||||||
|
@ -204,11 +205,10 @@ And again with the correct one:
|
||||||
Additional information: No tree found for: o=myhost
|
Additional information: No tree found for: o=myhost
|
||||||
|
|
||||||
Don't worry about all the flags we're passing into OpenLDAP, that's just to make
|
Don't worry about all the flags we're passing into OpenLDAP, that's just to make
|
||||||
their CLI less annonyingly noisy. Note that this time, we got another
|
their CLI less annonyingly noisy. This time, we got another `No such object`
|
||||||
`No such object` error, but this time note that it's for the tree
|
error, but it's for the tree `o=myhost`. That means our bind went through, and
|
||||||
`o=myhost`. That means our bind went through, and our search failed,
|
our search failed, since we haven't yet added a search handler. Just one more
|
||||||
since we haven't yet added a search handler. Just one more small thing to do
|
small thing to do first.
|
||||||
first.
|
|
||||||
|
|
||||||
Remember earlier I said there was no authorization rules baked into LDAP? Well,
|
Remember earlier I said there was no authorization rules baked into LDAP? Well,
|
||||||
we added a bind route, so the only user that can authenticate is `cn=root`, but
|
we added a bind route, so the only user that can authenticate is `cn=root`, but
|
||||||
|
@ -229,27 +229,26 @@ oriented, so we check that the connection remote user was indeed our `cn=root`
|
||||||
|
|
||||||
## Search
|
## Search
|
||||||
|
|
||||||
Ok, we said we wanted to allow LDAP operations over /etc/passwd, so let's detour
|
We said we wanted to allow LDAP operations over /etc/passwd, so let's detour
|
||||||
for a moment to explain an /etc/passwd record:
|
for a moment to explain an /etc/passwd record.
|
||||||
|
|
||||||
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8910,(234)555-0044,email:/home/jsmith:/bin/sh
|
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8910,(234)555-0044,email:/home/jsmith:/bin/sh
|
||||||
|
|
||||||
That maps to:
|
The sample record above maps to:
|
||||||
|
|
||||||
||jsmith||user name||
|
||jsmith||user name.||
|
||||||
||x||historically this contained the password hash, but that's usually in /etc/shadow now, so you get an 'x'||
|
||x||historically this contained the password hash, but that's usually in /etc/shadow now, so you get an 'x'.||
|
||||||
||1001||the unix numeric user id||
|
||1001||the unix numeric user id.||
|
||||||
||1000||the unix numeric group id. (primary)||
|
||1000||the unix numeric group id. (primary).||
|
||||||
||'Joe Smith,...'||the "gecos", which is a description, and is usually a comma separated list of contact details||
|
||'Joe Smith,...'||the "gecos," which is a description, and is usually a comma separated list of contact details.||
|
||||||
||/home/jsmith||the user's home directory||
|
||/home/jsmith||the user's home directory.||
|
||||||
||/bin/sh||the user's shell||
|
||/bin/sh||the user's shell.||
|
||||||
|
|
||||||
Great, let's some handlers to parse that and transform it into an LDAP search
|
Let's some handlers to parse that and transform it into an LDAP search
|
||||||
record (note, you'll need to add `var fs = require('fs');` at the top of the
|
record (note, you'll need to add `var fs = require('fs');` at the top of the
|
||||||
source file):
|
source file).
|
||||||
|
|
||||||
First, let's make a handler that just loads the "user database" for us in a
|
First, make a handler that just loads the "user database" in a "pre" handler:
|
||||||
"pre" handler:
|
|
||||||
|
|
||||||
function loadPasswdFile(req, res, next) {
|
function loadPasswdFile(req, res, next) {
|
||||||
fs.readFile('/etc/passwd', 'utf8', function(err, data) {
|
fs.readFile('/etc/passwd', 'utf8', function(err, data) {
|
||||||
|
@ -336,25 +335,25 @@ the tree for entries that might match the search filter, which above is
|
||||||
`cn=root`.
|
`cn=root`.
|
||||||
|
|
||||||
In this little LDAP example, we're mostly throwing out any qualification of the
|
In this little LDAP example, we're mostly throwing out any qualification of the
|
||||||
"tree", since there's not actually a tree in /etc/passwd (we will extend later
|
"tree," since there's not actually a tree in /etc/passwd (we will extend later
|
||||||
with /etc/group). Remember how I said ldapjs gets out of the way and doesn't
|
with /etc/group). Remember how I said ldapjs gets out of the way and doesn't
|
||||||
force anything on you. Here's an example. If we wanted an LDAP server to run
|
force anything on you? Here's an example. If we wanted an LDAP server to run
|
||||||
over the filesystem, we actually would use this, but here, meh.
|
over the filesystem, we actually would use this, but here, meh.
|
||||||
|
|
||||||
Next, "cn=root" is the search 'filter'. LDAP has a rich specification of
|
Next, `cn=root` is the search "filter". LDAP has a rich specification of
|
||||||
filters, where you can specify `and`, `or`, `not`, `>=`, `<=`, `equal`,
|
filters, where you can specify `and`, `or`, `not`, `>=`, `<=`, `equal`,
|
||||||
`wildcard`, `present` and a few other esoteric things. Really, `equal`,
|
`wildcard`, `present` and a few other esoteric things. Really, `equal`,
|
||||||
`wildcard`, `present` and the boolean operators are all you'll likely ever need.
|
`wildcard`, `present` and the boolean operators are all you'll likely ever need.
|
||||||
So, the filter `cn=root` is an 'equality' filter, and says to only return
|
So, the filter `cn=root` is an "equality" filter, and says to only return
|
||||||
entries that have attributes that match that. In the second invocation, we used
|
entries that have attributes that match that. In the second invocation, we used
|
||||||
a 'presence' filter, to say 'return any entries that have an objectclass'
|
a 'presence' filter, to say 'return any entries that have an objectclass'
|
||||||
attribute, which in LDAP parlance is saying "give me everything".
|
attribute, which in LDAP parlance is saying "give me everything."
|
||||||
|
|
||||||
### The code
|
### The code
|
||||||
|
|
||||||
So in the code above, let's ignore the fs and split stuff, since really all we
|
In the code above, let's ignore the fs and split stuff, since really all we
|
||||||
did was read in /etc/passwd line by line. After that, we looked at each record
|
did was read in /etc/passwd line by line. After that, we looked at each record
|
||||||
and made the cheesiest transform ever, which is making up a "search entry". A
|
and made the cheesiest transform ever, which is making up a "search entry." A
|
||||||
search entry _must_ have a DN so the client knows what record it is, and a set
|
search entry _must_ have a DN so the client knows what record it is, and a set
|
||||||
of attributes. So that's why we did this:
|
of attributes. So that's why we did this:
|
||||||
|
|
||||||
|
@ -373,9 +372,9 @@ of attributes. So that's why we did this:
|
||||||
|
|
||||||
Next, we let ldapjs do all the hard work of figuring out LDAP search filters
|
Next, we let ldapjs do all the hard work of figuring out LDAP search filters
|
||||||
for us by calling `req.filter.matches`. If it matched, we return the whole
|
for us by calling `req.filter.matches`. If it matched, we return the whole
|
||||||
record with `res.send`. Note in this little example we're running O(n), so for
|
record with `res.send`. In this little example we're running O(n), so for
|
||||||
something big and/or slow, you'd have to do some work to effectively write a
|
something big and/or slow, you'd have to do some work to effectively write a
|
||||||
query planner (or just not support it...); for some reference code, check out
|
query planner (or just not support it...). For some reference code, check out
|
||||||
`node-ldapjs-riak`, which takes on the fairly difficult task of writing a 'full'
|
`node-ldapjs-riak`, which takes on the fairly difficult task of writing a 'full'
|
||||||
LDAP server over riak.
|
LDAP server over riak.
|
||||||
|
|
||||||
|
@ -468,7 +467,7 @@ Then, you'll need to be root to have this running, so start your server with
|
||||||
shell: /bin/bash
|
shell: /bin/bash
|
||||||
description: Created via ldapadd
|
description: Created via ldapadd
|
||||||
|
|
||||||
Now go ahead and invoke like:
|
Now go ahead and invoke with:
|
||||||
|
|
||||||
$ ldapadd -H ldap://localhost:1389 -x -D cn=root -w secret -f ./user.ldif
|
$ ldapadd -H ldap://localhost:1389 -x -D cn=root -w secret -f ./user.ldif
|
||||||
adding new entry "cn=ldapjs, ou=users, o=myhost"
|
adding new entry "cn=ldapjs, ou=users, o=myhost"
|
||||||
|
@ -499,7 +498,7 @@ As before, here's a breakdown of the code:
|
||||||
if (entry.objectclass.indexOf('unixUser') === -1)
|
if (entry.objectclass.indexOf('unixUser') === -1)
|
||||||
return next(new ldap.ConstraintViolation('entry must be a unixUser'));
|
return next(new ldap.ConstraintViolation('entry must be a unixUser'));
|
||||||
|
|
||||||
Here's a few new things:
|
A few new things:
|
||||||
|
|
||||||
* We mounted this handler at `ou=users, o=myhost`. Why? What if we want to
|
* We mounted this handler at `ou=users, o=myhost`. Why? What if we want to
|
||||||
extend this little project with groups? We probably want those under a
|
extend this little project with groups? We probably want those under a
|
||||||
|
@ -519,16 +518,16 @@ looking at.
|
||||||
one point: attribute names are case-insensitive, so ldapjs converts them all to
|
one point: attribute names are case-insensitive, so ldapjs converts them all to
|
||||||
lower case (note the client sent _objectClass_ over the wire).
|
lower case (note the client sent _objectClass_ over the wire).
|
||||||
|
|
||||||
After that, we really just delegated off to the _useradd_ command. AFAIK there
|
After that, we really just delegated off to the _useradd_ command. As far as I
|
||||||
is not a node.js module that wraps up `getpwent` and friends, otherwise we'd use
|
know, there is not a node.js module that wraps up `getpwent` and friends,
|
||||||
that.
|
otherwise we'd use that.
|
||||||
|
|
||||||
Now, what's missing? Oh, right, we need to let you set a password. Well, let's
|
Now, what's missing? Oh, right, we need to let you set a password. Well, let's
|
||||||
support that via the _modify_ command.
|
support that via the _modify_ command.
|
||||||
|
|
||||||
## Modify
|
## Modify
|
||||||
|
|
||||||
So unlike HTTP "partial" document updates are fully specified as part of the
|
Unlike HTTP, "partial" document updates are fully specified as part of the
|
||||||
RFC, so appending, removing, or replacing a single attribute is pretty natural.
|
RFC, so appending, removing, or replacing a single attribute is pretty natural.
|
||||||
Go ahead and add the following code into your source file:
|
Go ahead and add the following code into your source file:
|
||||||
|
|
||||||
|
@ -571,10 +570,10 @@ Go ahead and add the following code into your source file:
|
||||||
|
|
||||||
Basically, we made sure the remote client was targeting an entry that exists,
|
Basically, we made sure the remote client was targeting an entry that exists,
|
||||||
ensuring that they were asking to "replace" the `userPassword` attribute (which
|
ensuring that they were asking to "replace" the `userPassword` attribute (which
|
||||||
is the 'standard' LDAP attribute for passwords, for whatever that's worth; if
|
is the 'standard' LDAP attribute for passwords; if you think it's easier to use
|
||||||
you think it's easier to use 'password', knock yourself out), and then just
|
'password', knock yourself out), and then just delegating to the `chpasswd`
|
||||||
delegating to the `chpasswd` command (which lets you change a user's password
|
command (which lets you change a user's password over stdin). Next, go ahead
|
||||||
over stdin). Next, go ahead and create a `passwd.ldif` file:
|
and create a `passwd.ldif` file:
|
||||||
|
|
||||||
dn: cn=ldapjs, ou=users, o=myhost
|
dn: cn=ldapjs, ou=users, o=myhost
|
||||||
changetype: modify
|
changetype: modify
|
||||||
|
@ -582,17 +581,17 @@ over stdin). Next, go ahead and create a `passwd.ldif` file:
|
||||||
userPassword: secret
|
userPassword: secret
|
||||||
-
|
-
|
||||||
|
|
||||||
And then run the OpenLDAP CLI like:
|
And then run the OpenLDAP CLI:
|
||||||
|
|
||||||
$ ldapmodify -H ldap://localhost:1389 -x -D cn=root -w secret -f ./passwd.ldif
|
$ ldapmodify -H ldap://localhost:1389 -x -D cn=root -w secret -f ./passwd.ldif
|
||||||
|
|
||||||
You should now be able to login to your box as the ldapjs user. Ok, let's get
|
You should now be able to login to your box as the ldapjs user. Let's get
|
||||||
the last "mainline" piece of work out of the way, and delte the user.
|
the last "mainline" piece of work out of the way, and delete the user.
|
||||||
|
|
||||||
## Delete
|
## Delete
|
||||||
|
|
||||||
Delete is pretty straightforward. The client gives you a dn to delete, and you
|
Delete is pretty straightforward. The client gives you a dn to delete, and you
|
||||||
delete it :). Go ahead and add the following code into your server:
|
delete it :). Add the following code into your server:
|
||||||
|
|
||||||
server.del('ou=users, o=myhost', pre, function(req, res, next) {
|
server.del('ou=users, o=myhost', pre, function(req, res, next) {
|
||||||
if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn])
|
if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn])
|
||||||
|
@ -625,5 +624,16 @@ And then run the following command:
|
||||||
|
|
||||||
$ ldapdelete -H ldap://localhost:1389 -x -D cn=root -w secret "cn=ldapjs, ou=users, o=myhost"
|
$ ldapdelete -H ldap://localhost:1389 -x -D cn=root -w secret "cn=ldapjs, ou=users, o=myhost"
|
||||||
|
|
||||||
This should be pretty much self-explanatory by now :)
|
|
||||||
|
|
||||||
|
# Where to go from here
|
||||||
|
|
||||||
|
The complete source code for this example server is available in
|
||||||
|
[examples](/examples.html). Make sure to read up on the [server](/server.html)
|
||||||
|
and [client](/client.html) APIs. If you're looking for a "drop in" solution,
|
||||||
|
take a look at [ldapjs-riak](https://github.com/mcavage/node-ldapjs-riak).
|
||||||
|
|
||||||
|
[Mozilla](https://wiki.mozilla.org/Mozilla_LDAP_SDK_Programmer%27s_Guide/Understanding_LDAP)
|
||||||
|
still maintains some web pages with LDAP overviews if you look around, if you're
|
||||||
|
looking for more tutorials. After that, you'll need to work your way through
|
||||||
|
the [RFCs](http://tools.ietf.org/html/rfc4510) as you work through the APIs in
|
||||||
|
ldapjs.
|
||||||
|
|
|
@ -6,13 +6,15 @@ logo-font-family: google:Aldrich, Verdana, sans-serif
|
||||||
header-font-family: google:Aldrich, Verdana, sans-serif
|
header-font-family: google:Aldrich, Verdana, sans-serif
|
||||||
---
|
---
|
||||||
|
|
||||||
LDAP for [node.js](http://nodejs.org).
|
<div id="indextagline">
|
||||||
|
Reimagining <a href="http://tools.ietf.org/html/rfc4510" id="indextaglink">LDAP</a> for <a id="indextaglink" href="http://nodejs.org">Node.js</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
ldapjs is a pure JavaScript, from-scratch framework for implementing
|
ldapjs is a pure JavaScript, from-scratch framework for implementing
|
||||||
[LDAP](http://tools.ietf.org/html/rfc4510) clients and servers in
|
[LDAP](http://tools.ietf.org/html/rfc4510) clients and servers in
|
||||||
[node.js](http://nodejs.org). It is intended for developers used to interacting
|
[Node.js](http://nodejs.org). It is intended for developers used to interacting
|
||||||
with HTTP services in node and [express](http://expressjs.com).
|
with HTTP services in node and [express](http://expressjs.com).
|
||||||
|
|
||||||
var server = require('ldapjs').createServer();
|
var server = require('ldapjs').createServer();
|
||||||
|
|
|
@ -18,13 +18,13 @@ The code to create a new server looks like:
|
||||||
|
|
||||||
var server = ldap.createServer();
|
var server = ldap.createServer();
|
||||||
|
|
||||||
Full list of options:
|
The full list of options is:
|
||||||
|
|
||||||
||log4js||You can optionally pass in a log4js instance that the client will get a logger from. You'll need to set the level to `TRACE` To get any output from the client||
|
||log4js||You can optionally pass in a log4js instance the client will use to acquire a logger. You'll need to set the level to `TRACE` to get any output from the client.||
|
||||||
||certificate||A PEM-encoded X.509 certificate; will cause this server to run in TLS mode||
|
||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||
|
||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.||
|
||||||
|
|
||||||
## Properties on server
|
## Properties on the server object
|
||||||
|
|
||||||
### maxConnections
|
### maxConnections
|
||||||
|
|
||||||
|
@ -134,31 +134,30 @@ server up with riak looks like:
|
||||||
backend.add());
|
backend.add());
|
||||||
...
|
...
|
||||||
|
|
||||||
Basically, the first parameter to an ldapjs route is always the point in the
|
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
|
tree to mount the handler chain at. The second argument is _optionally_ a
|
||||||
backend object, if applicable. After that you can pass in an arbitrary
|
backend object. After that you can pass in an arbitrary combination of
|
||||||
combination of functions in the form `f(req, res, next)` or Arrays of functions
|
functions in the form `f(req, res, next)` or arrays of functions of the same
|
||||||
of the same signature (ldapjs will unroll them).
|
signature (ldapjs will unroll them).
|
||||||
|
|
||||||
Unlike HTTP, LDAP operations do not have a heterogenous format, so each
|
Unlike HTTP, LDAP operations do not have a heterogeneous wire format, so each
|
||||||
operation in the rest of the document includes documentation for the
|
operation requires specific methods/fields on the request/response objects.
|
||||||
request/response objects appropriate to that operation type.
|
|
||||||
|
|
||||||
## Common Request Elements
|
## Common Request Elements
|
||||||
|
|
||||||
All request objects has the `dn` getter on it, which is "context-sensitive"
|
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
|
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
|
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
|
name for just about every op. So, ldapjs calls it `dn`. The DN object itself
|
||||||
is documented at [DN](/dn.html).
|
is documented at [DN](/dn.html).
|
||||||
|
|
||||||
All requests have an optional array of `Control` objects. `Control` will have
|
All requests have an optional array of `Control` objects. `Control` will have
|
||||||
the properties `type` (string), `criticality` (boolean), and optionally a string
|
the properties `type` (string), `criticality` (boolean), and optionally, a
|
||||||
`value`.
|
string `value`.
|
||||||
|
|
||||||
All request objects will have a `connection` object, which is the `net.Socket`
|
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.
|
associated to this request. Off the `connection` object is an `ldap` object.
|
||||||
The most important property to pay attention to there is the `bindDN` property
|
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
|
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
|
authenticated as on this connection. If the client didn't bind, then a DN object
|
||||||
will be there defaulted to `cn=anonymous`.
|
will be there defaulted to `cn=anonymous`.
|
||||||
|
@ -233,7 +232,7 @@ No extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
# Add
|
# Add
|
||||||
|
|
||||||
Adds a mount in the tree to perform LDAP adds with. Example:
|
Adds a mount in the tree to perform LDAP adds with.
|
||||||
|
|
||||||
server.add('ou=people, o=example', function(req, res, next) {
|
server.add('ou=people, o=example', function(req, res, next) {
|
||||||
console.log('DN: ' + req.dn.toString());
|
console.log('DN: ' + req.dn.toString());
|
||||||
|
@ -247,12 +246,12 @@ AddRequest objects have the following properties:
|
||||||
|
|
||||||
### entry
|
### entry
|
||||||
|
|
||||||
The DN the client is attempting to add (note this is the same as the `dn`
|
The DN the client is attempting to add (this is the same as the `dn`
|
||||||
property).
|
property).
|
||||||
|
|
||||||
### attributes
|
### attributes
|
||||||
|
|
||||||
The set of attributes in this entry. Note that this will be an array of
|
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
|
`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
|
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
|
`toObject()` and iterate that way, since that will transform an AddRequest into
|
||||||
|
@ -278,7 +277,7 @@ No extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
|
|
||||||
Adds a handler for the LDAP search operation. Example:
|
Adds a handler for the LDAP search operation.
|
||||||
|
|
||||||
server.search('o=example', function(req, res, next) {
|
server.search('o=example', function(req, res, next) {
|
||||||
console.log('base object: ' + req.dn.toString());
|
console.log('base object: ' + req.dn.toString());
|
||||||
|
@ -305,7 +304,7 @@ The DN the client is attempting to start the search at (equivalent to `dn`).
|
||||||
|
|
||||||
### derefAliases
|
### derefAliases
|
||||||
|
|
||||||
Will be an integer (defined in the LDAP protocol). Defaults to '0' (meaning
|
An integer (defined in the LDAP protocol). Defaults to '0' (meaning
|
||||||
never deref).
|
never deref).
|
||||||
|
|
||||||
### sizeLimit
|
### sizeLimit
|
||||||
|
@ -321,13 +320,13 @@ Defaults to '0' (unlimited).
|
||||||
### typesOnly
|
### typesOnly
|
||||||
|
|
||||||
Whether to return only the names of attributes, and not the values. Defaults to
|
Whether to return only the names of attributes, and not the values. Defaults to
|
||||||
false. Note that ldapjs will take care of this for you.
|
'false'. ldapjs will take care of this for you.
|
||||||
|
|
||||||
### filter
|
### filter
|
||||||
|
|
||||||
The [filter](/filters.html) object that the client requested. Notably this has
|
The [filter](/filters.html) object that the client requested. Notably this has
|
||||||
a `matches()` api on it that you can leverage. For an example of introspecting
|
a `matches()` method on it that you can leverage. For an example of
|
||||||
a filter, take a look at the ldapjs-riak source.
|
introspecting a filter, take a look at the ldapjs-riak source.
|
||||||
|
|
||||||
### attributes
|
### attributes
|
||||||
|
|
||||||
|
@ -338,10 +337,10 @@ will automatically handle this for you.
|
||||||
|
|
||||||
### send(entry)
|
### send(entry)
|
||||||
|
|
||||||
Allows you to send a `SearchEntry` object. Note that you do not need to
|
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
|
explicitly pass in a `SearchEntry` object, and can instead just send a plain
|
||||||
JavaScript object that matches the format used from `AddRequest.toObject()`.
|
JavaScript object that matches the format used from `AddRequest.toObject()`.
|
||||||
Example:
|
|
||||||
|
|
||||||
server.search('o=example', function(req, res, next) {
|
server.search('o=example', function(req, res, next) {
|
||||||
var obj = {
|
var obj = {
|
||||||
|
@ -360,7 +359,7 @@ Example:
|
||||||
|
|
||||||
# modify
|
# modify
|
||||||
|
|
||||||
Allows you to handle an LDAP modify operation. Example:
|
Allows you to handle an LDAP modify operation.
|
||||||
|
|
||||||
server.modify('o=example', function(req, res, next) {
|
server.modify('o=example', function(req, res, next) {
|
||||||
console.log('DN: ' + req.dn.toString());
|
console.log('DN: ' + req.dn.toString());
|
||||||
|
@ -378,7 +377,7 @@ ModifyRequest objects have the following properties:
|
||||||
|
|
||||||
### object
|
### object
|
||||||
|
|
||||||
The DN the client is attempting to update (note this is the same as the `dn`
|
The DN the client is attempting to update (this is the same as the `dn`
|
||||||
property).
|
property).
|
||||||
|
|
||||||
### changes
|
### changes
|
||||||
|
@ -388,11 +387,11 @@ details on the `Change` object.
|
||||||
|
|
||||||
## Change
|
## Change
|
||||||
|
|
||||||
The Change object will have the following properties:
|
The `Change` object will have the following properties:
|
||||||
|
|
||||||
### operation
|
### operation
|
||||||
|
|
||||||
A string, and will be one of: 'add', 'delete', 'replace'.
|
A string, and will be one of: 'add', 'delete', or 'replace'.
|
||||||
|
|
||||||
### modification
|
### modification
|
||||||
|
|
||||||
|
@ -405,7 +404,7 @@ No extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
# del
|
# del
|
||||||
|
|
||||||
Allows you to handle an LDAP delete operation. Example:
|
Allows you to handle an LDAP delete operation.
|
||||||
|
|
||||||
server.delete('o=example', function(req, res, next) {
|
server.delete('o=example', function(req, res, next) {
|
||||||
console.log('DN: ' + req.dn.toString());
|
console.log('DN: ' + req.dn.toString());
|
||||||
|
@ -416,7 +415,7 @@ Allows you to handle an LDAP delete operation. Example:
|
||||||
|
|
||||||
### entry
|
### entry
|
||||||
|
|
||||||
The DN the client is attempting to delete (note this is the same as the `dn`
|
The DN the client is attempting to delete (this is the same as the `dn`
|
||||||
property).
|
property).
|
||||||
|
|
||||||
## DeleteResponse
|
## DeleteResponse
|
||||||
|
@ -425,7 +424,7 @@ No extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
# compare
|
# compare
|
||||||
|
|
||||||
Allows you to handle an LDAP compare operation. Example:
|
Allows you to handle an LDAP compare operation.
|
||||||
|
|
||||||
server.compare('o=example', function(req, res, next) {
|
server.compare('o=example', function(req, res, next) {
|
||||||
console.log('DN: ' + req.dn.toString());
|
console.log('DN: ' + req.dn.toString());
|
||||||
|
@ -438,12 +437,12 @@ Allows you to handle an LDAP compare operation. Example:
|
||||||
|
|
||||||
### entry
|
### entry
|
||||||
|
|
||||||
The DN the client is attempting to compare (note this is the same as the `dn`
|
The DN the client is attempting to compare (this is the same as the `dn`
|
||||||
property).
|
property).
|
||||||
|
|
||||||
### attribute
|
### attribute
|
||||||
|
|
||||||
Will be the string name of the attribute to compare values of.
|
The string name of the attribute to compare values of.
|
||||||
|
|
||||||
### value
|
### value
|
||||||
|
|
||||||
|
@ -452,12 +451,12 @@ The string value of the attribute to compare.
|
||||||
## CompareResponse
|
## CompareResponse
|
||||||
|
|
||||||
The `end()` method for compare takes a boolean, as opposed to a numeric code
|
The `end()` method for compare takes a boolean, as opposed to a numeric code
|
||||||
(note you can still pass in a numeric LDAP status code if you want). Beyond
|
(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.
|
that, there are no extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
# modifyDN
|
# modifyDN
|
||||||
|
|
||||||
Allows you to handle an LDAP modifyDN operation. Example:
|
Allows you to handle an LDAP modifyDN operation.
|
||||||
|
|
||||||
server.modifyDN('o=example', function(req, res, next) {
|
server.modifyDN('o=example', function(req, res, next) {
|
||||||
console.log('DN: ' + req.dn.toString());
|
console.log('DN: ' + req.dn.toString());
|
||||||
|
@ -473,7 +472,7 @@ Allows you to handle an LDAP modifyDN operation. Example:
|
||||||
|
|
||||||
### entry
|
### entry
|
||||||
|
|
||||||
The DN the client is attempting to rename (note this is the same as the `dn`
|
The DN the client is attempting to rename (this is the same as the `dn`
|
||||||
property).
|
property).
|
||||||
|
|
||||||
### newRdn
|
### newRdn
|
||||||
|
@ -482,13 +481,12 @@ The leaf RDN the client wants to rename this entry to. This will be a DN object.
|
||||||
|
|
||||||
### deleteOldRdn
|
### deleteOldRdn
|
||||||
|
|
||||||
boolean. Whether or not to delete the old RDN (i.e., rename vs copy). Defaults
|
Whether or not to delete the old RDN (i.e., rename vs copy). Defaults to 'true'.
|
||||||
to true.
|
|
||||||
|
|
||||||
### newSuperior
|
### newSuperior
|
||||||
|
|
||||||
Optional (DN). If the modifyDN operation wishes to relocate the entry in the
|
Optional (DN). If the modifyDN operation wishes to relocate the entry in the
|
||||||
tree, the newSuperior field will contain the new parent.
|
tree, the `newSuperior` field will contain the new parent.
|
||||||
|
|
||||||
## ModifyDNResponse
|
## ModifyDNResponse
|
||||||
|
|
||||||
|
@ -498,10 +496,9 @@ No extra methods above an `LDAPResult` API call.
|
||||||
|
|
||||||
Allows you to handle an LDAP extended operation. Extended operations are pretty
|
Allows you to handle an LDAP extended operation. Extended operations are pretty
|
||||||
much arbitrary extensions, by definition. Typically the extended 'name' is an
|
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. Note
|
OID, but ldapjs makes no such restrictions; it just needs to be a string.
|
||||||
that unlike the other operations, extended operations don't map to any location
|
Unlike the other operations, extended operations don't map to any location in
|
||||||
in the tree, so routing here will be exact match, as opposed to subtree.
|
the tree, so routing here will be exact match, as opposed to subtree.
|
||||||
Example:
|
|
||||||
|
|
||||||
// LDAP whoami
|
// LDAP whoami
|
||||||
server.exop('1.3.6.1.4.1.4203.1.11.3', function(req, res, next) {
|
server.exop('1.3.6.1.4.1.4203.1.11.3', function(req, res, next) {
|
||||||
|
@ -516,8 +513,8 @@ Example:
|
||||||
|
|
||||||
### name
|
### name
|
||||||
|
|
||||||
String. Will always be a match to the route-defined name. But the client
|
Will always be a match to the route-defined name. Clients must include this
|
||||||
includes this in their requests.
|
in their requests.
|
||||||
|
|
||||||
### value
|
### value
|
||||||
|
|
||||||
|
@ -538,7 +535,8 @@ The arbitrary (string) value to send back as part of the response.
|
||||||
|
|
||||||
ldapjs by default provides an unbind handler that just disconnects the client
|
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
|
and cleans up any internals (in ldapjs core). You can override this handler
|
||||||
if you need to clean up any items in your backend, for example.
|
if you need to clean up any items in your backend, or perform any other cleanup
|
||||||
|
tasks you need to.
|
||||||
|
|
||||||
server.unbind(function(req, res, next) {
|
server.unbind(function(req, res, next) {
|
||||||
res.end();
|
res.end();
|
||||||
|
@ -547,5 +545,5 @@ if you need to clean up any items in your backend, for example.
|
||||||
Note that the LDAP unbind operation actually doesn't send any response (by
|
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
|
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
|
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
|
on either the request or response objects, except, of course, for `end()` on the
|
||||||
response.
|
response.
|
||||||
|
|
Loading…
Reference in New Issue