Last active
March 30, 2022 18:40
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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