@fujitsusweden/activedirectory-handler

0.5.1 • Public • Published

activedirectory-handler

What is it

A library for fetching data from Active Directory.

Why another LDAP library

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.

How to use it

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 the from option in searches, see getObjects below.
  • schemaConfigBaseDN: Required string. The DN for the Schema root.
  • clientSideTransitiveSearchBaseDN: Optional string, defaults to the value of the domainBaseDN option. Used as the from option in searches performed internally by the clientSideTransitiveSearch workaround.
  • clientSideTransitiveSearchDefault: Optional boolean, defaults to false. Used as the default for the clientSideTransitiveSearch search option.
  • log: Required object. This object must hold the following log functions: debug, info, warn, error and critical. Each log function should be an async function taking arguments data and req.
  • overrideSingleValued: Optional object. This is used to override schema information for what attributes are single- and multi-valued. If exampleAttribute 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.

getObjects

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 the domainBaseDN config option. The base DN to search.
  • where: Optional filter expression, defaults to ["true"]. See 'LDAP filter DSL' below.
  • scope: Optional string, defaults to sub. May be set to base, one or sub.
  • clientSideTransitiveSearch: Optional boolean, defaults to the value of the clientSideTransitiveSearchDefault config option. If set to true, 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, the clientSideTransitiveSearchBaseDN configuration option might be too specific.
  • req: The req object for passing to the log functions. Optional, unless the log functions require it.

LDAP filter DSL

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

getObjectsA

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.

getOneObject

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.

runIntegrationTests

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 to 1. Designates what fraction of the tests to run. To run only general tests not pertaining to any individual object, use 0. To also test a random fraction of individual objects, use a number between 0 and 1. To run all tests, use 1.
  • req: The req object for passing to the log functions. Optional, unless the log functions require it.

Development

Run ./script without arguments for help

Dependencies (4)

Dev Dependencies (3)

Package Sidebar

Install

npm i @fujitsusweden/activedirectory-handler

Weekly Downloads

2

Version

0.5.1

License

MIT

Unpacked Size

87 kB

Total Files

18

Last publish

Collaborators

  • mrhoffmann
  • tobiasolofsson1988
  • raeven
  • axelsvensson