Browse Source

[PM-13007] Replace ldapjs with ldapts (#641)

pull/642/head
Thomas Rittson 1 year ago committed by GitHub
parent
commit
91dfd7e0b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 164
      package-lock.json
  2. 3
      package.json
  3. 326
      src/services/ldap-directory.service.ts

164
package-lock.json generated

@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
"https-proxy-agent": "7.0.5",
"inquirer": "8.2.6",
"keytar": "7.9.0",
"ldapjs": "2.3.3",
"ldapts": "7.2.1",
"lowdb": "1.0.0",
"ngx-toastr": "17.0.2",
"node-fetch": "2.7.0",
@ -53,7 +53,6 @@ @@ -53,7 +53,6 @@
"@ngtools/webpack": "17.3.10",
"@types/inquirer": "8.2.10",
"@types/jest": "29.5.13",
"@types/ldapjs": "2.2.5",
"@types/lowdb": "1.0.15",
"@types/node": "20.16.10",
"@types/node-fetch": "2.6.11",
@ -5124,6 +5123,15 @@ @@ -5124,6 +5123,15 @@
"node": ">= 10"
}
},
"node_modules/@types/asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -5427,16 +5435,6 @@ @@ -5427,16 +5435,6 @@
"@types/node": "*"
}
},
"node_modules/@types/ldapjs": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz",
"integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz",
@ -5479,7 +5477,6 @@ @@ -5479,7 +5477,6 @@
"version": "20.16.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz",
"integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@ -6486,12 +6483,6 @@ @@ -6486,12 +6483,6 @@
"node": ">=6.5"
}
},
"node_modules/abstract-logging": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
"license": "MIT"
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -7163,7 +7154,9 @@ @@ -7163,7 +7154,9 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.8"
}
@ -7490,18 +7483,6 @@ @@ -7490,18 +7483,6 @@
"@babel/core": "^7.0.0"
}
},
"node_modules/backoff": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
"license": "MIT",
"dependencies": {
"precond": "0.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -8999,6 +8980,7 @@ @@ -8999,6 +8980,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true,
"license": "MIT"
},
"node_modules/cosmiconfig": {
@ -11553,10 +11535,12 @@ @@ -11553,10 +11535,12 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
"dev": true,
"engines": [
"node >=0.6.0"
],
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
@ -15166,36 +15150,68 @@ @@ -15166,36 +15150,68 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/ldap-filter": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
"node_modules/ldapts": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/ldapts/-/ldapts-7.2.1.tgz",
"integrity": "sha512-2NSA9drjHdRiApF+TO18c+Hy/uyBLs96OS6Gia4+dPQWPxvqDbu3Ji2beCbNCXTvvgxDj4cLZ0WoOZLt5ojfAg==",
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0"
"@types/asn1": ">=0.2.4",
"asn1": "~0.2.6",
"debug": "~4.3.7",
"strict-event-emitter-types": "~2.0.0",
"uuid": "~10.0.0",
"whatwg-url": "~14.0.0"
},
"engines": {
"node": ">=0.8"
"node": ">=18"
}
},
"node_modules/ldapjs": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
"node_modules/ldapts/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/ldapts/node_modules/tr46": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
"license": "MIT",
"dependencies": {
"abstract-logging": "^2.0.0",
"asn1": "^0.2.4",
"assert-plus": "^1.0.0",
"backoff": "^2.5.0",
"ldap-filter": "^0.3.3",
"once": "^1.4.0",
"vasync": "^2.2.0",
"verror": "^1.8.1"
"punycode": "^2.3.1"
},
"engines": {
"node": ">=10.13.0"
"node": ">=18"
}
},
"node_modules/ldapts/node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/ldapts/node_modules/whatwg-url": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
"integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
"license": "MIT",
"dependencies": {
"tr46": "^5.0.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/less": {
@ -18275,14 +18291,6 @@ @@ -18275,14 +18291,6 @@
"node": ">=10"
}
},
"node_modules/precond": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -20327,6 +20335,12 @@ @@ -20327,6 +20335,12 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/strict-event-emitter-types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==",
"license": "ISC"
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -21480,7 +21494,6 @@ @@ -21480,7 +21494,6 @@
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": {
@ -21725,37 +21738,13 @@ @@ -21725,37 +21738,13 @@
"node": ">= 0.8"
}
},
"node_modules/vasync": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"dependencies": {
"verror": "1.10.0"
}
},
"node_modules/vasync/node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"node_modules/verror": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
@ -22311,7 +22300,6 @@ @@ -22311,7 +22300,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"

