A library for fetching data from Active Directory.
We couldn't find an existing one that matched our needs. It is implemented as a wrapper around ldapjs. Compared to ldapjs, this package have the following advantages:
- Exposes result set as an asynchronous generator with back-pressure, meaning you can consume the search result using
for await
as slowly as needed and with limited memory use. - Protects against injection into filter expressions by forcing use of a DSL rather than filter strings.
- Handles single- and multi-valued attributes correctly. Values come in an array if and only if the attribute is multi-valued.
- Correctly parses several data types that ldapjs doesn't: Sid, GUID, Bool, Int32, Windows NT time format, GeneralizedTime and OctetString.
- Transparent fetching of all values in an attribute when the server limits the number of values per query.
and the following disadvantages:
- It is strictly for searching. No server functionality, and no client functionality for adding, deleting or modifying data.
- Less performant at first call. This is due to an initialization routine that reads the entire AD schema.
- Probably slightly less performant in general due to being implemented as a wrapper.
- Not generic; only tested with Microsoft domain controllers.
Import and configure it like so:
const ActiveDirectoryHandler =
require("@fujitsusweden/activedirectory-handler");
const adHandler = new ActiveDirectoryHandler({
url: "ldap://your-domain.example.com",
user: "username",
password: "password",
domainBaseDN: "ou=MainOU,dc=your-domain,dc=example,dc=com",
clientSideTransitiveSearchBaseDN: "dc=your-domain,dc=example,dc=com",
schemaConfigBaseDN:
"cn=Schema,cn=Configuration,dc=your-domain,dc=example,dc=com",
log,
overrideSingleValued: {
exampleAttribute: true,
},
});
Details for configuration options:
-
url
: Required string. The LDAP URL to connect to. -
user
: Required string. The username to authenticate with. -
password
: Required string. The password to authenticate with. -
domainBaseDN
: Required string. Used as the default value for thefrom
option in searches, seegetObjects
below. -
schemaConfigBaseDN
: Required string. The DN for the Schema root. -
clientSideTransitiveSearchBaseDN
: Optional string, defaults to the value of thedomainBaseDN
option. Used as thefrom
option in searches performed internally by theclientSideTransitiveSearch
workaround. -
clientSideTransitiveSearchDefault
: Optional boolean, defaults tofalse
. Used as the default for theclientSideTransitiveSearch
search option. -
log
: Required object. This object must hold the following log functions:debug
,info
,warn
,error
andcritical
. Each log function should be an async function taking argumentsdata
andreq
. -
overrideSingleValued
: Optional object. This is used to override schema information for what attributes are single- and multi-valued. IfexampleAttribute
is declared in the AD schema as multi-valued but no entity has more than one such value and you don't want to deal with an array, you can force treating it as single-valued as in the example above.
Performs a search over LDAP and generates the results as an asynchronous generator.
Example:
for await (const user of adHandler.getObjects({
select: ["distinguishedName", "sn"],
from: "ou=Users,ou=MainOU,dc=your-domain,dc=example,dc=com",
where: [
"and",
["equals", "objectCategory", "person"],
["equals", "objectClass", "user"],
],
req,
})) {
console.log(user);
}
Details for options sent to getObjects
:
-
select
: Required, either non-empty array of strings or"*"
. A list of the names of the attributes to fetch, or"*"
for fetching all attributes. -
from
: Optional string, defaults to the value of thedomainBaseDN
config option. The base DN to search. -
where
: Optional filter expression, defaults to["true"]
. See 'LDAP filter DSL' below. -
scope
: Optional string, defaults tosub
. May be set tobase
,one
orsub
. -
clientSideTransitiveSearch
: Optional boolean, defaults to the value of theclientSideTransitiveSearchDefault
config option. If set totrue
, it turns on a workaround for Microsoft-specific performance problems with transitive (a.k.a. in-chain) membership searches. If you use the special attributes_transitive_member
or_transitive_memberOf
in a filter expression and experience performance problems, turn this option on and test thoroughly that you get the same results. If results differ, theclientSideTransitiveSearchBaseDN
configuration option might be too specific. -
req
: The req object for passing to the log functions. Optional, unless the log functions require it.
A filter expression in this DSL/mini-language is made up of strings and arrays. It has the following grammar:
<expression> := <and> | <or> | <not> | <equals> | <beginswith> |
<endswith> | <contains> | <has> | <oneof> | <true> |
<false>
<and> := ["and", <expression>, <expression>, ...]
<or> := ["or", <expression>, <expression>, ...]
<not> := ["not", <expression>]
<equals> := ["equals", <attribute>, <value>]
<beginswith> := ["beginswith", <attribute>, <value>]
<endswith> := ["endswith", <attribute>, <value>]
<contains> := ["contains", <attribute>, <value>]
<has> := ["has", <attribute>]
<oneof> := ["oneof", <attribute>, <arrValue>]
<true> := ["true"]
<false> := ["false"]
<attribute> := A string matching /^[A-Za-z][A-Za-z0-9-]{0,59}$/ i.e.
1-60 English alphanumeric characters or dashes, the
first of which is a letter. It can also be
"_transitive_member" or "_transitive_memberOf" for
transitive (a.k.a. in-chain) membership searches.
<value> := A string matching /^.{1,255}$/ i.e. with a length in
the interval [1, 255].
<arrValue> := An array with zero or more items, each of which must
be a <value>.
The semantics are as follows:
["and", X1, ...]: All expressions are true.
["or", X1, ...]: At least one expression is true.
["not", X]: X is false.
["equals", A, V]: True if the object has an attribute A with a
value that equals V, or a multi-valued
attribute A where at least one of the values
equals V.
["beginswith", A, V]: True if the object has an attribute A with a
value that begins with V, or a multi-valued
attribute A where at least one of the values
begins with V.
["endswith", A, V]: True if the object has an attribute A with a
value that ends with V, or a multi-valued
attribute A where at least one of the values
ends with V.
["contains", A, V]: True if the object has an attribute A with a
value that contains V as a substring, or a
multi-valued attribute A where at least one of
the values contains V as a substring.
["has", A]: True if the object has an attribute A with any
value.
["oneof", A, arrV]: True if the object has an attribute A with a
value that equals at least one of the elements
of arrV, or a multi-valued attribute A where at
least one of the values equals at least one of
the elements of arrV.
["true"]: Always true.
["false"]: Always false.
Note that the expressions beginswith
, endswith
and contains
, cannot be used with DN attributes. See details here
Variant of getObjects
.
An asynchronous function that returns an array.
Use only if you expect a fairly low number of results.
Example:
for (const user of adHandler.getObjectsA({
select: ["distinguishedName", "sn"],
from: "ou=Users,ou=MainOU,dc=your-domain,dc=example,dc=com",
where: [
"and",
["equals", "objectCategory", "person"],
["equals", "objectClass", "user"],
],
req,
})) {
console.log(user);
}
Details for options sent to getObjectsA
are exactly the same as for getObjects
.
Variant of getObjects
.
An asynchronous function that returns one entry if exactly one entry was found, and otherwise throws an error.
Options sent to getOneObject
are exactly the same as for getObjects
.
Run integration tests.
These include testing that your configuration works, that ActiveDirectoryHandler
works with your AD server, and that your AD server fulfills the assumptions made in ActiveDirectoryHandler
.
These tests will download a lot of data and can take hours or even days to complete for large domains.
The info
, warning
and error
log functions will be used to detail possible problems and, if you're lucky, some suggestions.
await adHandler.runIntegrationTests({ fraction, req });
Details for options sent to runIntegrationTests
:
-
fraction
: Optional number, defaults to1
. Designates what fraction of the tests to run. To run only general tests not pertaining to any individual object, use0
. To also test a random fraction of individual objects, use a number between0
and1
. To run all tests, use1
. -
req
: The req object for passing to the log functions. Optional, unless the log functions require it.
Run ./script
without arguments for help