Doc guide cleanup
This commit is contained in:
parent
cffaab7730
commit
d9b6a07be9
|
@ -378,3 +378,25 @@ a#homelink:hover {
|
|||
top: 0;
|
||||
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
|
||||
brand: spartan
|
||||
markdown2extras: wiki-tables
|
||||
logo-color: green
|
||||
logo-font-family: google:Aldrich, Verdana, sans-serif
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
title: ldapjs
|
||||
brand: spartan
|
||||
markdown2extras: wiki-tables
|
||||
logo-color: green
|
||||
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
|
||||
explains the LDAPv3 text filter representation.
|
||||
|
||||
Basically, ldapjs gives you a distinct object type mapping to each filter that
|
||||
is context-sensitive. However, _all_ filters have a `matches()` api on them, if
|
||||
ldapjs gives you a distinct object type mapping to each filter that is
|
||||
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,
|
||||
since "simple" filters all operate on an attribute/value assertion. The
|
||||
"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
|
||||
`EqualityFilter` objects.
|
||||
|
||||
Note that `parseFilter` will throw if an invalid string is passed in
|
||||
(that is, a syntactically invalid string). All filter objects in th
|
||||
`parseFilter` will throw if an invalid string is passed in (that is, a
|
||||
syntactically invalid string). All filter objects in th
|
||||
|
||||
# EqualityFilter
|
||||
|
||||
|
@ -79,10 +78,10 @@ key matching `attribute` and a value matching `value`.
|
|||
f.matches({cn: 'foo'}); => true
|
||||
f.matches({cn: 'bar'}); => false
|
||||
|
||||
Note that "strict" equality matching is used, and by default everything in
|
||||
ldapjs (and LDAP) is a UTF-8 string. If you want comparison of numbers, or
|
||||
something else, you'll need to use a middleware interceptor that transforms
|
||||
values of objects.
|
||||
Equality matching uses "strict" type JavaScript comparison, and by default
|
||||
everything in ldapjs (and LDAP) is a UTF-8 string. If you want comparison
|
||||
of numbers, or something else, you'll need to use a middleware interceptor
|
||||
that transforms values of objects.
|
||||
|
||||
# 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
|
||||
map to:
|
||||
|
||||
{
|
||||
initial: 'foo',
|
||||
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 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
|
||||
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
|
||||
"real" task.
|
||||
|
||||
# What exactly is LDAP?
|
||||
|
||||
If you haven't already read the [wikipedia](http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
|
||||
entry, LDAP is the "Lightweight Directory Access Protocol". A directory service
|
||||
basically breaks down as follows:
|
||||
If you haven't already read the
|
||||
[wikipedia](http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
|
||||
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.
|
||||
* 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
|
||||
/ \
|
||||
|
@ -40,7 +41,7 @@ It might be helpful to visualize that:
|
|||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
analogy, an _attribute_ would be like a column).
|
||||
|
||||
That's really it. LDAP really then is the protocol for interacting with the
|
||||
directory tree, and it's pretty comprehensively specified for common operations,
|
||||
like add/update/delete and importantly, search. Really, the power of LDAP
|
||||
really comes through the search operations defined in the protocol, which are
|
||||
richer than HTTP query string filtering, but less powerful than full SQL. If it
|
||||
helps, you can think of LDAP as a NoSQL/document store with a well-defined query
|
||||
syntax.
|
||||
That's it. LDAP, then, is the protocol for interacting with the directory tree,
|
||||
and it's comprehensively specified for common operations, like
|
||||
add/update/delete and importantly, search. Really, the power of LDAP comes
|
||||
through the search operations defined in the protocol, which are richer
|
||||
than HTTP query string filtering, but less powerful than full SQL. You can
|
||||
think of LDAP as a NoSQL/document store with a well-defined query syntax.
|
||||
|
||||
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
|
||||
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
|
||||
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,
|
||||
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?
|
||||
|
||||
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
|
||||
about what you need and don't for your use of a directory system. For example,
|
||||
be useful, it's not. On the other hand, there are no forced assumptions about
|
||||
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
|
||||
server implementations support arbitrary "backends" for persistence, but really
|
||||
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
|
||||
day or two.
|
||||
|
||||
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, 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
|
||||
/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
|
||||
/etc/passwd and /etc/group files. Usually sysadmins "go the other way," and
|
||||
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.
|
||||
Oh, and if it is useful to you, then that's gravy.
|
||||
If it is useful to you, then that's gravy.
|
||||
|
||||
## Install
|
||||
|
||||
|
@ -112,9 +112,10 @@ respectively. After that, run:
|
|||
|
||||
$ 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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
and going to use for allowing an (LDAP) admin to do anything. Great, so go
|
||||
ahead and add this code into your file:
|
||||
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. To do so, add
|
||||
this code into your file:
|
||||
|
||||
server.bind('cn=root', function(req, res, next) {
|
||||
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')
|
||||
|
||||
So, 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
|
||||
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
|
||||
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.
|
||||
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
|
||||
|
||||
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
|
||||
`No such object` error, but this time note that it's for the tree
|
||||
`o=myhost`. That means our bind went through, and our search failed,
|
||||
since we haven't yet added a search handler. Just one more small thing to do
|
||||
first.
|
||||
their CLI less annonyingly noisy. This time, we got another `No such object`
|
||||
error, but it's for the tree `o=myhost`. That means our bind went through, and
|
||||
our search failed, since we haven't yet added a search handler. Just one more
|
||||
small thing to do first.
|
||||
|
||||
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
|
||||
|
@ -229,27 +229,26 @@ oriented, so we check that the connection remote user was indeed our `cn=root`
|
|||
|
||||
## Search
|
||||
|
||||
Ok, we said we wanted to allow LDAP operations over /etc/passwd, so let's detour
|
||||
for a moment to explain an /etc/passwd record:
|
||||
We said we wanted to allow LDAP operations over /etc/passwd, so let's detour
|
||||
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
|
||||
|
||||
That maps to:
|
||||
The sample record above maps to:
|
||||
|
||||
||jsmith||user name||
|
||||
||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||
|
||||
||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||
|
||||
||/home/jsmith||the user's home directory||
|
||||
||/bin/sh||the user's shell||
|
||||
||jsmith||user name.||
|
||||
||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.||
|
||||
||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.||
|
||||
||/home/jsmith||the user's home directory.||
|
||||
||/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
|
||||
source file):
|
||||
source file).
|
||||
|
||||
First, let's make a handler that just loads the "user database" for us in a
|
||||
"pre" handler:
|
||||
First, make a handler that just loads the "user database" in a "pre" handler:
|
||||
|
||||
function loadPasswdFile(req, res, next) {
|
||||
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`.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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`,
|
||||
`wildcard`, `present` and a few other esoteric things. Really, `equal`,
|
||||
`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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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'
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
lower case (note the client sent _objectClass_ over the wire).
|
||||
|
||||
After that, we really just delegated off to the _useradd_ command. AFAIK there
|
||||
is not a node.js module that wraps up `getpwent` and friends, otherwise we'd use
|
||||
that.
|
||||
After that, we really just delegated off to the _useradd_ command. As far as I
|
||||
know, there is not a node.js module that wraps up `getpwent` and friends,
|
||||
otherwise we'd use that.
|
||||
|
||||
Now, what's missing? Oh, right, we need to let you set a password. Well, let's
|
||||
support that via the _modify_ command.
|
||||
|
||||
## 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.
|
||||
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,
|
||||
ensuring that they were asking to "replace" the `userPassword` attribute (which
|
||||
is the 'standard' LDAP attribute for passwords, for whatever that's worth; if
|
||||
you think it's easier to use 'password', knock yourself out), and then just
|
||||
delegating to the `chpasswd` command (which lets you change a user's password
|
||||
over stdin). Next, go ahead and create a `passwd.ldif` file:
|
||||
is the 'standard' LDAP attribute for passwords; if you think it's easier to use
|
||||
'password', knock yourself out), and then just delegating to the `chpasswd`
|
||||
command (which lets you change a user's password over stdin). Next, go ahead
|
||||
and create a `passwd.ldif` file:
|
||||
|
||||
dn: cn=ldapjs, ou=users, o=myhost
|
||||
changetype: modify
|
||||
|
@ -582,17 +581,17 @@ over stdin). Next, go ahead and create a `passwd.ldif` file:
|
|||
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
|
||||
|
||||
You should now be able to login to your box as the ldapjs user. Ok, let's get
|
||||
the last "mainline" piece of work out of the way, and delte the user.
|
||||
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 delete the user.
|
||||
|
||||
## Delete
|
||||
|
||||
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) {
|
||||
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"
|
||||
|
||||
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
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
ldapjs is a pure JavaScript, from-scratch framework for implementing
|
||||
[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).
|
||||
|
||||
var server = require('ldapjs').createServer();
|
||||
|
|
|
@ -18,13 +18,13 @@ The code to create a new server looks like:
|
|||
|
||||
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||
|
||||
||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||
|
||||
||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.||
|
||||
||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.||
|
||||
|
||||
## Properties on server
|
||||
## Properties on the server object
|
||||
|
||||
### maxConnections
|
||||
|
||||
|
@ -134,31 +134,30 @@ server up with riak looks like:
|
|||
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
|
||||
backend object, if applicable. 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).
|
||||
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 heterogenous format, so each
|
||||
operation in the rest of the document includes documentation for the
|
||||
request/response objects appropriate to that operation type.
|
||||
Unlike HTTP, LDAP operations do not have a heterogeneous wire format, so each
|
||||
operation requires specific methods/fields on the request/response objects.
|
||||
|
||||
## 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
|
||||
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`.
|
||||
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 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
|
||||
authenticated as on this connection. If the client didn't bind, then a DN object
|
||||
will be there defaulted to `cn=anonymous`.
|
||||
|
@ -233,7 +232,7 @@ No extra methods above an `LDAPResult` API call.
|
|||
|
||||
# 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) {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
|
@ -247,12 +246,12 @@ AddRequest objects have the following properties:
|
|||
|
||||
### 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).
|
||||
|
||||
### 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
|
||||
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
|
||||
|
@ -278,7 +277,7 @@ No extra methods above an `LDAPResult` API call.
|
|||
|
||||
# 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) {
|
||||
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
|
||||
|
||||
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).
|
||||
|
||||
### sizeLimit
|
||||
|
@ -321,13 +320,13 @@ Defaults to '0' (unlimited).
|
|||
### typesOnly
|
||||
|
||||
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
|
||||
|
||||
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 filter, take a look at the ldapjs-riak source.
|
||||
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
|
||||
|
||||
|
@ -338,10 +337,10 @@ will automatically handle this for you.
|
|||
|
||||
### 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
|
||||
JavaScript object that matches the format used from `AddRequest.toObject()`.
|
||||
Example:
|
||||
|
||||
|
||||
server.search('o=example', function(req, res, next) {
|
||||
var obj = {
|
||||
|
@ -360,7 +359,7 @@ Example:
|
|||
|
||||
# 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) {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
|
@ -378,7 +377,7 @@ ModifyRequest objects have the following properties:
|
|||
|
||||
### 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).
|
||||
|
||||
### changes
|
||||
|
@ -388,11 +387,11 @@ details on the `Change` object.
|
|||
|
||||
## Change
|
||||
|
||||
The Change object will have the following properties:
|
||||
The `Change` object will have the following properties:
|
||||
|
||||
### operation
|
||||
|
||||
A string, and will be one of: 'add', 'delete', 'replace'.
|
||||
A string, and will be one of: 'add', 'delete', or 'replace'.
|
||||
|
||||
### modification
|
||||
|
||||
|
@ -405,7 +404,7 @@ No extra methods above an `LDAPResult` API call.
|
|||
|
||||
# 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) {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
|
@ -416,7 +415,7 @@ Allows you to handle an LDAP delete operation. Example:
|
|||
|
||||
### 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).
|
||||
|
||||
## DeleteResponse
|
||||
|
@ -425,7 +424,7 @@ No extra methods above an `LDAPResult` API call.
|
|||
|
||||
# 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) {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
|
@ -438,12 +437,12 @@ Allows you to handle an LDAP compare operation. Example:
|
|||
|
||||
### 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).
|
||||
|
||||
### attribute
|
||||
|
||||
Will be the string name of the attribute to compare values of.
|
||||
The string name of the attribute to compare values of.
|
||||
|
||||
### value
|
||||
|
||||
|
@ -452,12 +451,12 @@ The string value of the attribute to compare.
|
|||
## CompareResponse
|
||||
|
||||
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.
|
||||
|
||||
# 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) {
|
||||
console.log('DN: ' + req.dn.toString());
|
||||
|
@ -473,7 +472,7 @@ Allows you to handle an LDAP modifyDN operation. Example:
|
|||
|
||||
### 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).
|
||||
|
||||
### newRdn
|
||||
|
@ -482,13 +481,12 @@ The leaf RDN the client wants to rename this entry to. This will be a DN object.
|
|||
|
||||
### deleteOldRdn
|
||||
|
||||
boolean. Whether or not to delete the old RDN (i.e., rename vs copy). Defaults
|
||||
to true.
|
||||
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.
|
||||
tree, the `newSuperior` field will contain the new parent.
|
||||
|
||||
## 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
|
||||
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
|
||||
that 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.
|
||||
Example:
|
||||
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.
|
||||
|
||||
// LDAP whoami
|
||||
server.exop('1.3.6.1.4.1.4203.1.11.3', function(req, res, next) {
|
||||
|
@ -516,8 +513,8 @@ Example:
|
|||
|
||||
### name
|
||||
|
||||
String. Will always be a match to the route-defined name. But the client
|
||||
includes this in their requests.
|
||||
Will always be a match to the route-defined name. Clients must include this
|
||||
in their requests.
|
||||
|
||||
### 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
|
||||
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) {
|
||||
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
|
||||
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
|
||||
on either the request or response objects, except, of course, for `end()` on the
|
||||
response.
|
||||
|
|
Loading…
Reference in New Issue