3
package.json

@ -84,7 +84,6 @@ @@ -84,7 +84,6 @@
"@ngtools/webpack": "17.3.10",
"@types/inquirer": "8.2.10",
"@types/jest": "29.5.13",
"@types/ldapjs": "2.2.5",
"@types/lowdb": "1.0.15",
"@types/node": "20.16.10",
"@types/node-fetch": "2.6.11",
@ -164,7 +163,7 @@ @@ -164,7 +163,7 @@
"https-proxy-agent": "7.0.5",
"inquirer": "8.2.6",
"keytar": "7.9.0",
"ldapjs": "2.3.3",
"ldapts": "7.2.1",
"lowdb": "1.0.0",
"ngx-toastr": "17.0.2",
"node-fetch": "2.7.0",

326
src/services/ldap-directory.service.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import * as fs from "fs";
import { checkServerIdentity, PeerCertificate } from "tls";
import * as tls from "tls";
import * as ldap from "ldapjs";
import * as ldapts from "ldapts";
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService } from "@/jslib/common/src/abstractions/log.service";
@ -19,7 +19,7 @@ import { IDirectoryService } from "./directory.service"; @@ -19,7 +19,7 @@ import { IDirectoryService } from "./directory.service";
const UserControlAccountDisabled = 2;
export class LdapDirectoryService implements IDirectoryService {
private client: ldap.Client;
private client: ldapts.Client;
private dirConfig: LdapConfiguration;
private syncConfig: SyncConfiguration;
@ -48,21 +48,25 @@ export class LdapDirectoryService implements IDirectoryService { @@ -48,21 +48,25 @@ export class LdapDirectoryService implements IDirectoryService {
await this.bind();
let users: UserEntry[];
if (this.syncConfig.users) {
users = await this.getUsers(force, test);
}
let groups: GroupEntry[];
if (this.syncConfig.groups) {
let groupForce = force;
if (!groupForce && users != null) {
const activeUsers = users.filter((u) => !u.deleted && !u.disabled);
groupForce = activeUsers.length > 0;
try {
if (this.syncConfig.users) {
users = await this.getUsers(force, test);
}
if (this.syncConfig.groups) {
let groupForce = force;
if (!groupForce && users != null) {
const activeUsers = users.filter((u) => !u.deleted && !u.disabled);
groupForce = activeUsers.length > 0;
}
groups = await this.getGroups(groupForce);
}
groups = await this.getGroups(groupForce);
} finally {
await this.client.unbind();
}
await this.unbind();
return [groups, users];
}
@ -101,10 +105,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -101,10 +105,7 @@ export class LdapDirectoryService implements IDirectoryService {
const deletedPath = this.makeSearchPath("CN=Deleted Objects");
this.logService.info("Deleted user search: " + deletedPath + " => " + deletedFilter);
const delControl = new (ldap as any).Control({
type: "1.2.840.113556.1.4.417",
criticality: true,
});
const delControl = new ldapts.Control("1.2.840.113556.1.4.417", { critical: true });
const deletedUsers = await this.search<UserEntry>(
deletedPath,
deletedFilter,
@ -120,7 +121,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -120,7 +121,7 @@ export class LdapDirectoryService implements IDirectoryService {
private buildUser(searchEntry: any, deleted: boolean): UserEntry {
const user = new UserEntry();
user.referenceId = searchEntry.objectName;
user.referenceId = this.getReferenceId(searchEntry);
user.deleted = deleted;
if (user.referenceId == null) {
@ -172,7 +173,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -172,7 +173,7 @@ export class LdapDirectoryService implements IDirectoryService {
let groupSearchEntries: any[] = [];
const initialSearchGroupIds = await this.search<string>(path, filter, (se: any) => {
groupSearchEntries.push(se);
return se.objectName;
return this.getReferenceId(se);
});
if (searchSinceRevision && initialSearchGroupIds.length === 0) {
@ -188,7 +189,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -188,7 +189,7 @@ export class LdapDirectoryService implements IDirectoryService {
const userPath = this.makeSearchPath(this.syncConfig.userPath);
const userIdMap = new Map<string, string>();
await this.search<string>(userPath, userFilter, (se: any) => {
userIdMap.set(se.objectName, this.getExternalId(se, se.objectName));
userIdMap.set(this.getReferenceId(se), this.getExternalId(se, this.getReferenceId(se)));
return se;
});
@ -204,7 +205,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -204,7 +205,7 @@ export class LdapDirectoryService implements IDirectoryService {
private buildGroup(searchEntry: any, userMap: Map<string, string>) {
const group = new GroupEntry();
group.referenceId = searchEntry.objectName;
group.referenceId = this.getReferenceId(searchEntry);
if (group.referenceId == null) {
return null;
}
@ -220,7 +221,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -220,7 +221,7 @@ export class LdapDirectoryService implements IDirectoryService {
return null;
}
const members = this.getAttrVals(searchEntry, this.syncConfig.memberAttribute);
const members = this.getAttrVals<string>(searchEntry, this.syncConfig.memberAttribute);
if (members != null) {
for (const memDn of members) {
if (userMap.has(memDn) && !group.userMemberExternalIds.has(userMap.get(memDn))) {
@ -234,15 +235,26 @@ export class LdapDirectoryService implements IDirectoryService { @@ -234,15 +235,26 @@ export class LdapDirectoryService implements IDirectoryService {
return group;
}
private getExternalId(searchEntry: any, referenceId: string) {
const attrObj = this.getAttrObj(searchEntry, "objectGUID");
if (attrObj != null && attrObj._vals != null && attrObj._vals.length > 0) {
return this.bufToGuid(attrObj._vals[0]);
/**
* The externalId is the "objectGUID" property if present (a unique identifier used by Active Directory),
* otherwise it falls back to the provided referenceId.
*/
private getExternalId(searchEntry: ldapts.Entry, referenceId: string) {
const attr = this.getAttr<Buffer>(searchEntry, "objectGUID");
if (attr != null) {
return this.bufToGuid(attr);
} else {
return referenceId;
}
}
/**
* Gets the object's reference id (dn)
*/
private getReferenceId(entry: ldapts.Entry): string {
return entry.dn;
}
private buildBaseFilter(objectClass: string, subFilter: string): string {
let filter = this.buildObjectClassFilter(objectClass);
if (subFilter != null && subFilter.trim() !== "") {
@ -281,42 +293,48 @@ export class LdapDirectoryService implements IDirectoryService { @@ -281,42 +293,48 @@ export class LdapDirectoryService implements IDirectoryService {
return null;
}
private getAttrObj(searchEntry: any, attr: string): any {
if (searchEntry == null || searchEntry.attributes == null) {
/**
*/
/**
* Get all values for an ldap attribute
* @param searchEntry The ldap entry
* @param attr An attribute name on the ldap entry
* @returns An array containing all values of the attribute, or null if there are no values
*/
private getAttrVals<T extends string | Buffer>(
searchEntry: ldapts.Entry,
attr: string,
): T[] | null {
if (searchEntry == null || searchEntry[attr] == null) {
return null;
}
const attrs = searchEntry.attributes.filter((a: any) => a.type === attr);
if (
attrs == null ||
attrs.length === 0 ||
attrs[0].vals == null ||
attrs[0].vals.length === 0
) {
return null;
const vals = searchEntry[attr];
if (!Array.isArray(vals)) {
return [vals] as T[];
}
return attrs[0];
return vals as T[];
}
private getAttrVals(searchEntry: any, attr: string): string[] {
const obj = this.getAttrObj(searchEntry, attr);
if (obj == null) {
return null;
}
return obj.vals;
}
private getAttr(searchEntry: any, attr: string): string {
/**
* Get the first value for an ldap attribute
* @param searchEntry The ldap entry
* @param attr An attribute name on the ldap entry
* @returns The first value of the attribute, or null if there is not at least 1 value
*/
private getAttr<T extends string | Buffer>(searchEntry: ldapts.Entry, attr: string): T {
const vals = this.getAttrVals(searchEntry, attr);
if (vals == null) {
if (vals == null || vals.length < 1) {
return null;
}
return vals[0];
return vals[0] as T;
}
private entryDisabled(searchEntry: any): boolean {
const c = this.getAttr(searchEntry, "userAccountControl");
const c = this.getAttr<string>(searchEntry, "userAccountControl");
if (c != null) {
try {
const control = parseInt(c, null);
@ -333,145 +351,103 @@ export class LdapDirectoryService implements IDirectoryService { @@ -333,145 +351,103 @@ export class LdapDirectoryService implements IDirectoryService {
private async search<T>(
path: string,
filter: string,
processEntry: (searchEntry: any) => T,
controls: ldap.Control[] = [],
processEntry: (searchEntry: ldapts.Entry) => T,
controls: ldapts.Control[] = [],
): Promise<T[]> {
const options: ldap.SearchOptions = {
const options: ldapts.SearchOptions = {
filter: filter,
scope: "sub",
paged: this.dirConfig.pagedSearch,
};
const entries: T[] = [];
return new Promise<T[]>((resolve, reject) => {
this.client.search(path, options, controls, (err, res) => {
if (err != null) {
reject(err);
return;
}
res.on("error", (resErr) => {
reject(resErr);
});
res.on("searchEntry", (entry) => {
const e = processEntry(entry);
if (e != null) {
entries.push(e);
}
});
res.on("end", (result) => {
resolve(entries);
});
});
});
const { searchEntries } = await this.client.search(path, options, controls);
return searchEntries.map((e) => processEntry(e)).filter((e) => e != null);
}
private async bind(): Promise<any> {
return new Promise<void>((resolve, reject) => {
if (this.dirConfig.hostname == null || this.dirConfig.port == null) {
reject(this.i18nService.t("dirConfigIncomplete"));
return;
}
const protocol = "ldap" + (this.dirConfig.ssl && !this.dirConfig.startTls ? "s" : "");
const url = protocol + "://" + this.dirConfig.hostname + ":" + this.dirConfig.port;
const options: ldap.ClientOptions = {
url: url.trim().toLowerCase(),
};
if (this.dirConfig.hostname == null || this.dirConfig.port == null) {
throw new Error(this.i18nService.t("dirConfigIncomplete"));
}
const tlsOptions: any = {};
if (this.dirConfig.ssl) {
if (this.dirConfig.sslAllowUnauthorized) {
tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized;
}
if (!this.dirConfig.startTls) {
if (
this.dirConfig.sslCaPath != null &&
this.dirConfig.sslCaPath !== "" &&
fs.existsSync(this.dirConfig.sslCaPath)
) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (
this.dirConfig.sslCertPath != null &&
this.dirConfig.sslCertPath !== "" &&
fs.existsSync(this.dirConfig.sslCertPath)
) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (
this.dirConfig.sslKeyPath != null &&
this.dirConfig.sslKeyPath !== "" &&
fs.existsSync(this.dirConfig.sslKeyPath)
) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
} else {
if (
this.dirConfig.tlsCaPath != null &&
this.dirConfig.tlsCaPath !== "" &&
fs.existsSync(this.dirConfig.tlsCaPath)
) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.tlsCaPath)];
}
}
}
const protocol = this.dirConfig.ssl && !this.dirConfig.startTls ? "ldaps" : "ldap";
tlsOptions.checkServerIdentity = this.checkServerIdentityAltNames;
options.tlsOptions = tlsOptions;
const url = protocol + "://" + this.dirConfig.hostname + ":" + this.dirConfig.port;
const options: ldapts.ClientOptions = {
url: url.trim().toLowerCase(),
};
this.client = ldap.createClient(options);
// If using ldaps, TLS options are given to the client constructor
if (protocol === "ldaps") {
options.tlsOptions = this.buildTlsOptions();
}
const user =
this.dirConfig.username == null || this.dirConfig.username.trim() === ""
? null
: this.dirConfig.username;
const pass =
this.dirConfig.password == null || this.dirConfig.password.trim() === ""
? null
: this.dirConfig.password;
this.client = new ldapts.Client(options);
if (user == null || pass == null) {
reject(this.i18nService.t("usernamePasswordNotConfigured"));
return;
}
const user =
this.dirConfig.username == null || this.dirConfig.username.trim() === ""
? null
: this.dirConfig.username;
const pass =
this.dirConfig.password == null || this.dirConfig.password.trim() === ""
? null
: this.dirConfig.password;
if (this.dirConfig.startTls && this.dirConfig.ssl) {
this.client.starttls(options.tlsOptions, undefined, (err, res) => {
if (err != null) {
reject(err.message);
} else {
this.client.bind(user, pass, (err2) => {
if (err2 != null) {
reject(err2.message);
} else {
resolve();
}
});
}
});
} else {
this.client.bind(user, pass, (err) => {
if (err != null) {
reject(err.message);
} else {
resolve();
}
});
}
});
if (user == null || pass == null) {
throw new Error(this.i18nService.t("usernamePasswordNotConfigured"));
}
// If using StartTLS, TLS options are given to the StartTLS call
if (this.dirConfig.startTls && this.dirConfig.ssl) {
await this.client.startTLS(this.buildTlsOptions());
}
try {
await this.client.bind(user, pass);
} catch {
await this.client.unbind();
}
}
private async unbind(): Promise<void> {
return new Promise((resolve, reject) => {
this.client.unbind((err) => {
if (err != null) {
reject(err);
} else {
resolve();
}
});
});
private buildTlsOptions(): tls.ConnectionOptions {
const tlsOptions: tls.ConnectionOptions = {};
if (this.dirConfig.sslAllowUnauthorized) {
tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized;
}
if (!this.dirConfig.startTls) {
if (
this.dirConfig.sslCaPath != null &&
this.dirConfig.sslCaPath !== "" &&
fs.existsSync(this.dirConfig.sslCaPath)
) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (
this.dirConfig.sslCertPath != null &&
this.dirConfig.sslCertPath !== "" &&
fs.existsSync(this.dirConfig.sslCertPath)
) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (
this.dirConfig.sslKeyPath != null &&
this.dirConfig.sslKeyPath !== "" &&
fs.existsSync(this.dirConfig.sslKeyPath)
) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
} else {
if (
this.dirConfig.tlsCaPath != null &&
this.dirConfig.tlsCaPath !== "" &&
fs.existsSync(this.dirConfig.tlsCaPath)
) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.tlsCaPath)];
}
}
tlsOptions.checkServerIdentity = this.checkServerIdentityAltNames;
return tlsOptions;
}
private bufToGuid(buf: Buffer) {
@ -494,7 +470,7 @@ export class LdapDirectoryService implements IDirectoryService { @@ -494,7 +470,7 @@ export class LdapDirectoryService implements IDirectoryService {
return guid.toLowerCase();
}
private checkServerIdentityAltNames(host: string, cert: PeerCertificate) {
private checkServerIdentityAltNames(host: string, cert: tls.PeerCertificate) {
// Fixes the cert representation when subject is empty and altNames are present
// Required for node versions < 12.14.1 (which could be used for bwdc cli)
// Adapted from: https://github.com/auth0/ad-ldap-connector/commit/1f4dd2be6ed93dda591dd31ed5483a9b452a8d2a
@ -510,6 +486,6 @@ export class LdapDirectoryService implements IDirectoryService { @@ -510,6 +486,6 @@ export class LdapDirectoryService implements IDirectoryService {
};
}
return checkServerIdentity(host, cert);
return tls.checkServerIdentity(host, cert);
}
}

Loading…
Cancel
Save