Skip to content

Instantly share code, notes, and snippets.

@tvaliasek
Last active March 30, 2022 18:40
Show Gist options
  • Save tvaliasek/90bbd9e4759e23949c57699128e0d2b7 to your computer and use it in GitHub Desktop.
Save tvaliasek/90bbd9e4759e23949c57699128e0d2b7 to your computer and use it in GitHub Desktop.
node activedirectory - check if user is locked, disabled or expired and get date of last password change
/*
To get some attributes from AD, you need to set the correct permissions on the container
or object you are trying to read. (e.g., pwdLastSet, userAccountControl)
*/
const ActiveDirectory = require('activedirectory')
/* eslint-disable-next-line no-unused-vars */
const SCRIPT_FLAG = 0
const ACCOUNTDISABLE_FLAG = 1
/* eslint-disable-next-line no-unused-vars */
const HOMEDIR_REQUIRED_FLAG = 3
/* eslint-disable-next-line no-unused-vars */
const LOCKOUT_FLAG = 4
/* eslint-disable-next-line no-unused-vars */
const PASSWD_NOTREQD_FLAG = 5
/* eslint-disable-next-line no-unused-vars */
const PASSWD_CANT_CHANGE_FLAG = 6
/* eslint-disable-next-line no-unused-vars */
const ENCRYPTED_TEXT_PWD_ALLOWED_FLAG = 7
/* eslint-disable-next-line no-unused-vars */
const TEMP_DUPLICATE_ACCOUNT_FLAG = 8
/* eslint-disable-next-line no-unused-vars */
const NORMAL_ACCOUNT_FLAG = 9
/* eslint-disable-next-line no-unused-vars */
const INTERDOMAIN_TRUST_ACCOUNT_FLAG = 11
/* eslint-disable-next-line no-unused-vars */
const WORKSTATION_TRUST_ACCOUNT_FLAG = 12
/* eslint-disable-next-line no-unused-vars */
const SERVER_TRUST_ACCOUNT_FLAG = 13
/* eslint-disable-next-line no-unused-vars */
const DONT_EXPIRE_PASSWORD_FLAG = 16
/* eslint-disable-next-line no-unused-vars */
const MNS_LOGON_ACCOUNT_FLAG = 17
/* eslint-disable-next-line no-unused-vars */
const SMARTCARD_REQUIRED_FLAG = 18
/* eslint-disable-next-line no-unused-vars */
const TRUSTED_FOR_DELEGATION_FLAG = 19
/* eslint-disable-next-line no-unused-vars */
const NOT_DELEGATED_FLAG = 20
/* eslint-disable-next-line no-unused-vars */
const USE_DES_KEY_ONLY_FLAG = 21
/* eslint-disable-next-line no-unused-vars */
const DONT_REQ_PREAUTH_FLAG = 22
/* eslint-disable-next-line no-unused-vars */
const PASSWORD_EXPIRED_FLAG = 23
/* eslint-disable-next-line no-unused-vars */
const TRUSTED_TO_AUTH_FOR_DELEGATION_FLAG = 24
/* eslint-disable-next-line no-unused-vars */
const PARTIAL_SECRETS_ACCOUNT_FLAG = 26
class ADClient {
constructor(ldapOptions) {
this.ldapOptions = ldapOptions
this.adClient = new ActiveDirectory(this.ldapOptions)
}
getUser (username, retrieveAttributes) {
const attributes = (retrieveAttributes !== undefined && retrieveAttributes !== null && Array.isArray(retrieveAttributes) && retrieveAttributes.length > 0) ? retrieveAttributes : ['*']
const options = {
attributes: attributes
}
return new Promise((resolve, reject) => {
this.adClient.findUser(options, username, false, function (err, user) {
if (err) {
return reject(err)
}
if (!user) {
return reject(new Error('Nothing found. (ErrNothing)'))
}
resolve(user)
})
})
}
ldapTimestampToDate (ADtimestamp) {
return new Date(ADtimestamp / 1e4 - 1.16444736e13)
}
matchesBitFlag(value, flag) {
let bitArray = (parseInt(value)).toString(2).split('').reverse()
if (bitArray.length - 1 >= flag) {
return (bitArray[flag] === '1')
}
return false
}
async getAccountCheckData (username) {
let user = await this.getUser(username, ['sAMAccountName', 'pwdLastSet', 'badPwdCount', 'badPasswordTime', 'accountExpires', 'lockoutTime', 'userAccountControl'])
let expireDate = (user.accountExpires === '0' || user.accountExpires === '9223372036854775807') ? null : this.ldapTimestampToDate(user.accountExpires)
let parsedUser = {
username: user.sAMAccountName,
pwd_last_change: this.ldapTimestampToDate(user.pwdLastSet),
pwd_bad_count: parseInt(user.badPwdCount),
pwd_bad_last_attempt: this.ldapTimestampToDate(user.badPasswordTime),
expiry_date: expireDate,
lockout_time: (user.lockoutTime === '0') ? null : this.ldapTimestampToDate(user.lockoutTime),
account_locked: (user.lockoutTime !== '0'),
account_disabled: this.matchesBitFlag(user.userAccountControl, ACCOUNTDISABLE_FLAG),
account_expired: (expireDate !== null && expireDate > new Date())
}
return parsedUser
}
}
module.exports = ADClient
const ADClient = require('./active-directory.js')
const fs = require('fs')
const sslCerts = {
ca: fs.readFileSync('./ca.pem')
}
const ldapOptions = {
url: 'ldaps://dc.contoso.cz:636',
bindDN: 'CN=LDAP Account,OU=.Services,OU=Users,OU=CONTOSO,DC=Contoso,DC=cz',
bindCredentials: 'password',
tlsOptions: { ca: [sslCerts.ca], rejectUnauthorized: false },
baseDN: 'OU=Users,OU=CONTOSO,DC=Contoso,DC=cz'
}
const adClient = new ADClient(ldapOptions)
adClient.getAccountCheckData('test.account').then((checkData) => {
console.log(checkData)
}).catch((error) => {
console.error(error.message)
})
/*
output:
{
username: 'test.account',
pwd_last_change: 2018-05-07T06:35:34.208Z,
pwd_bad_count: 0,
pwd_bad_last_attempt: 2018-07-26T11:43:41.178Z,
expiry_date: null,
lockout_time: null,
account_locked: false,
account_disabled: false,
account_expired: false
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment