Comparing version 0.2.3 to 0.2.4
@@ -5,2 +5,2 @@ { | ||
} | ||
} | ||
} |
177
index.js
@@ -5,107 +5,107 @@ const activedirectory = require('activedirectory'); | ||
const imports = { | ||
user: require('./src/user'), | ||
group: require('./src/group'), | ||
ou: require('./src/ou'), | ||
others: require('./src/others'), | ||
helpers: require('./src/internal/helpers'), | ||
operations: require('./src/internal/operations'), | ||
chainable: require('./src/chainable'), | ||
} | ||
user: require('./src/user'), | ||
group: require('./src/group'), | ||
ou: require('./src/ou'), | ||
others: require('./src/others'), | ||
helpers: require('./src/internal/helpers'), | ||
operations: require('./src/internal/operations'), | ||
chainable: require('./src/chainable') | ||
}; | ||
class AD { | ||
constructor(config) { | ||
constructor(config) { | ||
if (config === undefined) { | ||
throw new Error('Configuration is required.'); | ||
} | ||
if (config === undefined) { | ||
throw new Error('Configuration is required.'); | ||
} | ||
let invalid = !config.url || !config.user || !config.pass; | ||
let invalid = ( | ||
!config.url || | ||
!config.user || | ||
!config.pass | ||
); | ||
if (invalid) { | ||
throw new Error( | ||
'The following configuration is required: {url, user, pass}.' | ||
); | ||
} | ||
if (invalid) { | ||
throw new Error('The following configuration is required: {url, user, pass}.'); | ||
} | ||
if (String(config.url).indexOf('://') === -1) { | ||
throw new Error( | ||
'You must specify the protocol in the url, such as ldaps://127.0.0.1.' | ||
); | ||
} | ||
if (String(config.url).indexOf('://') === -1) { | ||
throw new Error('You must specify the protocol in the url, such as ldaps://127.0.0.1.'); | ||
} | ||
if (String(config.user).indexOf('@') === -1) { | ||
throw new Error( | ||
'The user must include the fully qualified domain name, such as joe@acme.co.' | ||
); | ||
} | ||
if (String(config.user).indexOf('@') === -1) { | ||
throw new Error('The user must include the fully qualified domain name, such as joe@acme.co.'); | ||
} | ||
config.domain = String(config.user).split('@')[1]; | ||
config.domain = String(config.user).split('@')[1]; | ||
if (config.baseDN === undefined) { | ||
config.baseDN = config.domain.split('.').map(n => `DC=${n}`).join(','); | ||
} | ||
if (config.baseDN === undefined) { | ||
config.baseDN = config.domain.split('.').map(n => `DC=${n}`).join(','); | ||
} | ||
config = Object.assign(configFile, config); | ||
config = Object.assign(configFile, config); | ||
this.config = config; | ||
this.config = config; | ||
this._cache = { | ||
enabled: true, | ||
expiration: 600000, | ||
users: {}, | ||
groups: {}, | ||
ous: {}, | ||
all: {}, | ||
get: (type, key) => { | ||
if (!this._cache.enabled) { | ||
return undefined; | ||
} | ||
if (!this._cache[type] || !this._cache[type][key]) { | ||
return undefined; | ||
} | ||
let obj = this._cache[type][key]; | ||
let diff = new Date() - obj.timestamp; | ||
if (diff > this._cache.expiration) { | ||
delete this._cache[type][key]; | ||
return undefined; | ||
} | ||
return obj.value; | ||
}, | ||
set: (type, key, value) => { | ||
this._cache[type][key] = { | ||
timestamp: new Date(), | ||
value | ||
}; | ||
} | ||
}; | ||
this._cache = { | ||
enabled: true, | ||
expiration: 600000, | ||
users: {}, | ||
groups: {}, | ||
ous: {}, | ||
all: {}, | ||
get: (type, key) => { | ||
if (!this._cache.enabled) { | ||
return undefined; | ||
} | ||
if (!this._cache[type] || !this._cache[type][key]) { | ||
return undefined; | ||
} | ||
let obj = this._cache[type][key]; | ||
let diff = (new Date() - obj.timestamp); | ||
if (diff > this._cache.expiration) { | ||
delete this._cache[type][key]; | ||
return undefined; | ||
} | ||
return obj.value; | ||
}, | ||
set: (type, key, value) => { | ||
this._cache[type][key] = { | ||
timestamp: new Date(), | ||
value | ||
} | ||
} | ||
}; | ||
this.ad = new activedirectory({ | ||
url: config.url, | ||
baseDN: config.baseDN, | ||
username: config.user, | ||
password: config.pass, | ||
tlsOptions: { | ||
rejectUnauthorized: false | ||
} | ||
}); | ||
} | ||
this.ad = new activedirectory({ | ||
url: config.url, | ||
baseDN: config.baseDN, | ||
username: config.user, | ||
password: config.pass, | ||
tlsOptions: { | ||
rejectUnauthorized: false | ||
} | ||
}); | ||
} | ||
cache(bool) { | ||
this._cache.enabled = bool; | ||
return this; | ||
} | ||
cache(bool) { | ||
this._cache.enabled = bool; | ||
return this; | ||
} | ||
cacheTimeout(millis) { | ||
this._cache.expiration = millis; | ||
return this; | ||
} | ||
cacheTimeout(millis) { | ||
this._cache.expiration = millis; | ||
return this; | ||
} | ||
} | ||
const loadImports = () => { | ||
for (const key in imports) { | ||
let file = imports[key]; | ||
for (const fn in file) { | ||
AD.prototype[fn] = file[fn]; | ||
} | ||
} | ||
} | ||
for (const key in imports) { | ||
let file = imports[key]; | ||
for (const fn in file) { | ||
AD.prototype[fn] = file[fn]; | ||
} | ||
} | ||
}; | ||
@@ -115,2 +115,1 @@ loadImports(); | ||
module.exports = AD; | ||
{ | ||
"name": "ad", | ||
"version": "0.2.3", | ||
"version": "0.2.4", | ||
"description": "Active Directory API for Node", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "jest --coverage" | ||
"precommit": "lint-staged" | ||
}, | ||
@@ -31,4 +31,7 @@ "keywords": [ | ||
"devDependencies": { | ||
"jest": "^20.0.4" | ||
"husky": "^0.14.3", | ||
"jest": "^20.0.4", | ||
"lint-staged": "^4.0.2", | ||
"prettier": "^1.5.3" | ||
} | ||
} |
@@ -13,3 +13,3 @@ # AD | ||
Really simple. jQuery-simple ([it doesn't do addition though](http://4.bp.blogspot.com/-Hk1mt-RKYLc/UOkxShm6NrI/AAAAAAAACqo/LVmqHOfWV7g/s1600/20091116-so-large.gif)). | ||
Really simple. | ||
@@ -56,2 +56,3 @@ You can use `async` / `await`: | ||
- Fancy result filtering including column and value filtering, sorting and pagination | ||
- [Companion drop-in REST API](https://github.com/dthree/addict) | ||
@@ -58,0 +59,0 @@ ## Getting Started |
@@ -1,2 +0,1 @@ | ||
/** | ||
@@ -44,139 +43,141 @@ * Exposes library through simple, | ||
module.exports = { | ||
user(userName) { | ||
if (userName === undefined) { | ||
return { | ||
get: filter => { | ||
return this.getAllUsers(filter); | ||
}, | ||
add: opts => { | ||
return this.addUser(opts); | ||
} | ||
}; | ||
} | ||
user(userName) { | ||
if (userName === undefined) { | ||
return { | ||
get: (filter) => { | ||
return this.getAllUsers(filter); | ||
}, | ||
add: (opts) => { | ||
return this.addUser(opts); | ||
} | ||
} | ||
} | ||
return { | ||
get: opts => { | ||
return this.findUser(userName, opts); | ||
}, | ||
update: opts => { | ||
return this.updateUser(userName, opts); | ||
}, | ||
exists: () => { | ||
return this.userExists(userName); | ||
}, | ||
addToGroup: groupName => { | ||
return this.addUserToGroup(userName, groupName); | ||
}, | ||
removeFromGroup: groupName => { | ||
return this.removeUserFromGroup(userName, groupName); | ||
}, | ||
isMemberOf: groupName => { | ||
return this.userIsMemberOf(userName, groupName); | ||
}, | ||
authenticate: pass => { | ||
return this.authenticateUser(userName, pass); | ||
}, | ||
password: pass => { | ||
return this.setUserPassword(userName, pass); | ||
}, | ||
passwordNeverExpires: () => { | ||
return this.setUserPasswordNeverExpires(userName); | ||
}, | ||
passwordExpires: () => { | ||
return this.enableUser(userName); | ||
}, | ||
enable: () => { | ||
return this.enableUser(userName); | ||
}, | ||
disable: () => { | ||
return this.disableUser(userName); | ||
}, | ||
move: location => { | ||
return this.moveUser(userName, location); | ||
}, | ||
location: () => { | ||
return this.getUserLocation(userName); | ||
}, | ||
unlock: () => { | ||
return this.unlockUser(userName); | ||
}, | ||
remove: () => { | ||
return this.removeUser(userName); | ||
} | ||
}; | ||
}, | ||
return { | ||
get: (opts) => { | ||
return this.findUser(userName, opts); | ||
}, | ||
exists: () => { | ||
return this.userExists(userName); | ||
}, | ||
addToGroup: (groupName) => { | ||
return this.addUserToGroup(userName, groupName); | ||
}, | ||
removeFromGroup: (groupName) => { | ||
return this.removeUserFromGroup(userName, groupName); | ||
}, | ||
isMemberOf: (groupName) => { | ||
return this.userIsMemberOf(userName, groupName); | ||
}, | ||
authenticate: (pass) => { | ||
return this.authenticateUser(userName, pass); | ||
}, | ||
password: (pass) => { | ||
return this.setUserPassword(userName, pass); | ||
}, | ||
passwordNeverExpires: () => { | ||
return this.setUserPasswordNeverExpires(userName); | ||
}, | ||
passwordExpires: () => { | ||
return this.enableUser(userName); | ||
}, | ||
enable: () => { | ||
return this.enableUser(userName); | ||
}, | ||
disable: () => { | ||
return this.disableUser(userName); | ||
}, | ||
move: (location) => { | ||
return this.moveUser(userName, location); | ||
}, | ||
location: () => { | ||
return this.getUserLocation(userName); | ||
}, | ||
unlock: () => { | ||
return this.unlockUser(userName); | ||
}, | ||
remove: () => { | ||
return this.removeUser(userName); | ||
}, | ||
} | ||
}, | ||
group(groupName) { | ||
if (groupName === undefined) { | ||
return { | ||
get: opts => { | ||
return this.getAllGroups(opts); | ||
}, | ||
add: opts => { | ||
return this.addGroup(opts); | ||
} | ||
}; | ||
} | ||
group(groupName) { | ||
if (groupName === undefined) { | ||
return { | ||
get: (opts) => { | ||
return this.getAllGroups(opts); | ||
}, | ||
add: (opts) => { | ||
return this.addGroup(opts); | ||
} | ||
} | ||
} | ||
return { | ||
get: opts => { | ||
return this.findGroup(groupName, opts); | ||
}, | ||
exists: () => { | ||
return this.groupExists(groupName); | ||
}, | ||
addUser: userName => { | ||
return this.addUserToGroup(userName, groupName); | ||
}, | ||
removeUser: userName => { | ||
return this.removeUserFromGroup(userName, groupName); | ||
}, | ||
remove: () => { | ||
return this.removeGroup(groupName); | ||
} | ||
}; | ||
}, | ||
return { | ||
get: (opts) => { | ||
return this.findGroup(groupName, opts); | ||
}, | ||
exists: () => { | ||
return this.groupExists(groupName); | ||
}, | ||
addUser: (userName) => { | ||
return this.addUserToGroup(userName, groupName); | ||
}, | ||
removeUser: (userName) => { | ||
return this.removeUserFromGroup(userName, groupName); | ||
}, | ||
remove: () => { | ||
return this.removeGroup(groupName); | ||
} | ||
} | ||
}, | ||
ou(ouName) { | ||
if (ouName === undefined) { | ||
return { | ||
get: filter => { | ||
return this.getAllOUs(filter); | ||
}, | ||
add: opts => { | ||
return this.addOU(opts); | ||
} | ||
}; | ||
} | ||
ou(ouName) { | ||
if (ouName === undefined) { | ||
return { | ||
get: (filter) => { | ||
return this.getAllOUs(filter); | ||
}, | ||
add: (opts) => { | ||
return this.addOU(opts); | ||
} | ||
} | ||
} | ||
return { | ||
get: () => { | ||
return this.findOU(ouName); | ||
}, | ||
exists: () => { | ||
return this.ouExists(ouName); | ||
}, | ||
remove: () => { | ||
return this.removeOU(ouName); | ||
} | ||
}; | ||
}, | ||
return { | ||
get: () => { | ||
return this.findOU(ouName); | ||
}, | ||
exists: () => { | ||
return this.ouExists(ouName); | ||
}, | ||
remove: () => { | ||
return this.removeOU(ouName); | ||
} | ||
} | ||
}, | ||
other() { | ||
return { | ||
get: opts => { | ||
return this.getAllOthers(opts); | ||
} | ||
}; | ||
}, | ||
other() { | ||
return { | ||
get: (opts) => { | ||
return this.getAllOthers(opts); | ||
} | ||
} | ||
}, | ||
all() { | ||
return { | ||
get: opts => { | ||
return this.getAll(opts); | ||
} | ||
}; | ||
}, | ||
all() { | ||
return { | ||
get: (opts) => { | ||
return this.getAll(opts); | ||
} | ||
} | ||
}, | ||
find(searchString, opts) { | ||
return this._search(searchString, opts); | ||
} | ||
} | ||
find(searchString, opts) { | ||
return this._search(searchString, opts); | ||
} | ||
}; |
230
src/group.js
@@ -16,117 +16,127 @@ const api = require('./util/api'); | ||
module.exports = { | ||
async getAllGroups(opts) { | ||
return await this._findByType(opts, ['group']); | ||
}, | ||
async getAllGroups(opts) { | ||
return await this._findByType(opts, ['group']); | ||
}, | ||
async findGroup(groupName, opts) { | ||
groupName = String(groupName || ''); | ||
return new Promise(async (resolve, reject) => { | ||
groupName = | ||
groupName.indexOf('@') > -1 ? groupName.split('@')[0] : groupName; | ||
if (groupName.trim() === '') { | ||
/* istanbul ignore next */ | ||
return reject(`${groupName} is not a valid Group name.`); | ||
} | ||
const filter = `(|(cn=${groupName}))`; | ||
const config = { | ||
filter, | ||
includeDeleted: false | ||
}; | ||
try { | ||
this.ad.find(config, async (err, results) => { | ||
if (err) { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
} | ||
if (!results || !results.groups || results.groups.length < 1) { | ||
return resolve({}); | ||
} | ||
results.groups = api.processResults(opts, results.groups); | ||
return resolve(results.groups[0]); | ||
}); | ||
} catch (e) { | ||
/* istanbul ignore next */ | ||
reject(e); | ||
} | ||
}); | ||
}, | ||
async findGroup(groupName, opts) { | ||
groupName = String(groupName || ''); | ||
return new Promise(async (resolve, reject) => { | ||
groupName = (groupName.indexOf('@') > -1) ? groupName.split('@')[0] : groupName; | ||
if (groupName.trim() === '') { | ||
/* istanbul ignore next */ | ||
return reject(`${groupName} is not a valid Group name.`); | ||
} | ||
const filter = `(|(cn=${groupName}))`; | ||
const config = { | ||
filter, | ||
includeDeleted: false | ||
}; | ||
try { | ||
this.ad.find(config, async (err, results) => { | ||
if (err) { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
} | ||
if (!results || !results.groups || results.groups.length < 1) { | ||
return resolve({}); | ||
} | ||
results.groups = api.processResults(opts, results.groups); | ||
return resolve(results.groups[0]); | ||
}); | ||
} catch(e) { | ||
/* istanbul ignore next */ | ||
reject(e); | ||
} | ||
}); | ||
}, | ||
async groupExists(groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName) | ||
.then(groupObject => { | ||
let exists = !groupObject || !groupObject.dn ? false : true; | ||
resolve(exists); | ||
}) | ||
.catch(reject); | ||
}); | ||
}, | ||
async groupExists(groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName).then(groupObject => { | ||
let exists = (!groupObject || !groupObject.dn) ? false : true; | ||
resolve(exists); | ||
}).catch(reject); | ||
}); | ||
}, | ||
async addGroup(opts) { | ||
if (typeof opts === 'string') { | ||
opts = { name: opts }; | ||
} | ||
let { name, location, description } = opts; | ||
location = parseLocation(location); | ||
return this._addObject(`CN=${name}`, location, { | ||
cn: name, | ||
description: description, | ||
objectClass: 'group', | ||
sAmAccountName: name | ||
}); | ||
}, | ||
async addGroup(opts) { | ||
if (typeof opts === 'string') { | ||
opts = {name: opts}; | ||
} | ||
let { | ||
name, | ||
location, | ||
description | ||
} = opts; | ||
location = parseLocation(location); | ||
return this._addObject(`CN=${name}`, location, { | ||
cn: name, | ||
description: description, | ||
objectClass: 'group', | ||
sAmAccountName: name | ||
}); | ||
}, | ||
async addUserToGroup(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({ | ||
error: true, | ||
message: `User ${userName} does not exist.` | ||
}); | ||
} | ||
this._groupAddOperation(groupName, { | ||
member: [userObject.dn] | ||
}) | ||
.then(resp => { | ||
resolve(resp); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, { error: true })); | ||
}); | ||
}); | ||
}); | ||
}, | ||
async addUserToGroup(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({error: true, message: `User ${userName} does not exist.`}); | ||
} | ||
this._groupAddOperation(groupName, { | ||
member: [userObject.dn] | ||
}).then(resp => { | ||
resolve(resp); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, {error: true})); | ||
}); | ||
}); | ||
}); | ||
}, | ||
async removeUserFromGroup(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({ error: true, message: 'User does not exist.' }); | ||
} | ||
this._groupDeleteOperation(groupName, { | ||
member: [userObject.dn] | ||
}) | ||
.then(resp => { | ||
resolve(resp); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, { error: true })); | ||
}); | ||
}); | ||
}); | ||
}, | ||
async removeUserFromGroup(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({error: true, message: 'User does not exist.'}); | ||
} | ||
this._groupDeleteOperation(groupName, { | ||
member: [userObject.dn] | ||
}).then(resp => { | ||
resolve(resp); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, {error: true})); | ||
}); | ||
}); | ||
}) | ||
}, | ||
async removeGroup(groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName).then(groupObject => { | ||
if (Object.keys(groupObject).length < 1) { | ||
return reject({error: true, message: `Group ${groupName} does not exist.`}); | ||
} | ||
return this._deleteObjectByDN(groupObject.dn) | ||
}).then(resp => { | ||
return resolve(resp); | ||
}).catch(reject); | ||
}); | ||
}, | ||
} | ||
async removeGroup(groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName) | ||
.then(groupObject => { | ||
if (Object.keys(groupObject).length < 1) { | ||
return reject({ | ||
error: true, | ||
message: `Group ${groupName} does not exist.` | ||
}); | ||
} | ||
return this._deleteObjectByDN(groupObject.dn); | ||
}) | ||
.then(resp => { | ||
return resolve(resp); | ||
}) | ||
.catch(reject); | ||
}); | ||
} | ||
}; |
@@ -15,78 +15,81 @@ const ldap = require('ldapjs'); | ||
module.exports = { | ||
async _operation(objectString, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
const [error, client] = await this._getBoundClient(); | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
operation = Array.isArray(operation) ? operation : [operation]; | ||
const operations = operation.map(op => new ldap.Change(op)); | ||
client.modify(objectString, operations, (error3, data) => { | ||
if (error3) { | ||
/* istanbul ignore next */ | ||
return reject(error3); | ||
} | ||
return resolve({ success: true }); | ||
}); | ||
}); | ||
}, | ||
async _operation(objectString, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
const [error, client] = await this._getBoundClient(); | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
operation = Array.isArray(operation) ? operation : [operation]; | ||
const operations = operation.map(op => new ldap.Change(op)); | ||
client.modify(objectString, operations, (error3, data) => { | ||
if (error3) { | ||
/* istanbul ignore next */ | ||
return reject(error3); | ||
} | ||
return resolve({success: true}); | ||
} | ||
); | ||
}); | ||
}, | ||
async _operationByUser(userName, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
const domain = this.config.domain; | ||
userName = `${userName}@${domain}`; | ||
this.findUser(userName) | ||
.then(async userObject => { | ||
if (!userObject || !userObject.dn) { | ||
/* istanbul ignore next */ | ||
return reject({ message: `User ${userName} does not exist.` }); | ||
} | ||
return this._operation(userObject.dn, operation); | ||
}) | ||
.then(data => { | ||
delete this._cache.users[userName]; | ||
resolve({ success: true }); | ||
}) | ||
.catch(error => { | ||
/* istanbul ignore next */ | ||
reject(error); | ||
}); | ||
}); | ||
}, | ||
async _operationByUser(userName, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
const domain = this.config.domain; | ||
userName = `${userName}@${domain}`; | ||
this.findUser(userName).then(async userObject => { | ||
if (!userObject || !userObject.dn) { | ||
/* istanbul ignore next */ | ||
return reject({message: `User ${userName} does not exist.`}); | ||
} | ||
return this._operation(userObject.dn, operation); | ||
}).then(data => { | ||
resolve({success: true}); | ||
}).catch(error => { | ||
/* istanbul ignore next */ | ||
reject(error); | ||
}); | ||
}); | ||
}, | ||
async _operationByGroup(groupName, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName) | ||
.then(async groupObject => { | ||
if (!groupObject || Object.keys(groupObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({ message: `Group ${groupName} does not exist.` }); | ||
} | ||
return this._operation(groupObject.dn, operation); | ||
}) | ||
.then(data => { | ||
resolve(data); | ||
}) | ||
.catch(reject); | ||
}); | ||
}, | ||
async _operationByGroup(groupName, operation) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findGroup(groupName).then(async groupObject => { | ||
if (!groupObject || Object.keys(groupObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({message: `Group ${groupName} does not exist.`}); | ||
} | ||
return this._operation(groupObject.dn, operation); | ||
}).then(data => { | ||
resolve(data); | ||
}).catch(reject); | ||
}); | ||
}, | ||
async _groupAddOperation(groupName, modification) { | ||
return this._operationByGroup(groupName, { | ||
operation: 'add', | ||
modification | ||
}); | ||
}, | ||
async _groupAddOperation(groupName, modification) { | ||
return this._operationByGroup(groupName, { | ||
operation: 'add', | ||
modification | ||
}); | ||
}, | ||
async _groupDeleteOperation(groupName, modification) { | ||
return this._operationByGroup(groupName, { | ||
operation: 'delete', | ||
modification | ||
}); | ||
}, | ||
async _groupDeleteOperation(groupName, modification) { | ||
return this._operationByGroup(groupName, { | ||
operation: 'delete', | ||
modification | ||
}); | ||
}, | ||
async _userReplaceOperation(userName, modification) { | ||
return this._operationByUser(userName, { | ||
operation: 'replace', | ||
modification | ||
}); | ||
}, | ||
} | ||
async _userReplaceOperation(userName, modification) { | ||
return this._operationByUser(userName, { | ||
operation: 'replace', | ||
modification | ||
}); | ||
} | ||
}; |
@@ -9,12 +9,9 @@ /** | ||
module.exports = { | ||
async getAllOthers(opts) { | ||
return await this._findByType(opts, ['other']); | ||
}, | ||
async getAllOthers(opts) { | ||
return await this._findByType(opts, ['other']); | ||
}, | ||
async getAll(opts) { | ||
return await this._findByType(opts, ['all']); | ||
} | ||
} | ||
async getAll(opts) { | ||
return await this._findByType(opts, ['all']); | ||
} | ||
}; |
118
src/ou.js
@@ -14,63 +14,65 @@ const api = require('./util/api'); | ||
module.exports = { | ||
async getAllOUs(opts) { | ||
return new Promise(async (resolve, reject) => { | ||
const search = `OU=*`; | ||
this._search(search) | ||
.then(results => { | ||
if (!results || !results.other) { | ||
/* istanbul ignore next */ | ||
return resolve([]); | ||
} | ||
let match = results.other.filter(obj => { | ||
return ( | ||
String(obj.dn).split(',')[0].toLowerCase().indexOf('ou=') > -1 | ||
); | ||
}); | ||
resolve(api.processResults(opts, match)); | ||
}) | ||
.catch(reject); | ||
}); | ||
}, | ||
async getAllOUs(opts) { | ||
return new Promise(async (resolve, reject) => { | ||
const search = `OU=*`; | ||
this._search(search).then(results => { | ||
if (!results || !results.other) { | ||
/* istanbul ignore next */ | ||
return resolve([]); | ||
} | ||
let match = results.other.filter(obj => { | ||
return (String(obj.dn).split(',')[0].toLowerCase().indexOf('ou=') > -1); | ||
}); | ||
resolve(api.processResults(opts, match)); | ||
}).catch(reject); | ||
}); | ||
}, | ||
async findOU(ouName) { | ||
return new Promise(async (resolve, reject) => { | ||
const search = `OU=${ouName}`; | ||
this._search(search) | ||
.then(results => { | ||
if (!results || !results.other) { | ||
return resolve(undefined); | ||
} | ||
let match = results.other.filter(ou => { | ||
return ( | ||
String(ou.dn).split(',')[0].toLowerCase() === search.toLowerCase() | ||
); | ||
}); | ||
resolve(match[0]); | ||
}) | ||
.catch(reject); | ||
}); | ||
}, | ||
async findOU(ouName) { | ||
return new Promise(async (resolve, reject) => { | ||
const search = `OU=${ouName}`; | ||
this._search(search).then(results => { | ||
if (!results || !results.other) { | ||
return resolve(undefined); | ||
} | ||
let match = results.other.filter(ou => { | ||
return (String(ou.dn).split(',')[0].toLowerCase() === search.toLowerCase()); | ||
}); | ||
resolve(match[0]); | ||
}).catch(reject); | ||
}); | ||
}, | ||
async ouExists(ouName) { | ||
return new Promise(async (resolve, reject) => { | ||
return this.findOU(ouName).then(ou => { | ||
resolve(ou !== undefined ? true : false); | ||
}); | ||
}); | ||
}, | ||
async ouExists(ouName) { | ||
return new Promise(async (resolve, reject) => { | ||
return this.findOU(ouName).then(ou => { | ||
resolve((ou !== undefined) ? true : false) | ||
}); | ||
}); | ||
}, | ||
async addOU(opts) { | ||
if (typeof opts === 'string') { | ||
opts = { name: opts }; | ||
} | ||
let { name, location, description } = opts; | ||
location = parseLocation(location); | ||
return this._addObject(`OU=${name}`, location, { | ||
ou: name, | ||
description: description, | ||
objectClass: 'organizationalunit' | ||
}); | ||
}, | ||
async addOU(opts) { | ||
if (typeof opts === 'string') { | ||
opts = {name: opts}; | ||
} | ||
let { | ||
name, | ||
location, | ||
description | ||
} = opts; | ||
location = parseLocation(location); | ||
return this._addObject(`OU=${name}`, location, { | ||
ou: name, | ||
description: description, | ||
objectClass: 'organizationalunit' | ||
}); | ||
}, | ||
async removeOU(ouName) { | ||
return this._deleteObjectBySearch(`OU=${ouName}`); | ||
} | ||
} | ||
async removeOU(ouName) { | ||
return this._deleteObjectBySearch(`OU=${ouName}`); | ||
} | ||
}; |
629
src/user.js
@@ -26,269 +26,410 @@ const ssha = require('node-ssha256'); | ||
module.exports = { | ||
async getAllUsers(opts) { | ||
return await this._findByType(opts, ['user']); | ||
}, | ||
async getAllUsers(opts) { | ||
return await this._findByType(opts, ['user']); | ||
}, | ||
async addUser(opts) { | ||
return new Promise(async (resolve, reject) => { | ||
let { | ||
firstName, | ||
lastName, | ||
commonName, | ||
userName, | ||
pass, | ||
email, | ||
title, | ||
phone, | ||
location | ||
} = opts; | ||
async addUser(opts) { | ||
return new Promise(async (resolve, reject) => { | ||
let { passwordExpires, enabled } = opts; | ||
let { | ||
firstName, | ||
lastName, | ||
commonName, | ||
userName, | ||
pass, | ||
email, | ||
title, | ||
phone, | ||
location | ||
} = opts; | ||
if (commonName) { | ||
let cnParts = String(commonName).split(' '); | ||
firstName = firstName ? firstName : cnParts[0]; | ||
if (cnParts.length > 1) { | ||
lastName = lastName ? lastName : cnParts[cnParts.length - 1]; | ||
} | ||
} else { | ||
if (firstName && lastName) { | ||
commonName = `${firstName} ${lastName}`; | ||
} | ||
} | ||
if (commonName) { | ||
let cnParts = String(commonName).split(' '); | ||
firstName = (firstName) ? firstName : cnParts[0]; | ||
if (cnParts.length > 1) { | ||
lastName = (lastName) ? lastName : cnParts[cnParts.length - 1]; | ||
} | ||
} else { | ||
if (firstName && lastName) { | ||
commonName = `${firstName} ${lastName}`; | ||
} | ||
} | ||
location = parseLocation(location); | ||
location = parseLocation(location); | ||
let valid = | ||
email && String(email).indexOf('@') === -1 | ||
? 'Invalid email address.' | ||
: !commonName | ||
? 'A commonName is required.' | ||
: !userName ? 'A userName is required.' : true; | ||
let valid = (email && String(email).indexOf('@') === -1) ? 'Invalid email address.' | ||
: (!commonName) ? 'A commonName is required.' | ||
: (!userName) ? 'A userName is required.' | ||
: true; | ||
if (valid !== true) { | ||
/* istanbul ignore next */ | ||
return reject({ error: true, message: valid, httpStatus: 400 }); | ||
} | ||
if (valid !== true) { | ||
/* istanbul ignore next */ | ||
return reject({error: true, message: valid, httpStatus: 400}); | ||
} | ||
const userObject = { | ||
cn: commonName, | ||
givenName: firstName, | ||
sn: lastName, | ||
mail: email, | ||
uid: userName, | ||
title: title, | ||
telephone: phone, | ||
userPrincipalName: `${userName}@${this.config.domain}`, | ||
sAMAccountName: userName, | ||
objectClass: this.config.defaults.userObjectClass, | ||
userPassword: ssha.create(pass) | ||
}; | ||
const userObject = { | ||
cn: commonName, | ||
givenName: firstName, | ||
sn: lastName, | ||
mail: email, | ||
uid: userName, | ||
title: title, | ||
telephone: phone, | ||
userPrincipalName: `${userName}@${this.config.domain}`, | ||
sAMAccountName: userName, | ||
objectClass: this.config.defaults.userObjectClass, | ||
userPassword: ssha.create(pass) | ||
} | ||
this._addObject(`CN=${commonName}`, location, userObject) | ||
.then(res => { | ||
delete this._cache.users[userName]; | ||
this._cache.all = {}; | ||
return this.setUserPassword(userName, pass); | ||
}) | ||
.then(data => { | ||
let expirationMethod = | ||
passwordExpires === false | ||
? 'setUserPasswordNeverExpires' | ||
: 'enableUser'; | ||
return this[expirationMethod](userName); | ||
}) | ||
.then(data => { | ||
let enableMethod = enabled === false ? 'disableUser' : 'enableUser'; | ||
return this[enableMethod](userName); | ||
}) | ||
.then(data => { | ||
delete userObject.userPassword; | ||
return resolve(userObject); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
const ENTRY_EXISTS = String(err.message).indexOf('ENTRY_EXISTS') > -1; | ||
/* istanbul ignore next */ | ||
if (ENTRY_EXISTS) { | ||
/* istanbul ignore next */ | ||
return reject({ | ||
message: `User ${userName} already exists.`, | ||
httpStatus: 400 | ||
}); | ||
} | ||
/* istanbul ignore next */ | ||
return reject({ | ||
message: `Error creating user: ${err.message}`, | ||
httpStatus: 503 | ||
}); | ||
}); | ||
}); | ||
}, | ||
this._addObject(`CN=${commonName}`, location, userObject).then(res => { | ||
delete this._cache.users[userName]; | ||
this._cache.all = {}; | ||
return this.setUserPassword(userName, pass); | ||
}).then(data => { | ||
return this.enableUser(userName) | ||
}).then(data => { | ||
delete userObject.userPassword; | ||
return resolve(userObject); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
const ENTRY_EXISTS = (String(err.message).indexOf('ENTRY_EXISTS') > -1); | ||
/* istanbul ignore next */ | ||
if (ENTRY_EXISTS) { | ||
/* istanbul ignore next */ | ||
return reject({message: `User ${userName} already exists.`, httpStatus: 400}); | ||
} | ||
/* istanbul ignore next */ | ||
return reject({message: `Error creating user: ${err.message}`, httpStatus: 503}); | ||
}); | ||
}); | ||
}, | ||
async updateUser(userName, opts) { | ||
return new Promise((resolve, reject) => { | ||
const domain = this.config.domain; | ||
const map = { | ||
firstName: 'givenName', | ||
lastName: 'sn', | ||
password: 'unicodePwd', | ||
commonName: 'cn', | ||
email: 'mail', | ||
title: 'title', | ||
objectClass: 'objectClass', | ||
userName: 'sAMAccountName' | ||
}; | ||
async findUser(userName, opts) { | ||
userName = String(userName || ''); | ||
return new Promise(async (resolve, reject) => { | ||
let cached = this._cache.get('users', userName); | ||
if (cached) { | ||
return resolve(api.processResults(opts, [cached])[0]); | ||
} | ||
const domain = this.config.domain; | ||
userName = (userName.indexOf('@') > -1) ? userName.split('@')[0] : userName; | ||
const filter = `(|(userPrincipalName=${userName}@${domain})(sAMAccountName=${userName}))`; | ||
const params = { | ||
filter, | ||
includeMembership: ['all'], | ||
includeDeleted: false | ||
}; | ||
this.ad.find(params, (err, results) => { | ||
if (err) { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
} | ||
if (!results || !results.users || results.users.length < 1) { | ||
this._cache.set('users', userName, {}); | ||
return resolve({}); | ||
} | ||
this._cache.set('users', userName, results.users[0]); | ||
results.users = api.processResults(opts, results.users); | ||
return resolve(results.users[0]); | ||
}); | ||
}); | ||
}, | ||
let later = []; | ||
let operations = []; | ||
for (const name in opts) { | ||
if (map[name] !== undefined) { | ||
let key = map[name]; | ||
let value = | ||
name === 'password' ? encodePassword(opts[name]) : opts[name]; | ||
if (key !== 'cn') { | ||
if (key === 'sAMAccountName') { | ||
later.push({ | ||
sAMAccountName: value | ||
}); | ||
later.push({ | ||
uid: value | ||
}); | ||
later.push({ | ||
userPrincipalName: `${value}@${domain}` | ||
}); | ||
} else { | ||
operations.push({ | ||
[key]: value | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
async userExists(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
const domain = this.config.domain; | ||
let fullUser = `${userName}@${domain}`; | ||
this.ad.userExists(fullUser, (error, exists) => { | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
return resolve(exists); | ||
}); | ||
}); | ||
}, | ||
operations = operations.concat(later); | ||
let currUserName = userName; | ||
const go = () => { | ||
if (operations.length < 1) { | ||
delete this._cache.users[currUserName]; | ||
delete this._cache.users[userName]; | ||
resolve(); | ||
return; | ||
} | ||
let next = operations.pop(); | ||
this.setUserProperty(currUserName, next) | ||
.then(res => { | ||
if (next.userPrincipalName !== undefined) { | ||
currUserName = next.userPrincipalName; | ||
} | ||
delete this._cache.users[currUserName]; | ||
go(); | ||
}) | ||
.catch(err => { | ||
return reject(err); | ||
}); | ||
}; | ||
async userIsMemberOf(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
let userDN; | ||
this.findUser(userName).then(userObject => { | ||
userDN = userObject.dn; | ||
return this._getGroupUsers(groupName); | ||
}).then(users => { | ||
users = users.filter(u => u.dn === userDN); | ||
let exists = users.length > 0; | ||
resolve(exists); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
reject(err); | ||
}); | ||
}); | ||
}, | ||
if (opts.commonName !== undefined) { | ||
this.setUserCN(currUserName, opts.commonName) | ||
.then(res => { | ||
go(); | ||
}) | ||
.then(data => { | ||
let expirationMethod = | ||
opts.passwordExpires === false | ||
? 'setUserPasswordNeverExpires' | ||
: 'enableUser'; | ||
return this[expirationMethod](userName); | ||
}) | ||
.then(data => { | ||
let enableMethod = | ||
opts.enabled === false ? 'disableUser' : 'enableUser'; | ||
return this[enableMethod](userName); | ||
}) | ||
.catch(err => { | ||
return reject(err); | ||
}); | ||
} | ||
}); | ||
}, | ||
async authenticateUser(userName, pass) { | ||
const domain = this.config.domain; | ||
let fullUser = `${userName}@${domain}`; | ||
return new Promise(async (resolve, reject) => { | ||
this.ad.authenticate(fullUser, pass, (error, authorized) => { | ||
let code; | ||
let out = authorized; | ||
if (error && error.lde_message) { | ||
out.detail = error.lde_message; | ||
out.message = String(error.stack).split(':')[0]; | ||
error = undefined; | ||
} | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
return resolve(out); | ||
}); | ||
}); | ||
}, | ||
async findUser(userName, opts) { | ||
userName = String(userName || ''); | ||
return new Promise(async (resolve, reject) => { | ||
let cached = this._cache.get('users', userName); | ||
if (cached) { | ||
return resolve(api.processResults(opts, [cached])[0]); | ||
} | ||
const domain = this.config.domain; | ||
userName = userName.indexOf('@') > -1 ? userName.split('@')[0] : userName; | ||
const filter = `(|(userPrincipalName=${userName}@${domain})(sAMAccountName=${userName}))`; | ||
const params = { | ||
filter, | ||
includeMembership: ['all'], | ||
includeDeleted: false | ||
}; | ||
this.ad.find(params, (err, results) => { | ||
if (err) { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
} | ||
if (!results || !results.users || results.users.length < 1) { | ||
this._cache.set('users', userName, {}); | ||
return resolve({}); | ||
} | ||
this._cache.set('users', userName, results.users[0]); | ||
results.users = api.processResults(opts, results.users); | ||
return resolve(results.users[0]); | ||
}); | ||
}); | ||
}, | ||
async setUserPassword(userName, pass) { | ||
return new Promise((resolve, reject) => { | ||
if (!pass) { | ||
return reject({message: 'No password provided.'}); | ||
} | ||
this._userReplaceOperation(userName, { | ||
unicodePwd: encodePassword(pass) | ||
}).then(resolve).catch(reject); | ||
}); | ||
}, | ||
async userExists(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
const domain = this.config.domain; | ||
let fullUser = `${userName}@${domain}`; | ||
this.ad.userExists(fullUser, (error, exists) => { | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
return resolve(exists); | ||
}); | ||
}); | ||
}, | ||
async setUserPasswordNeverExpires(userName) { | ||
const NEVER_EXPIRES = 66048; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: NEVER_EXPIRES | ||
}); | ||
}, | ||
async userIsMemberOf(userName, groupName) { | ||
return new Promise(async (resolve, reject) => { | ||
let userDN; | ||
this.findUser(userName) | ||
.then(userObject => { | ||
userDN = userObject.dn; | ||
return this._getGroupUsers(groupName); | ||
}) | ||
.then(users => { | ||
users = users.filter(u => u.dn === userDN); | ||
let exists = users.length > 0; | ||
resolve(exists); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(err); | ||
}); | ||
}); | ||
}, | ||
async enableUser(userName) { | ||
const ENABLED = 512; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: ENABLED | ||
}); | ||
}, | ||
async authenticateUser(userName, pass) { | ||
const domain = this.config.domain; | ||
let fullUser = `${userName}@${domain}`; | ||
return new Promise(async (resolve, reject) => { | ||
this.ad.authenticate(fullUser, pass, (error, authorized) => { | ||
let code; | ||
let out = authorized; | ||
if (error && error.lde_message) { | ||
out.detail = error.lde_message; | ||
out.message = String(error.stack).split(':')[0]; | ||
error = undefined; | ||
} | ||
if (error) { | ||
/* istanbul ignore next */ | ||
return reject(error); | ||
} | ||
return resolve(out); | ||
}); | ||
}); | ||
}, | ||
async disableUser(userName) { | ||
const DISABLED = 514; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: DISABLED | ||
}); | ||
}, | ||
async setUserPassword(userName, pass) { | ||
return new Promise((resolve, reject) => { | ||
if (!pass) { | ||
return reject({ message: 'No password provided.' }); | ||
} | ||
this._userReplaceOperation(userName, { | ||
unicodePwd: encodePassword(pass) | ||
}) | ||
.then(resolve) | ||
.catch(reject); | ||
}); | ||
}, | ||
async moveUser(userName, location) { | ||
return new Promise(async (resolve, reject) => { | ||
location = parseLocation(location); | ||
this.findUser(userName).then(userObject => { | ||
let oldDN = userObject.dn; | ||
let baseDN = String(this.config.baseDN).replace(/dc=/g, 'DC='); | ||
let newDN = `CN=${userObject.cn},${location}${baseDN}`; | ||
return this._modifyDN(oldDN, newDN); | ||
}).then(result => { | ||
delete this._cache.users[userName]; | ||
resolve(result); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
reject(err); | ||
}); | ||
}); | ||
}, | ||
async setUserCN(userName, cn) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName) | ||
.then(userObject => { | ||
let oldDN = userObject.dn; | ||
let parts = String(oldDN).split(','); | ||
parts.shift(); | ||
parts.unshift(`CN=${cn}`); | ||
return this._modifyDN(oldDN, parts.join(',')); | ||
}) | ||
.then(result => { | ||
delete this._cache.users[userName]; | ||
resolve(result); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(err); | ||
}); | ||
}); | ||
}, | ||
async getUserLocation(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({error: true, message: 'User does not exist.'}); | ||
} | ||
let dn = userObject.dn; | ||
let left = String(dn) | ||
.replace(/DC=/g, 'dc=') | ||
.replace(/CN=/g, 'cn=') | ||
.replace(/OU=/g, 'ou=') | ||
.split(',dc=')[0]; | ||
let location = String(left) | ||
.split(',') | ||
.slice(1) | ||
.reverse() | ||
.join('/') | ||
.replace(/cn=/g, '!') | ||
.replace(/ou=/g, ''); | ||
return resolve(location); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
}); | ||
}); | ||
}, | ||
async setUserProperty(userName, obj) { | ||
return this._userReplaceOperation(userName, obj); | ||
}, | ||
async unlockUser(userName) { | ||
return this._userReplaceOperation(userName, { | ||
lockoutTime: 0 | ||
}); | ||
}, | ||
async setUserPasswordNeverExpires(userName) { | ||
const NEVER_EXPIRES = 66048; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: NEVER_EXPIRES | ||
}); | ||
}, | ||
async removeUser(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
return reject({error: true, message: 'User does not exist.'}); | ||
} | ||
this._deleteObjectByDN(userObject.dn).then(resp => { | ||
resolve(resp); | ||
}).catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, {error: true})); | ||
}); | ||
}); | ||
}); | ||
}, | ||
async enableUser(userName) { | ||
const ENABLED = 512; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: ENABLED | ||
}); | ||
}, | ||
} | ||
async disableUser(userName) { | ||
const DISABLED = 514; | ||
return this._userReplaceOperation(userName, { | ||
userAccountControl: DISABLED | ||
}); | ||
}, | ||
async moveUser(userName, location) { | ||
return new Promise(async (resolve, reject) => { | ||
location = parseLocation(location); | ||
this.findUser(userName) | ||
.then(userObject => { | ||
let oldDN = userObject.dn; | ||
let baseDN = String(this.config.baseDN).replace(/dc=/g, 'DC='); | ||
let newDN = `CN=${userObject.cn},${location}${baseDN}`; | ||
return this._modifyDN(oldDN, newDN); | ||
}) | ||
.then(result => { | ||
delete this._cache.users[userName]; | ||
resolve(result); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(err); | ||
}); | ||
}); | ||
}, | ||
async getUserLocation(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName) | ||
.then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
/* istanbul ignore next */ | ||
return reject({ error: true, message: 'User does not exist.' }); | ||
} | ||
let dn = userObject.dn; | ||
let left = String(dn) | ||
.replace(/DC=/g, 'dc=') | ||
.replace(/CN=/g, 'cn=') | ||
.replace(/OU=/g, 'ou=') | ||
.split(',dc=')[0]; | ||
let location = String(left) | ||
.split(',') | ||
.slice(1) | ||
.reverse() | ||
.join('/') | ||
.replace(/cn=/g, '!') | ||
.replace(/ou=/g, ''); | ||
return resolve(location); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
return reject(err); | ||
}); | ||
}); | ||
}, | ||
async unlockUser(userName) { | ||
return this._userReplaceOperation(userName, { | ||
lockoutTime: 0 | ||
}); | ||
}, | ||
async removeUser(userName) { | ||
return new Promise(async (resolve, reject) => { | ||
this.findUser(userName).then(userObject => { | ||
if (Object.keys(userObject).length < 1) { | ||
return reject({ error: true, message: 'User does not exist.' }); | ||
} | ||
this._deleteObjectByDN(userObject.dn) | ||
.then(resp => { | ||
resolve(resp); | ||
}) | ||
.catch(err => { | ||
/* istanbul ignore next */ | ||
reject(Object.assign(err, { error: true })); | ||
}); | ||
}); | ||
}); | ||
} | ||
}; |
@@ -13,24 +13,29 @@ const AD = require('../index.js'); | ||
test('throws on missing url', () => { | ||
expect(() => new AD({user, pass})) | ||
.toThrow('The following configuration is required: {url, user, pass}.'); | ||
expect(() => new AD({ user, pass })).toThrow( | ||
'The following configuration is required: {url, user, pass}.' | ||
); | ||
}); | ||
test('throws on incomplete url', () => { | ||
expect(() => new AD({url: '127.0.0.1', user, pass})) | ||
.toThrow('You must specify the protocol in the url, such as ldaps://127.0.0.1.'); | ||
expect(() => new AD({ url: '127.0.0.1', user, pass })).toThrow( | ||
'You must specify the protocol in the url, such as ldaps://127.0.0.1.' | ||
); | ||
}); | ||
test('throws on missing user', () => { | ||
expect(() => new AD({url, pass})) | ||
.toThrow('The following configuration is required: {url, user, pass}.'); | ||
expect(() => new AD({ url, pass })).toThrow( | ||
'The following configuration is required: {url, user, pass}.' | ||
); | ||
}); | ||
test('throws on incomplete user', () => { | ||
expect(() => new AD({url, user: 'mock', pass})) | ||
.toThrow('The user must include the fully qualified domain name, such as joe@acme.co.'); | ||
expect(() => new AD({ url, user: 'mock', pass })).toThrow( | ||
'The user must include the fully qualified domain name, such as joe@acme.co.' | ||
); | ||
}); | ||
test('throws on missing pass', () => { | ||
expect(() => new AD({url, user})) | ||
.toThrow('The following configuration is required: {url, user, pass}.'); | ||
expect(() => new AD({ url, user })).toThrow( | ||
'The following configuration is required: {url, user, pass}.' | ||
); | ||
}); |
@@ -7,84 +7,94 @@ const AD = require('../index.js'); | ||
beforeAll(async () => { | ||
try { | ||
await ad.group('Test Group 1').remove(); | ||
await ad.group('Test Group 2').remove(); | ||
} catch (e) {} | ||
try { | ||
await ad.group('Test Group 1').remove(); | ||
await ad.group('Test Group 2').remove(); | ||
} catch (e) {} | ||
}); | ||
test('group().add() should not throw', async () => { | ||
let result = await ad.group().add({ | ||
name: 'Test Group 1', | ||
location: '', | ||
description: 'This is test group 1.', | ||
}).catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
let result = await ad | ||
.group() | ||
.add({ | ||
name: 'Test Group 1', | ||
location: '', | ||
description: 'This is test group 1.' | ||
}) | ||
.catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
}); | ||
test('group().add() should return a group', async () => { | ||
let result = await ad.group().add({ | ||
name: 'Test Group 2', | ||
location: '!Builtin', | ||
description: 'This is test group 2.', | ||
}).catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
let result = await ad | ||
.group() | ||
.add({ | ||
name: 'Test Group 2', | ||
location: '!Builtin', | ||
description: 'This is test group 2.' | ||
}) | ||
.catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
}); | ||
test('group().get() should return all groups', async () => { | ||
try { | ||
let results = await ad.group().get(); | ||
expect(results.length).toBeGreaterThan(1); | ||
expect(results.filter(r => r.cn === 'Test Group 2').length).toBe(1); | ||
} | ||
catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let results = await ad.group().get(); | ||
expect(results.length).toBeGreaterThan(1); | ||
expect(results.filter(r => r.cn === 'Test Group 2').length).toBe(1); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('group(group).get() should find a single group by CN', async () => { | ||
let results = await ad.group('Test Group 2').get(); | ||
expect(results.description).toBe('This is test group 2.'); | ||
let results = await ad.group('Test Group 2').get(); | ||
expect(results.description).toBe('This is test group 2.'); | ||
}); | ||
test('group(group).get() should handle accept a suffix', async () => { | ||
let results = await ad.group('Test Group 2@' + config.domain).get(); | ||
expect(results.description).toBe('This is test group 2.'); | ||
let results = await ad.group('Test Group 2@' + config.domain).get(); | ||
expect(results.description).toBe('This is test group 2.'); | ||
}); | ||
test('group(group).exists() should return true for a given group', async () => { | ||
expect(await ad.group('Test Group 2').exists()).toBe(true); | ||
expect(await ad.group('Test Group 2').exists()).toBe(true); | ||
}); | ||
test('group(group).exists() should return false for a bs group', async () => { | ||
expect(await ad.group('stachelrodt').exists()).toBe(false); | ||
expect(await ad.group('stachelrodt').exists()).toBe(false); | ||
}); | ||
test('group(group).addUser(user) should add a user', async () => { | ||
let addResult = await ad.group('Test Group 2').addUser('Administrator').catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('Administrator').isMemberOf('Test Group 2'); | ||
expect(result).toBe(true); | ||
let addResult = await ad | ||
.group('Test Group 2') | ||
.addUser('Administrator') | ||
.catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('Administrator').isMemberOf('Test Group 2'); | ||
expect(result).toBe(true); | ||
}); | ||
test('group(group).removeUser(user) should remove a user', async () => { | ||
let addResult = await ad.group('Test Group 2').removeUser('Administrator').catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('Administrator').isMemberOf('Test Group 2'); | ||
expect(result).toBe(false); | ||
let addResult = await ad | ||
.group('Test Group 2') | ||
.removeUser('Administrator') | ||
.catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('Administrator').isMemberOf('Test Group 2'); | ||
expect(result).toBe(false); | ||
}); | ||
test('group(group).remove() should remove the group.', async () => { | ||
try { | ||
let result = await ad.group('Test Group 1').remove(); | ||
expect(result.success).toBe(true); | ||
let exists = await ad.group('Test Group 1').exists(); | ||
expect(exists).toBe(false); | ||
await ad.group('Test Group 2').remove(); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.group('Test Group 1').remove(); | ||
expect(result.success).toBe(true); | ||
let exists = await ad.group('Test Group 1').exists(); | ||
expect(exists).toBe(false); | ||
await ad.group('Test Group 2').remove(); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); |
@@ -7,252 +7,288 @@ const AD = require('../index.js'); | ||
beforeAll(async () => { | ||
try { | ||
await ad.user('test51').remove(); | ||
await ad.user('test52').remove(); | ||
} catch (e) {} | ||
try { | ||
await ad.user('test51').remove(); | ||
await ad.user('test52').remove(); | ||
await ad.user('test53').remove(); | ||
await ad.user('test54').remove(); | ||
} catch (e) {} | ||
}); | ||
test('user().add() should not throw', async () => { | ||
let result = await ad.user().add({ | ||
userName: 'test51', | ||
firstName: 'test', | ||
lastName: '51', | ||
commonName: 'Test User 51', | ||
email: 'test51@foobar.net', | ||
title: 'Director of Test Section 51', | ||
pass: 'SuperWord4567!' | ||
}).catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
let result = await ad | ||
.user() | ||
.add({ | ||
userName: 'test51', | ||
firstName: 'test', | ||
lastName: '51', | ||
commonName: 'Test User 51', | ||
email: 'test51@foobar.net', | ||
title: 'Director of Test Section 51', | ||
pass: 'SuperWord4567!' | ||
}) | ||
.catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
}); | ||
test('user().add() should return a user', async () => { | ||
let result = await ad.user().add({ | ||
userName: 'test52', | ||
firstName: 'test', | ||
lastName: '52', | ||
commonName: 'Test User 52', | ||
email: 'test52@foobar.net', | ||
title: 'Director of Test Section 52', | ||
pass: 'SuperWord4567!!!' | ||
}).catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
let result = await ad | ||
.user() | ||
.add({ | ||
userName: 'test52', | ||
firstName: 'test', | ||
lastName: '52', | ||
commonName: 'Test User 52', | ||
email: 'test52@foobar.net', | ||
title: 'Director of Test Section 52', | ||
pass: 'SuperWord4567!!!' | ||
}) | ||
.catch(err => { | ||
expect(err).not.toBeDefined(); | ||
}); | ||
}); | ||
test('user().add({firstName, lastName}) should infer a commonName', async () => { | ||
try { | ||
let result = await ad.user().add({ | ||
userName: 'test53', | ||
firstName: 'Test', | ||
lastName: '53', | ||
pass: 'SuperWord4567!!!' | ||
}); | ||
const user = await ad.user('test53').get(); | ||
expect(user.cn.toLowerCase()).toBe('test 53'); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user().add({ | ||
userName: 'test53', | ||
firstName: 'Test', | ||
lastName: '53', | ||
pass: 'SuperWord4567!!!' | ||
}); | ||
const user = await ad.user('test53').get(); | ||
expect(user.cn.toLowerCase()).toBe('test 53'); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user().get() should return all users', async () => { | ||
try { | ||
let results = await ad.user().get(); | ||
expect(results.length).toBeGreaterThan(1); | ||
expect(results.filter(r => r.sAMAccountName === 'test52').length).toBe(1); | ||
} | ||
catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let results = await ad.user().get(); | ||
expect(results.length).toBeGreaterThan(1); | ||
expect(results.filter(r => r.sAMAccountName === 'test52').length).toBe(1); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user().get() should filter by field', async () => { | ||
let results = await ad.user().get({ | ||
fields: ['givenName'] | ||
}); | ||
expect(results.filter(r => r.sAMAccountName !== undefined).length).toBe(0); | ||
expect(results.filter(r => r.givenName !== undefined).length).toBeGreaterThan(0); | ||
let results = await ad.user().get({ | ||
fields: ['givenName'] | ||
}); | ||
expect(results.filter(r => r.sAMAccountName !== undefined).length).toBe(0); | ||
expect(results.filter(r => r.givenName !== undefined).length).toBeGreaterThan( | ||
0 | ||
); | ||
}); | ||
test('user().get() should sort', async () => { | ||
let results = await ad.user().get({ | ||
sort: ['cn'], | ||
order: ['desc'] | ||
}); | ||
let idx51; | ||
let idx52; | ||
for (let i = 0; i < results.length; ++i) { | ||
if (results[i].cn === 'Test User 52') { | ||
idx52 = i; | ||
} | ||
if (results[i].cn === 'Test User 51') { | ||
idx51 = i; | ||
} | ||
} | ||
expect(idx51).not.toBeUndefined(); | ||
expect(idx52).not.toBeUndefined(); | ||
expect(idx52).toBeLessThan(idx51); | ||
let results = await ad.user().get({ | ||
sort: ['cn'], | ||
order: ['desc'] | ||
}); | ||
let idx51; | ||
let idx52; | ||
for (let i = 0; i < results.length; ++i) { | ||
if (results[i].cn === 'Test User 52') { | ||
idx52 = i; | ||
} | ||
if (results[i].cn === 'Test User 51') { | ||
idx51 = i; | ||
} | ||
} | ||
expect(idx51).not.toBeUndefined(); | ||
expect(idx52).not.toBeUndefined(); | ||
expect(idx52).toBeLessThan(idx51); | ||
}); | ||
test('user().get() should full text search', async () => { | ||
let results = await ad.user().get({ | ||
q: ['foobar'] | ||
}); | ||
expect(results.length).toBe(2); | ||
let results = await ad.user().get({ | ||
q: ['foobar'] | ||
}); | ||
expect(results.length).toBe(2); | ||
}); | ||
test('user().get() should filter values', async () => { | ||
let results = await ad.user().get({ | ||
filter: { | ||
cn: 'Test User 52' | ||
} | ||
}); | ||
expect(results.length).toBe(1); | ||
let results = await ad.user().get({ | ||
filter: { | ||
cn: 'Test User 52' | ||
} | ||
}); | ||
expect(results.length).toBe(1); | ||
}); | ||
test('user(user).get() should find a single user by sAMAccountName', async () => { | ||
let results = await ad.user('test52').get(); | ||
expect(results.sAMAccountName).toBe('test52'); | ||
let results = await ad.user('test52').get(); | ||
expect(results.sAMAccountName).toBe('test52'); | ||
}); | ||
test('user(user).get() should find a single user by userPrincipalName', async () => { | ||
let results = await ad.user('test51@' + config.domain).get(); | ||
expect(results.sAMAccountName).toBe('test51'); | ||
let results = await ad.user('test51@' + config.domain).get(); | ||
expect(results.sAMAccountName).toBe('test51'); | ||
}); | ||
test('user(user).get(opts) should take filter options', async () => { | ||
let results = await ad.user('test51').get({ | ||
fields: ['givenName'] | ||
}); | ||
expect(results.sAMAccountName).toBeUndefined(); | ||
expect(results.givenName).not.toBeUndefined(); | ||
let results = await ad.user('test51').get({ | ||
fields: ['givenName'] | ||
}); | ||
expect(results.sAMAccountName).toBeUndefined(); | ||
expect(results.givenName).not.toBeUndefined(); | ||
}); | ||
test('user(user).exists() should return true for a given user', async () => { | ||
expect(await ad.user('test51').exists()).toBe(true); | ||
expect(await ad.user('test51').exists()).toBe(true); | ||
}); | ||
test('user(user).exists() should return false for a bs user', async () => { | ||
expect(await ad.user('dskfdslkfjekfjeidj').exists()).toBe(false); | ||
expect(await ad.user('dskfdslkfjekfjeidj').exists()).toBe(false); | ||
}); | ||
test('user(user).addToGroup(group) should add a user', async () => { | ||
let addResult = await ad.user('test52').addToGroup('Administrators').catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('test52').isMemberOf('Administrators'); | ||
expect(result).toBe(true); | ||
let addResult = await ad | ||
.user('test52') | ||
.addToGroup('Administrators') | ||
.catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('test52').isMemberOf('Administrators'); | ||
expect(result).toBe(true); | ||
}); | ||
test('user(user).removeFromGroup(group) should remove a user', async () => { | ||
let addResult = await ad.user('test52').removeFromGroup('Administrators').catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('test52').isMemberOf('Administrators'); | ||
expect(result).toBe(false); | ||
let addResult = await ad | ||
.user('test52') | ||
.removeFromGroup('Administrators') | ||
.catch(err => { | ||
expect(err).toBeUndefined(); | ||
}); | ||
let result = await ad.user('test52').isMemberOf('Administrators'); | ||
expect(result).toBe(false); | ||
}); | ||
test('user(user).authenticate(pass) should test authentication.', async () => { | ||
try { | ||
let trueResult = await ad.user('test52').authenticate('SuperWord4567!!!'); | ||
expect(trueResult).toBe(true); | ||
let falseResult = await ad.user('test52').authenticate('jetlag'); | ||
expect(falseResult).toBe(false); | ||
} catch(err) { | ||
expect(err).toBeUndefined(); | ||
} | ||
try { | ||
let trueResult = await ad.user('test52').authenticate('SuperWord4567!!!'); | ||
expect(trueResult).toBe(true); | ||
let falseResult = await ad.user('test52').authenticate('jetlag'); | ||
expect(falseResult).toBe(false); | ||
} catch (err) { | ||
expect(err).toBeUndefined(); | ||
} | ||
}); | ||
test('user(user).password(pass) should change a password.', async () => { | ||
try { | ||
let result = await ad.user('test52').password('iSunMonkey23!'); | ||
let trueResult = await ad.user('test52').authenticate('iSunMonkey23!'); | ||
expect(trueResult).toBe(true); | ||
} catch(err) { | ||
expect(err).toBeUndefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').password('iSunMonkey23!'); | ||
let trueResult = await ad.user('test52').authenticate('iSunMonkey23!'); | ||
expect(trueResult).toBe(true); | ||
} catch (err) { | ||
expect(err).toBeUndefined(); | ||
} | ||
}); | ||
test('user(user).password(pass) should throw on a missing password.', async () => { | ||
try { | ||
let result = await ad.user('test52').password(); | ||
expect(false).toBe(true); | ||
} catch(err) { | ||
expect(err).toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').password(); | ||
expect(false).toBe(true); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
}); | ||
test('user(user).passwordNeverExpires() should not throw.', async () => { | ||
try { | ||
let result = await ad.user('test52').passwordNeverExpires(); | ||
expect(result.success).toBe(true); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').passwordNeverExpires(); | ||
expect(result.success).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).passwordExpires() should not throw.', async () => { | ||
try { | ||
let result = await ad.user('test52').passwordExpires(); | ||
expect(result.success).toBe(true); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').passwordExpires(); | ||
expect(result.success).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).enable() should not throw.', async () => { | ||
try { | ||
let result = await ad.user('test52').enable(); | ||
expect(result.success).toBe(true); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').enable(); | ||
expect(result.success).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).disable() should not throw.', async () => { | ||
try { | ||
let result = await ad.user('test52').disable(); | ||
expect(result.success).toBe(true); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').disable(); | ||
expect(result.success).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).move() should move the user.', async () => { | ||
try { | ||
let result = await ad.user('test52').move('!Builtin'); | ||
expect(result.success).toBe(true); | ||
let location = await ad.user('test52').location(); | ||
expect(location).toBe('!Builtin'); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').move('!Builtin'); | ||
expect(result.success).toBe(true); | ||
let location = await ad.user('test52').location(); | ||
expect(location).toBe('!Builtin'); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).move() should handle an invalid user.', async () => { | ||
try { | ||
let result = await ad.user('test52000').move('!Builtin'); | ||
} catch(err) { | ||
expect(err).toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52000').move('!Builtin'); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
}); | ||
test('user(user).unlock() should not throw.', async () => { | ||
try { | ||
let result = await ad.user('test52').unlock(); | ||
expect(result.success).toBe(true); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let result = await ad.user('test52').unlock(); | ||
expect(result.success).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).update({}) should update a user', async () => { | ||
try { | ||
let result = await ad.user('test53').update({ | ||
firstName: 'tester', | ||
lastName: '54', | ||
commonName: 'Test User 54', | ||
email: 'test54@foobar.net', | ||
title: 'Director of Test Section 54', | ||
password: 'SuperTesto54!!!', | ||
userName: 'test54', | ||
enabled: false | ||
}); | ||
exists = await ad.user('test54').exists(); | ||
expect(exists).toBe(true); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); | ||
test('user(user).remove() should remove the user.', async () => { | ||
try { | ||
let result = await ad.user('test52').remove(); | ||
expect(result.success).toBe(true); | ||
let exists = await ad.user('test52').exists(); | ||
expect(exists).toBe(false); | ||
} catch(err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
try { | ||
let exists = await ad.user('test53').exists(); | ||
if (exists === true) { | ||
await ad.user('test53').remove(); | ||
} | ||
expect(exists).toBe(false); | ||
await ad.user('test51').remove(); | ||
await ad.user('test54').remove(); | ||
} catch (err) { | ||
expect(err).not.toBeDefined(); | ||
} | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
70721
25
1970
563
4