simple-ad
Version:
Basic ldapjs wrapper for reading and writing AD objects. Currently only searching and editing of group members is implemented.
257 lines (256 loc) • 10.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _ActiveDirectory_password;
Object.defineProperty(exports, "__esModule", { value: true });
const ldapjs_1 = __importDefault(require("ldapjs"));
/**
* Creates a new LDAP client
* @class ActiveDirectory
*/
class ActiveDirectory {
constructor(options) {
_ActiveDirectory_password.set(this, void 0);
this.url = options.url;
this.tlsOptions = options.tlsOptions;
this.username = options.username;
__classPrivateFieldSet(this, _ActiveDirectory_password, options.password, "f");
this.clientOptions = options.clientOptions;
}
/**
* Bind to the LDAP Server
*/
bind() {
return new Promise((resolve, reject) => {
this.client = ldapjs_1.default.createClient({
url: this.url,
tlsOptions: this.tlsOptions
});
this.client.on('error', (err) => {
reject(err);
});
this.client.bind(this.username, __classPrivateFieldGet(this, _ActiveDirectory_password, "f"), (err, res) => {
if (err)
return reject(err);
resolve();
});
});
}
/**
* Unbind the connection
*/
unbind() {
return new Promise((resolve, reject) => {
this.client.unbind((err) => {
if (err)
return reject(err);
resolve();
});
});
}
/**
* Perform a LDAP search: http://ldapjs.org/client.html#search
* @param dn
* @param options
*/
search(dn, options) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const x = yield this.bind();
}
catch (e) {
return reject(e);
}
this.client.search(dn, options, (err, res) => {
if (err) {
return reject(err);
}
var entries = [];
res.on('searchEntry', (entry) => {
entries.push(entry);
});
const done = () => __awaiter(this, void 0, void 0, function* () {
try {
yield this.unbind();
}
catch (e) {
return reject(e);
}
if (entries.length === 0)
return resolve([]);
var result = entries.map(function (e) { return e.object; });
resolve(result);
});
res.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
if (err.message === 'Size Limit Exceeded')
return done();
try {
yield this.unbind();
}
catch (e) {
reject(e);
}
reject(err);
}));
res.on('end', done);
});
}));
}
/**
* Find Group objects
* @param dn
* @param attributes ['cn', 'dn', 'member']
*/
findGroup(groupDN, attributes) {
return __awaiter(this, void 0, void 0, function* () {
const options = {
scope: 'sub',
filter: '(&(objectclass=group))',
attributes: attributes
};
const groups = yield this.search(groupDN, options);
// if attribute 'member' is requested, the function always will return an member array
if (attributes.indexOf('member') !== -1) {
for (let i = 0; i < groups.length; i++) {
if (typeof groups[i].member === 'undefined') {
groups[i].member = [];
}
else if (!Array.isArray(groups[i].member)) {
groups[i].member = [groups[i].member];
}
}
}
// If only one group found, retrun the group object not an array
return (groups.length === 1) ? groups[0] : groups;
});
}
/**
* Check if entry is member in group
* @param groupDN
* @param memberDN
*/
isGroupMember(groupDN, memberDN) {
return __awaiter(this, void 0, void 0, function* () {
const options = {
scope: 'sub',
filter: `(&(objectclass=group)(member=${memberDN}))`,
attributes: ['cn']
};
const results = yield this.search(groupDN, options);
return (results.length > 0);
});
}
/**
* Add or delete group members
* @param groupDN
* @param members
* @param operation
*/
modifyGroupMember(groupDN, members, operation) {
return __awaiter(this, void 0, void 0, function* () {
members = (!Array.isArray(members)) ? [members] : members;
const change = new ldapjs_1.default.Change({
operation: operation,
modification: {
member: members
}
});
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
yield this.bind();
}
catch (e) {
return reject(e);
}
this.client.modify(groupDN, change, (err) => __awaiter(this, void 0, void 0, function* () {
try {
yield this.unbind();
}
catch (e) {
reject(e);
}
if (err && err.name !== 'EntryAlreadyExistsError') {
return reject(err);
}
resolve();
}));
}));
});
}
/**
* Delete one group member
* @param groupDN
* @param memberDN
*/
addGroupMember(groupDN, memberDN) {
return __awaiter(this, void 0, void 0, function* () {
return this.modifyGroupMember(groupDN, memberDN, 'add');
});
}
/**
* Remove one group member
* @param groupDN
* @param memberDN
*/
deleteGroupMember(groupDN, memberDN) {
return __awaiter(this, void 0, void 0, function* () {
// Check is is member in group
if (yield this.isGroupMember(groupDN, memberDN)) {
return this.modifyGroupMember(groupDN, memberDN, 'delete');
}
else {
return null;
}
});
}
/**
* Find a user objects
* @param dn
* @param attributes ['cn', 'dn', 'memberOf']
*/
findUser(userDN, attributes) {
return __awaiter(this, void 0, void 0, function* () {
const options = {
scope: 'sub',
filter: '(&(objectclass=user))',
attributes: attributes
};
const user = yield this.search(userDN, options);
// if attribute 'member' is requested, the function always will return an member array
// if (attributes.indexOf('member') !== -1) {
// for (let i = 0; i < groups.length; i++) {
// if(typeof groups[i].member === 'undefined') {
// groups[i].member = []
// } else if(!Array.isArray(groups[i].member)) {
// groups[i].member = [groups[i].member]
// }
// }
// }
// If only one group found, retrun the group object not an array
return (user.length === 1) ? user[0] : user;
});
}
}
exports.default = ActiveDirectory;
_ActiveDirectory_password = new WeakMap();