/**
* @module flitter-auth/ldap/AsyncLdapConnection
*/
const ldap = require('ldap')
const ssha = require('ssha')
/**
* Asynchronous helper methods for the jdap.js library.
* @class
*/
class AsyncLdapConnection {
constructor(connection){
this.connection = connection
}
/**
* Bind the active connection to the specified DN with the password.
* @param {string} dn - the fully-qualified bind DN
* @param {string} password - the password to authenticate against
* @param {Array<ldap/Control>} controls - collection of controls to pass to ldap.js
* @returns {Promise<module:flitter-auth/ldap/AsyncLdapConnection~AsyncLdapConnection>} - resolves to this if successful
*/
bind(dn, password, controls){
return new Promise((resolve, reject) => {
this.connection.bind(dn, password, controls, (err) => {
if ( err ) reject(err)
else resolve(this)
})
})
}
/**
* Search for all objects in the specified base, covered by the filter, for the specified attributes.
* @param {string} base - fully-qualified base to search
* @param {string} filter - LDAP-compliant string to filter items
* @param {Array<string>} attributes - object attributes to fetch
* @returns {Promise<Array<object>>} - collection of matches
*/
search(base, filter, attributes){
return new Promise(((resolve, reject) => {
this.connection.search(base, {
scope: 'sub',
attributes,
filter,
}, (err, response) => {
if ( err ) reject (err)
else {
const entries = []
response.on('searchEntry', (en) => entries.push(en.object))
response.on('error', (e) => reject(e))
response.on('end', (r) => resolve(entries))
}
})
}))
}
/**
* Check if the specified attribute is equal to the value for the object with the fully-qualified object DN.
* @param {string} object - fully-qualified object DN
* @param {string} attribute - attribute to compare
* @param {*} value - value to match
* @returns {Promise<boolean>} - resolves true if the attribute matches the value
*/
compare(object, attribute, value){
return new Promise(((resolve, reject) => {
this.connection.compare(object, attribute, value, (err, matched) => {
if ( err ) reject(err)
else resolve(!!matched)
})
}))
}
/**
* Check if the specified value can authenticate the user with the fully-qualified DN.
* @param {string} object - fully-qualified DN of the user to bind
* @param {string} password - password to check
* @returns {Promise<boolean>} - true if authentication succeeds
*/
password_check(object, password){
return new Promise(resolve => {
this.connection.bind(object, password, [], (err) => {
resolve(!err)
})
})
}
/**
* Reset the userPassword of the specified bind object by SSHA hashing the specified password.
* @param {string} object - fully-qualified DN of the user to reset
* @param {string} password - the password to set
* @returns {Promise<void>}
*/
password_reset(object, password){
const change = new ldap.Change({
operation: 'replace',
modification: {
userPassword: ssha.create(password),
}
})
return new Promise((resolve, reject) => {
this.connection.modify(object, change, err => {
if ( err ) reject(err)
else resolve()
})
})
}
/**
* Modify the values of the specified object.
* @param {string} object - fully qualified DN of the object to be modified
* @param {object} entry - collection of key-value pairs to set on the object
* @returns {Promise<void>}
*/
modify(object, entry){
const change = new ldap.Change({
operation: 'replace',
modification: entry,
})
return new Promise((resolve, reject) => {
this.connection.modify(object, change, err => {
if ( err ) reject(err)
else resolve()
})
})
}
/**
* Similar to modify, add the following attributes to the specified object. If they don't exist,
* the attributes will be created. If they do, the specified values will be appended to the existing
* attributes.
* @param {string} object - fully qualified DN of the object to be modified
* @param {object} entry - collection of key-value pairs to be set on the object
* @returns {Promise<void>}
*/
add_to(object, entry){
const change = new ldap.Change({
operation: 'add',
modification: entry,
})
return new Promise((resolve, reject) => {
this.connection.modify(object, change, err => {
if ( err ) reject(err)
else resolve()
})
})
}
/**
* Create the object with the specified DN with the specified attributes.
* @param {string} object - fully-qualified DN of the object to be created
* @param {object} entry - collection of attribute-value pairs for the object
* @returns {Promise<void>}
*/
add(object, entry){
return new Promise((resolve, reject) => {
this.connection.add(object, entry, (err) => {
if ( err ) reject(err)
else resolve()
})
})
}
/**
* Delete the object with the specified DN.
* @param {string} object - fully-qualified DN of the object to be deleted
* @returns {Promise<void>}
*/
delete(object){
return new Promise((resolve, reject) => {
this.connection.del(object, (err) => {
if ( err ) reject(err)
else resolve()
})
})
}
}
module.exports = exports = AsyncLdapConnection