diff --git a/lib/accesskey.js b/lib/accesskey.js new file mode 100644 index 0000000..1128aeb --- /dev/null +++ b/lib/accesskey.js @@ -0,0 +1,205 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright 2025 Edgecast Cloud LLC. + */ + +/* + * Routines for generating and validating secret access keys. + */ + +var crypto = require('crypto'); +var crc32 = require('crc').buffer.crc32; +var assert = require('assert-plus'); + +var TO_B64_REG = new RegExp('[+/=]', 'g'); +var FROM_B64_REG = new RegExp('[-_]', 'g'); + +var DEFAULT_PREFIX = 'tdc_'; +var DEFAULT_BYTE_LENGTH = 32; + +// Don't have base64url encoded Buffers until Node v14 +function toBase64url(input) { + return input + .toString('base64') + .replace(TO_B64_REG, function (c) { + if (c === '+') { + return '-'; + } + if (c === '/') { + return '_'; + } + if (c === '=') { + return ''; + } + return null; + }); +} + +function fromBase64url(input) { + var base64 = input.replace(FROM_B64_REG, function (c) { + if (c === '-') { + return '+'; + } + if (c === '_') { + return '/'; + } + return null; + }); + + // Restore padding + while (base64.length % 4 !== 0) { + base64 += '='; + } + + // Buffer.from not available until Node v5 + if (typeof (Buffer.from) === 'function') { + return Buffer.from(base64, 'base64'); + } + + return new Buffer(base64, 'base64'); +} + +/* + * Node v0.10 didn't yet have `Buffer.alloc()` and `new Buffer()` was the only + * option until v5. However, using `new Buffer()` in newer Node versions emits a + * deprecation message with a security warning so `Buffer.alloc` is used where + * available. + */ +function newBuffer(size) { + assert.number(size, 'size'); + if (typeof (Buffer.alloc) === 'function') { + return Buffer.alloc(size); + } + return new Buffer(size); +} + +/** + * Generate a random secret access key inspired by suggestions from Github's + * Secret Scanning Partner Program[0] and how they structure their keys[1]: + * [0] https://i.no.de/c12f50d544eececf + * [1] https://i.no.de/5a4e8cea87c0a873 + * + * Instead of using Base62 as Github does, base64url encoding is used instead. + * + * Keys generated from this function have: + * - A uniquely defined prefix (e.g. "tdc_" for "Triton DataCenter") + * - High entropy random strings (32 random bytes from node crypto) + * - A 32-bit crc checksum (to validate token structure) + * + * An example key: + * + * tdc_SU4xWXL-HzrMIDM_A8GH94sl-uc-aX8mqsEMiK4JSVdAGyjH + * + * +--------+--------------------------------------------+--------+ + * | PREFIX | RANDOM BYTES | CRC32 | + * +--------+--------------------------------------------+--------+ + * | tdc_ | SU4xWXL-HzrMIDM_A8GH94sl-uc-aX8mqsEMiK4JSV | dAGyjH |---+ + * +--------+--------------------------------------------+--------+ | + * | BASE64 URL ENCODED | | + * +--------+--------------------------------------------+--------+ | + * | CRC32 coverage (PREFIX + RANDOM BYTES) | <----------+ + * +-----------------------------------------------------+ + * + * @param {String} prefix string for the token. + * @param {Number} byte count to randomly generate. + * @param {Function} callback of the form fn(err, key). + * @throws {TypeError} on bad input. + */ +function generate(prefix, bytes, done) { + assert.string(prefix, 'prefix'); + assert.number(bytes, 'bytes'); + assert.func(done, 'done'); + + crypto.randomBytes(bytes, function generateBytes(err, randBytes) { + if (err) { + done(err); + return; + } + + // Create a buffer containing the prefix and random bytes + var prefixBuf = newBuffer(prefix.length); + prefixBuf.write(prefix); + + var tokenBuf = Buffer.concat([prefixBuf, randBytes]); + + // Obtain CRC32 from prefix + random bytes + var crc = crc32(tokenBuf); + + // Write the CRC32 into a new buffer encoded as a 32-bit signed int + var crcBuf = newBuffer(4); + + // Some anicent versions of Node return undefined for writeInt32LE (at + // least v0.10.48 but not v0.12.14) + var wrote = crcBuf.writeInt32LE(crc, 0); + if (wrote !== undefined && wrote !== 4) { + done(new Error('Failed to generate access key')); + return; + } + + // Base64 URL the encode random bytes + CRC32, prepend the prefix + var key = prefix + toBase64url(Buffer.concat([randBytes, crcBuf])); + + done(null, key); + return; + }); +} + +/** + * Validates the structure of a secret access key. Does NOT validate that the + * token is active and valid for authentication purposes it only validates that + * the token structure is correct. This function can be used to toss out a + * garbage token before attempting to look it up against UFDS. + * + * @param {String} prefix string for the token. + * @param {Number} byte count expected in the token. + * @param {String} secret key string. + * @throws {TypeError} on bad input. + */ +function validate(prefix, bytes, secret) { + assert.string(prefix, 'prefix'); + assert.number(bytes, 'bytes'); + assert.string(secret, 'secret'); + + if (secret.indexOf(prefix) !== 0) { + return false; + } + + // Remove prefix from the secret + var body = secret.slice(prefix.length); + + // Base64 URL decode the body containing random bytes + CRC32 + var parts = fromBase64url(body); + + // Must contain the expected number of random bytes + 4 bytes for the CRC32 + if (parts.length !== (bytes + 4)) { + return false; + } + + // Create a buffer containg the prefix + var prefixBuf = newBuffer(prefix.length); + prefixBuf.write(secret.slice(0, prefix.length)); + + // Create a buffer containing the random bytes + var randBytesBuf = parts.slice(0, -4); + + // Create a buffer from the CRC32 at the end of the secret + var crc32Buf = parts.slice(-4); + + // Create a new buffer containing the prefix + random bytes + var tokenBuf = Buffer.concat([prefixBuf, randBytesBuf]); + + // Recompute CRC32 and compare with the CRC32 obtained from the secret + return (crc32(tokenBuf) === crc32Buf.readInt32LE(0)); +} + +module.exports = { + generate: generate, + validate: validate, + DEFAULT_PREFIX: DEFAULT_PREFIX, + DEFAULT_BYTE_LENGTH: DEFAULT_BYTE_LENGTH +}; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index f808bf0..ac21971 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,6 +6,7 @@ /* * Copyright 2020 Joyent, Inc. + * Copyright 2025 Edgecast Cloud LLC. */ /* @@ -29,8 +30,7 @@ var vasync = require('vasync'); var sshpk = require('sshpk'); var cache = require('./cache'); - - +var accesskey = require('./accesskey'); // --- Globals @@ -3068,19 +3068,47 @@ UFDS.prototype.listResolvers = function listResolvers(region, cb, noCache) { * Adds a new AccessKey to a given user record. * * The AccessKeyId and AccessKeySecret will be generated. + * This method will return the full key as processed by UFDS. * - * This method will return you the full key as processed by UFDS. + * To preserve compatibiliy with any existing clients that were using this + * method before adding the `attrs` argument was added and to align with + * conventions pre-existing in this class, this method may be called with + * any of the following signatures: * - * @param {Object} user the user record you got from getUser. + * addAccessKey(user, cb); + * addAccessKey(user, attrs, cb); + * addAccessKey(user, account, cb); + * addAccessKey(user, account, attrs, cb); + * + * @param {Object|String} the user record you got from getUser. * @param {String} (Optional) account uuid for a customer sub-user + * @param {String} (Optional) attrs for the access key * @param {Function} callback of the form fn(err, key). * @throws {TypeError} on bad input. */ -UFDS.prototype.addAccessKey = function addAccessKey(user, account, cb) { +UFDS.prototype.addAccessKey = function addAccessKey(user, account, attrs, cb) { + var argc = arguments.length; + + // addAccessKey(user, cb) if (typeof (account) === 'function') { cb = account; account = null; + attrs = {}; + } else if (typeof (attrs) === 'function') { + cb = attrs; + // addAccessKey(user, attrs, cb) + if (argc === 3 && account && typeof (account) !== 'string') { + attrs = account; + account = null; + // addAccessKey(user, account, cb) + } else { + attrs = {}; + } + } else if (argc < 4) { + attrs = {}; } + // addAccessKey(user, account, attrs, cb) + if (typeof (user) !== 'string') { assert.object(user, 'user'); if (user.account) { @@ -3094,46 +3122,83 @@ UFDS.prototype.addAccessKey = function addAccessKey(user, account, cb) { var self = this; - function _addAccKey(init_err, usr) { - if (init_err) { - cb(init_err); - return; - } + vasync.waterfall([ - var entry = { - objectclass: 'accesskey', - created: Date.now() - }; + function getUserObject(next) { + if (typeof (user) === 'object') { + next(null, user); + } else { + self.getUser(user, account, next); + } + }, - try { - var accessKeyId = crypto.randomBytes(16).toString('hex'); - var accessKeySecret = crypto.randomBytes(32).toString('hex'); - entry.accesskeyid = accessKeyId; - entry.accesskeysecret = accessKeySecret; - } catch (e) { - cb(new InternalError(e.message)); - return; - } + function generateSecretKey(user, next) { + const prefix = accesskey.DEFAULT_PREFIX; + const bytes = accesskey.DEFAULT_BYTE_LENGTH; + accesskey.generate(prefix, bytes, function (err, secret) { + var errMsg = 'Failed to generate accesskeysecret'; + if (err) { + self.log.error(errMsg, {err: err}); + next(new InternalError(err.message)); + return; + } + if (!accesskey.validate(prefix, bytes, secret)) { + errMsg = 'Generated accesskeysecret failed validation'; + self.log.error(errMsg, {secret: secret}); + next(new InternalError(errMsg)); + return; + } + next(null, {user: user, secret: secret}); + return; + }); - var dn = (account) ? - sprintf(SUBUSER_ACCESS_KEY_FMT, - entry.accesskeyid, usr.uuid, account) : - sprintf(USER_ACCESS_KEY_FMT, entry.accesskeyid, usr.uuid); + }, - self.add(dn, entry, function (err) { - if (err) { - cb(translateError(err)); - return; + function generateId(context, next) { + crypto.randomBytes(16, function (err, id) { + if (err) { + var errMsg = 'Failed to generate accesskeyid'; + self.log.error(errMsg, {err: err}); + next(new InternalError(errMsg)); + } else { + context.id = id.toString('hex'); + next(null, context); + } + }); + }, + + function addEntry(context, next) { + var entry = clone(attrs); + entry.objectclass = 'accesskey'; + entry.updated = entry.created = Date.now(); + + if (!entry.status) { + entry.status = 'Active'; } - self.getAccessKey(usr, entry.accesskeyid, account, cb); - }); - } - if (typeof (user) === 'object') { - _addAccKey(null, user); - } else { - this.getUser(user, account, _addAccKey); - } + entry.accesskeyid = context.id; + entry.accesskeysecret = context.secret; + + var userUuid = context.user.uuid; + var dn = (account) ? + sprintf(SUBUSER_ACCESS_KEY_FMT, + entry.accesskeyid, userUuid, account) : + sprintf(USER_ACCESS_KEY_FMT, entry.accesskeyid, userUuid); + + self.add(dn, entry, function (err) { + if (err) { + next(translateError(err)); + } else { + next(null, context); + } + }); + }, + + function returnAccessKey(context, next) { + self.getAccessKey(context.user, context.id, account, next); + } + + ], cb); }; @@ -3226,25 +3291,92 @@ function listAccessKeys(user, account, cb, noCache) { cb = once(cb); var self = this; - function _accKeys(err, usr) { - if (err) { - cb(err); - return; + + vasync.waterfall([ + function getFullUserObject(next) { + if (typeof (user) === 'object') { + next(null, user); + } else { + self.getUser(user, account, next); + } + }, + function searchForKeys(user, next) { + var opts = { + scope: 'one', + filter: '(objectclass=accesskey)' + }; + self.search(user.dn, opts, function (err, entries) { + if (err) { + next(err); + } else { + + // AccessKeys created before UFDS v7.5.0 may not have status + // or created fields. Patch those fields in here so clients + // can rely on them being defined. + var keys = entries.map(function _mapEntries(entry) { + if (!entry.status) { + entry.status = 'Inactive'; + } + + if (!entry.updated) { + entry.updated = entry.created; + } + + return entry; + }); + + next(null, keys); + } + }, noCache); } - var opts = { - scope: 'one', - filter: '(objectclass=accesskey)' - }; - self.search(usr.dn, opts, cb, noCache); - } + ], cb); +}; - if (typeof (user) === 'object') { - _accKeys(null, user); - } else { - self.getUser(user, account, _accKeys); +/** + * Convenience function to load all the Active access keys for a given user. + * + * @param {Object} user the user you got from getUser. + * @param {String} (Optional) account uuid for a customer sub-user + * @param {Function} callback of the form fn(err, keys). + * @param {noCache} (Optional) boolean to force a full directory lookup. + * @throws {TypeError} on bad input. + */ +UFDS.prototype.listActiveAccessKeys = +function listActiveAccessKeys(user, account, cb, noCache) { + if (typeof (account) === 'function') { + cb = account; + account = null; } -}; + if (typeof (user) !== 'string') { + assert.object(user, 'user'); + if (user.account) { + account = user.account; + } + } + assert.func(cb, 'callback'); + account = workAroundUnderspecification(account, 'account'); + cb = once(cb); + + var self = this; + + vasync.waterfall([ + function getFullUserObject(next) { + if (typeof (user) === 'object') { + next(null, user); + } else { + self.getUser(user, account, next); + } + }, + function searchForKeys(user, next) { + var opts = { + scope: 'one', + filter: '(&(objectclass=accesskey)(status=Active))' + }; + self.search(user.dn, opts, next, noCache); + } + ], cb); +}; /** * Deletes an AccessKey under a user. @@ -3311,6 +3443,171 @@ function deleteAccessKey(user, accesskey, account, cb) { } }; +/** + * Updates an AccessKey under a user. + * + * This method will return you the full key as processed by UFDS. + * + * This method may be called with any of the following signatures: + * + * updateAccessKey(user, accesskey, cb); + * updateAccessKey(user, account, accesskey, cb); + * + * @param {Object} user record from getUser. + * @param {String} (Optional) account uuid for a customer sub-user. + * @param {Object} accesskey object must contain at least an accesskeyid + * property and the attributes you wish to update. + * You may pass in a null value to delete an attr. + * @param {Function} callback of the form fn(err, key). + * @throws {TypeError} on bad input. + */ +UFDS.prototype.updateAccessKey = +function updateAccessKey(user, account, accesskey, cb) { + + // updateAccessKey(user, accesskey, cb) + if (typeof (account) === 'object' && typeof accesskey === 'function') { + cb = accesskey; + accesskey = account; + account = null; + } + // updateAccessKey(user, account, accesskey, cb); + + if (typeof (user) !== 'string') { + assert.object(user, 'user'); + if (user.account) { + account = user.account; + } + } + assert.func(cb, 'callback'); + account = workAroundUnderspecification(account, 'account'); + + cb = once(cb); + + var self = this; + + vasync.waterfall([ + + function getFullUserObject(next) { + if (typeof (user) === 'object') { + next(null, user); + } else { + self.getUser(user, account, next); + } + }, + + function getFullAccessKey(user, next) { + if (typeof (accesskey) === 'object' && + typeof (accesskey.dn) === 'string') { + next(null, { + user: user, + accesskey: accesskey + }); + return; + } + + if (typeof (accesskey.accesskeyid) !== 'string') { + var errMsg = 'accesskeyid must be a string'; + next(InvalidArgumentError(errMsg)); + return; + } + + self.getAccessKey(user, accesskey.accesskeyid, account, + function (err, keyObj) { + if (err) { + next(err); + } else { + next(null, { + user: user, + accesskey: keyObj + }); + } + }); + }, + + function UpdateAccessKey(context, next) { + assert.string(context.user.dn, 'userDn'); + assert.string(context.accesskey.dn, 'accesskeyDn'); + var userDn = context.user.dn; + var accesskeyDn = context.accesskey.dn; + + if (!ldap.parseDN(userDn).parentOf(accesskeyDn)) { + var errMsg = accesskeyDn + ' not a child of ' + userDn; + next(new NotAuthorizedError(errMsg)); + return; + } + + var changes = []; + var ignoreAttrs = [ + 'dn', + 'controls', + 'accesskeyid', + 'accesskeysecret', + 'created', + 'objectclass', + 'updated' + ]; + + Object.keys(accesskey).forEach(function _keys(key) { + var change = {modification: {}}; + + // Nothing to do + if (ignoreAttrs.indexOf(key) !== -1 || + context.accesskey[key] === accesskey[key]) { + return; + } + + if (accesskey[key] === null) { + change.type = 'delete'; + change.modification[key] = []; + changes.push(change); + return; + } + + if (context.accesskey.hasOwnProperty(key)) { + change.type = 'replace'; + change.modification[key] = accesskey[key]; + changes.push(change); + return; + } + + change.type = 'add'; + change.modification[key] = accesskey[key]; + changes.push(change); + return; + }); + + if (changes.length) { + changes.push({ + type: 'replace', + modification: { + updated: Date.now() + } + }); + } + + self.modify(accesskeyDn, changes, function _modify(err) { + if (err) { + next(err); + } else { + next(null, context); + } + }); + }, + + function returnAccessKey(context, next) { + var accesskeyid = context.accesskey.accesskeyid; + self.getAccessKey(context.user, accesskeyid, account, + function _getAccessKey(err, key) { + if (err) { + next(err); + } else { + next(null, key); + } + }); + } + ], cb); +}; + /** * Low-level API to wrap up UFDS add operations. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6bdf757 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1591 @@ +{ + "name": "ufds", + "version": "1.9.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha512-6i37w/+EhlWlGUJff3T/Q8u1RGmP5wgbiwYnOnbOqvtrPxT63/sYFyP9RcpxtxGymtfA075IvmOnL7ycNOWl3w==" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha512-u1L0ZLywRziOVjUhRxI0Qg9G+4RnFB9H/Rq40YWn0dieDgO7vAYeJz6jKAO6t/aruzlDFLAPkQTT87e+f8Imaw==" + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==" + }, + "async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-equal": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.2.tgz", + "integrity": "sha512-4hr0gS7+NK47X6WbA/okVFrN5qGh3WLT7N3hMRv7+hlkXnbUIdU2u05n6r0RQv6cq6xke06nVl70r0NW0WM2OQ==", + "dev": true + }, + "bunker": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/bunker/-/bunker-0.1.2.tgz", + "integrity": "sha512-YnahkcXBNT522S46k5LUA9P18lzvgkunbMl0qIJQ8oeRMQ+dAg3YI3k32q5TnO+AAUErFHO6R768To6jslgYmQ==", + "dev": true, + "requires": { + "burrito": ">=0.2.5 <0.3" + } + }, + "bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "burrito": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/burrito/-/burrito-0.2.12.tgz", + "integrity": "sha512-ZhhT5iVTAgzQ+s8rily7m45Swxe/cU3dVCHTzqmHVWD/cc0Ds3W4Q4MExbkevY+fm0Me3lEwpehIy6TH7p+ehw==", + "dev": true, + "requires": { + "traverse": "~0.5.1", + "uglify-js": "~1.1.1" + }, + "dependencies": { + "traverse": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.5.2.tgz", + "integrity": "sha512-PUBVcfB3RqgLpzgTRGNiqK4duqrDbgGa1bobbUtzUwLiBNAjZ7vd5eCOdBxqZ/Fgezagr9o69IxP2fZp41RGFA==", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==", + "dev": true + }, + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha512-g62n3Kb9cszeZvmvBUqP/dsEJD/+80pDA8u8KqHnAPrVnQ2Je9rVV6opxkhuWCd1kCn2gOibzDKxCtBvD3q5kA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "crc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-0.2.0.tgz", + "integrity": "sha512-LFlOXOW6KT46bjpUevoixE6UQVdm9wMwCrR4JHxg4LJ+9COF7efwTdVMRXrSlNXYmUQgtAcHsWa0VgKBiQZmMQ==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + } + } + }, + "data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dev": true, + "requires": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "difflet": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/difflet/-/difflet-0.2.6.tgz", + "integrity": "sha512-ruldDDRmY1t678UOAJBng6sL77f62SqjHj0498YC0EJhxIe2yKkqJn2qEchwG3eU/dqJ/RxPZkAnYjePS4pDCw==", + "dev": true, + "requires": { + "charm": "0.1.x", + "deep-is": "0.1.x", + "traverse": "0.6.x" + } + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "requires": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + } + }, + "extsprintf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha512-T3PYC6HucmF4OfunfZb5d1nRvTSvWYhsr/Og33HANcCuCtGPUtWVyt/tTs8SU9sR0SGh5Z/xQCuX/D72ph2H+A==" + }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "requires": { + "is-callable": "^1.2.7" + } + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + } + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, + "has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + } + }, + "is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "requires": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } + }, + "is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "requires": { + "has-bigints": "^1.0.2" + } + }, + "is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + } + }, + "is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "requires": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } + }, + "is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true + }, + "is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + } + }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.16" + } + }, + "is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true + }, + "is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "ldap-filter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha512-HDnDRNY/z0E3qljSjDWtu7xXCUdiXzwadz7m1jIwl3XHhPMrqUyurOd32YWH5IZ3zZMP4PrG7gKdRIB2uZHKGA==", + "requires": { + "assert-plus": "0.1.5" + }, + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==" + } + } + }, + "ldapjs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", + "integrity": "sha512-Y8fRYFXOWWKBs9I2QRNXb0FncDcG0Gjc7YU6XI3kIV9hLMRaFKH5QHi2TZeB7FrpgXuabOgL0GdYALbssHxH5Q==", + "requires": { + "asn1": "0.2.3", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "bunyan": "^1.8.3", + "dashdash": "^1.14.0", + "dtrace-provider": "~0.8", + "ldap-filter": "0.2.2", + "once": "^1.4.0", + "vasync": "^1.6.4", + "verror": "^1.8.1" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "vasync": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha512-3oQMomVgQgHzNe5iKuT8PGOhMCQcg1wfh00Nh/Kl39ERdTlw/uNS7kbrhEraDMDKWHdDdc0iBFahPEd/Ft2b+A==", + "requires": { + "verror": "1.6.0" + }, + "dependencies": { + "verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha512-bIOaZx4+Bf6a7sIORfmYnyKLDLk/lhVym6rjYlq+vkitYKnhFmUpmPpDTCltWFrUTlGKs6sCeoDWfMA0oOOneA==", + "requires": { + "extsprintf": "1.2.0" + } + } + } + } + } + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==" + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "optional": true + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", + "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "optional": true + }, + "nodeunit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/nodeunit/-/nodeunit-0.9.1.tgz", + "integrity": "sha512-rYM7lWZRkjgvGAArx32B/iSQqXXQGSSyqix7ZkAaVG0DqM9VF2tRWoUfF54qG/1Td7Kxc241d7ZncJNAC2sfnQ==", + "dev": true, + "requires": { + "tap": "^0.7.1" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true + }, + "object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "optional": true + }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" + }, + "reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + } + }, + "regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + } + }, + "restify-errors": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-3.1.0.tgz", + "integrity": "sha512-4RDQs4zirMPXH03y5LKIFoAs+LvO9HTd5Ig4KfD5h4yRtTC5aWK/F2L1g9O2CSjTsgNIc+d0ib0f1rSob3FjNg==", + "requires": { + "assert-plus": "^0.2.0", + "lodash": "^3.10.1", + "verror": "^1.6.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "runforcover": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/runforcover/-/runforcover-0.0.2.tgz", + "integrity": "sha512-yarCIK2HcAOadqnKW419+FA38qpWDCKcOr5RZU+jnyLL/hn3No9BHZY+YJDEzvQ0k8Oyl7ffLjZv9ZUxvyKoLQ==", + "dev": true, + "requires": { + "bunker": "0.1.X" + } + }, + "safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + } + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + } + }, + "safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, + "set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + } + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + } + } + }, + "stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + } + }, + "string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "tap": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/tap/-/tap-0.7.1.tgz", + "integrity": "sha512-Dh7fyYFJzj9VZP/Oa79kFWeq2KxPwjCNHNWc0mXJTqUq7PKLrhg7d/mR748Bty9TKHsbMxpFLHn1YJZVjxJaug==", + "dev": true, + "requires": { + "buffer-equal": "~0.0.0", + "deep-equal": "^1.0.0", + "difflet": "~0.2.0", + "glob": "^4.3.5", + "inherits": "*", + "mkdirp": "^0.5.0", + "nopt": "^3.0.1", + "runforcover": "~0.0.2", + "slide": "*", + "yamlish": "*" + }, + "dependencies": { + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha512-I0rTWUKSZKxPSIAIaqhSXTM/DiII6wame+rEC3cFA5Lqmr9YmdL7z6Hj9+bdWtTvoY1Su4/OiMLmb37Y7JzvJQ==", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha512-jQo6o1qSVLEWaw3l+bwYA2X0uLuK2KjNh2wjgO7Q/9UJnXr1Q3yQKR8BI0/Bt/rPg75e6SMW4hW/6cBHVTZUjA==", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + } + } + }, + "traverse": { + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", + "dev": true, + "requires": { + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + } + }, + "typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + } + }, + "typedarray.prototype.slice": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" + } + }, + "uglify-js": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.1.1.tgz", + "integrity": "sha512-YYY9Dle1leC+btgrHnAR05eq0aRdcPJsXlYYD+SYw2lqc5HFuFNHg3wWEW4SNE0iXXEUl0fz43gTQ3r1YK76rg==", + "dev": true + }, + "unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vasync": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.3.tgz", + "integrity": "sha512-yeYyCFquU8aXjAZwKfgMpkwW1Wj9Ea/G/SG5dUTN07WNu8chJ4dAkYFXqOylIlpHNt/EobdQpiQNDNMzq1/EQw==", + "requires": { + "verror": "1.6.0" + }, + "dependencies": { + "verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha512-bIOaZx4+Bf6a7sIORfmYnyKLDLk/lhVym6rjYlq+vkitYKnhFmUpmPpDTCltWFrUTlGKs6sCeoDWfMA0oOOneA==", + "requires": { + "extsprintf": "1.2.0" + } + } + } + }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + } + } + }, + "which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "requires": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + } + }, + "which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + } + }, + "which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "requires": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + } + }, + "which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "yamlish": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/yamlish/-/yamlish-0.0.7.tgz", + "integrity": "sha512-MfSpIJj5Hmm0Yz788MEW1NWfdOhZR/O0WPrdiUeWBKTtCNm0jZwMJZhScqveQoE6CvUoXOQBs5/1I/nC6Sbj/w==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 5cb6f10..bfafb51 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "ufds", - "author": "MNX Cloud (mnx.io)", + "author": "Edgecast Cloud (edgecast.io)", "description": "Triton UFDS Client API", - "version": "1.8.0", + "version": "1.9.0", "homepage": "https://github.com/TritonDataCenter/triton", "repository": { "type": "git", @@ -14,6 +14,7 @@ "async": "~0.9.0", "bunyan": "^1.8.1", "clone": "~0.2.0", + "crc": "0.2.0", "ldapjs": "1.0.2", "lru-cache": "^2.5.0", "once": "^1.3.1", diff --git a/test/accesskey.test.js b/test/accesskey.test.js new file mode 100644 index 0000000..ef7093e --- /dev/null +++ b/test/accesskey.test.js @@ -0,0 +1,1016 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright 2025 Edgecast Cloud LLC. + */ + +var crypto = require('crypto'); +var util = require('util'); +var assert = require('assert-plus'); +var vasync = require('vasync'); +var Logger = require('bunyan'); +var uuidv4 = require('uuid/v4'); +var restify_errors = require('restify-errors'); + +var UFDS = require('../lib/index'); +var accesskey = require('../lib/accesskey'); + +assert.string(process.env.UFDS_IP, 'UFDS_IP envvar'); +assert.string(process.env.UFDS_LDAP_ROOT_PASSWORD, + 'UFDS_LDAP_ROOT_PASSWORD envvar'); + +var UFDS_URL = 'ldaps://' + process.env.UFDS_IP; +var UFDS_PASSWORD = process.env.UFDS_LDAP_ROOT_PASSWORD; +var ufds; + +var ID = uuidv4(); +var LOGIN = 'a' + ID.substr(0, 7); +var EMAIL = LOGIN + '_test@tritondatacenter.com'; +var USER_FMT = 'uuid=%s, ou=users, o=smartdc'; +var DN = util.format(USER_FMT, ID); + +var SUB_ID = uuidv4(); +var SUB_LOGIN = 'aa' + SUB_ID.substr(0, 7); +var SUB_EMAIL = SUB_LOGIN + '_test@tritondatacenter.com'; +var SUB_UUID; + +var SUB_ID2 = uuidv4(); +var SUB_LOGIN2 = 'aa' + SUB_ID2.substr(0, 7); +var SUB_EMAIL2 = SUB_LOGIN2 + '_test@tritondatacenter.com'; +var SUB_UUID2; + +var IDB = uuidv4(); +var LOGINB = 'b' + IDB.substr(0, 7); +var EMAILB = LOGINB + '_test@tritondatacenter.com'; +var USER_FMTB = 'uuid=%s, ou=users, o=smartdc'; +var DNB = util.format(USER_FMTB, IDB); + +var SUB_IDB = uuidv4(); +var SUB_LOGINB = 'bb' + SUB_IDB.substr(0, 7); +var SUB_EMAILB = SUB_LOGINB + '_test@tritondatacenter.com'; +var SUB_UUIDB; + +var SUB_IDB2 = uuidv4(); +var SUB_LOGINB2 = 'bb' + SUB_IDB2.substr(0, 7); +var SUB_EMAILB2 = SUB_LOGINB2 + '_test@tritondatacenter.com'; +var SUB_UUIDB2; + +var PWD = process.env.ADMIN_PWD || 'joypass123'; + +exports.setUp = function (callback) { + ufds = new UFDS({ + url: UFDS_URL, + bindDN: 'cn=root', + bindPassword: UFDS_PASSWORD, + clientTimeout: 2000, + log: new Logger({ + name: 'ufds_unit_test', + stream: process.stdout, + level: (process.env.LOG_LEVEL || 'info'), + serializers: Logger.stdSerializers + }), + tlsOptions: { + rejectUnauthorized: false + }, + retry: { + retries: 5, + maxTimeout: 10000, + minTimeout: 100 + } + }); + ufds.once('ready', function () { + ufds.removeAllListeners('error'); + callback(); + }); + ufds.once('error', function (err) { + ufds.removeAllListeners('ready'); + callback(err); + }); +}; + +exports.testAccessKeyGenerator = function (t) { + var prefix = accesskey.DEFAULT_PREFIX; + var bytes = accesskey.DEFAULT_BYTE_LENGTH; + + accesskey.generate(prefix, bytes, function (err, key) { + assert.ifError(err, 'failed to generate key'); + t.ok(key, 'access key was generated'); + t.ok(accesskey.validate(prefix, bytes, key), 'access key is valid'); + + // replace a char within the random byte area + var change = String.fromCharCode(key[8].charCodeAt(0) + 1); + var modifiedKey = key.substring(0, 8) + change + key.substring(8 + 1); + t.ok(!accesskey.validate(prefix, bytes, modifiedKey), 'invalid key'); + + // replace a char in the prefix area + change = String.fromCharCode(key[2].charCodeAt(0) + 1); + modifiedKey = key.substring(0, 2) + change + key.substring(2 + 1); + t.ok(!accesskey.validate(prefix, bytes, modifiedKey), 'invalid key'); + + // replace a char in the checksum area + change = String.fromCharCode(key[key.length - 2].charCodeAt(0) + 1); + modifiedKey = key.substring(0, key.length - 2) + change + + key.substring(key.length - 1); + t.ok(!accesskey.validate(prefix, bytes, modifiedKey), 'invalid key'); + + // replace a char within the random byte areawith an non-base64 char + modifiedKey = key.substring(0, 9) + '!' + key.substring(9 + 1); + t.ok(!accesskey.validate(prefix, bytes, modifiedKey), 'invalid key'); + + // toss random bytes at validate() + var randPrefix = crypto.randomBytes(4).toString('base64'); + var randBytes = crypto.randomBytes(bytes + 4).toString('base64'); + t.ok(!accesskey.validate(randPrefix, bytes, randBytes), 'invalid key'); + + t.done(); + }); +}; + +exports.setupTestUsers = function (test) { + var entry = { + login: LOGIN, + email: EMAIL, + uuid: ID, + userpassword: PWD, + objectclass: 'sdcperson' + }; + ufds.add(DN, entry, function (err) { + assert.ifError(err, 'err adding user'); + var entry = { + login: SUB_LOGIN, + email: SUB_EMAIL, + userpassword: PWD, + objectclass: 'sdcperson', + account: ID + }; + ufds.addUser(entry, function (err, user) { + assert.ifError(err, 'err adding subuser'); + SUB_UUID = user.uuid; + var entry2 = { + login: SUB_LOGIN2, + email: SUB_EMAIL2, + userpassword: PWD, + objectclass: 'sdcperson', + account: ID + }; + ufds.addUser(entry2, function (err, user2) { + assert.ifError(err, 'err adding subuser2'); + SUB_UUID2 = user2.uuid; + test.done(); + }); + }); + }); +}; + +exports.setupTestUsersB = function (test) { + var entry = { + login: LOGINB, + email: EMAILB, + uuid: IDB, + userpassword: PWD, + objectclass: 'sdcperson' + }; + ufds.add(DNB, entry, function (err) { + assert.ifError(err, 'err adding user'); + var entry = { + login: SUB_LOGINB, + email: SUB_EMAILB, + userpassword: PWD, + objectclass: 'sdcperson', + account: IDB + }; + ufds.addUser(entry, function (err, user) { + assert.ifError(err, 'err adding subuser'); + SUB_UUIDB = user.uuid; + var entry2 = { + login: SUB_LOGINB2, + email: SUB_EMAILB2, + userpassword: PWD, + objectclass: 'sdcperson', + account: IDB + }; + ufds.addUser(entry2, function (err, user2) { + assert.ifError(err, 'err adding subuser2'); + SUB_UUIDB2 = user2.uuid; + test.done(); + }); + }); + }); +}; + +exports.testAccountAccessKeysBasic = function (t) { + vasync.waterfall([ + function addAccessKey(next) { + ufds.addAccessKey(ID, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, 'Active', 'AccessKey Status'); + next(null, accKey); + }, + function getAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, next); + }, + function listAccessKeys(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.listAccessKeys(ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + t.ok(listOfKeys, 'List of access keys'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + t.ok(listOfKeys[0], 'list of keys contains a key'); + var foundKey = listOfKeys.some(function (key) { + return (key.accesskeyid === accKey.accesskeyid); + }); + t.ok(foundKey, 'list of keys contains created key'); + next(null, accKey); + }); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + assert.deepEqual([accKey], listOfKeys); + next(null, accKey); + }); + }, + function deleteAccessKey(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.deleteAccessKey(ID, accKey, function (delErr) { + assert.ifError(delErr, 'deleteAccessKey error'); + next(null, accKey); + }); + }, + function getDeletedAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, function (error, key) { + t.ok(error instanceof restify_errors.ResourceNotFoundError, + 'getAccessKey deleted key is absent'); + t.equal(key, undefined, 'deleted key not returned'); + next(null, accKey); + }); + }, + function listDeletedAccessKey(accKey, next) { + ufds.listAccessKeys(ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + t.ok(listOfKeys, 'List of access keys'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + var foundKey = listOfKeys.some(function (key) { + return (key.accesskeyid === accKey.accesskeyid); + }); + t.ok(!foundKey, 'deleted key abesent from list of keys'); + next(); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testAccountAccessKeysFalsyAccount = function (t) { + vasync.waterfall([ + function addAccessKey(next) { + ufds.addAccessKey(ID, null, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, 'Active', 'AccessKey Status'); + next(null, accKey); + }, + function getAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, next); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + assert.deepEqual([accKey], listOfKeys); + next(null, accKey); + }); + }, + function deleteAccessKey(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.deleteAccessKey(ID, accKey, function (delErr) { + assert.ifError(delErr, 'deleteAccessKey error'); + next(null, accKey); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testAccountAccessKeysOptions = function (t) { + const options = { + status: 'Inactive', + description: 'My hovercraft is full of eels.' + }; + vasync.waterfall([ + function addAccessKeyWithOptions(next) { + ufds.addAccessKey(ID, options, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, options.status, 'AccessKey Status'); + t.equal(accKey.description, options.description, + 'AccessKey description'); + next(null, accKey); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(SUB_UUID, ID, + function (listErr, listOfKeys) { + assert.deepEqual([], listOfKeys, 'no active keys'); + next(null, accKey); + }); + }, + function deleteAccessKey(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.deleteAccessKey(ID, accKey, function (delErr) { + assert.ifError(delErr, 'deleteAccessKey error'); + next(null, accKey); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testAccountAccessKeysStatusValidation = function (t) { + const options = { + status: 'NotYetCreated' + }; + ufds.addAccessKey(ID, options, function (error, key) { + t.ok(error instanceof restify_errors.InvalidArgumentError, + 'addAccessKey rejects invalid status'); + t.equal(key, undefined, 'key not returned for invalid status'); + t.done(); + }); +}; + +exports.testAccountAccessKeysDescValidation = function (t) { + const options = { + description: 'Strange women lying in ponds distributing swords is ' + + 'no basis for a system of government. Supreme executive power ' + + 'derives from a mandate from the masses, not from some farcical ' + + 'aquatic ceremony.' + }; + ufds.addAccessKey(ID, options, function (error, key) { + t.ok(error instanceof restify_errors.InvalidArgumentError, + 'addAccessKey rejects invalid description'); + t.equal(key, undefined, 'key not returned for invalid description'); + t.done(); + }); +}; + +exports.testSubAccountAccessKeysBasic = function (t) { + vasync.waterfall([ + function addAccessKey(next) { + ufds.addAccessKey(SUB_UUID, ID, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, 'Active', 'AccessKey Status'); + next(null, accKey); + }, + function getAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(SUB_UUID, accessKeyId, ID, next); + }, + function listAccessKeys(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.listAccessKeys(SUB_UUID, ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + t.ok(listOfKeys, 'List of access keys'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + t.ok(listOfKeys[0], 'list of keys contains a key'); + var foundKey = listOfKeys.some(function (key) { + return (key.accesskeyid === accKey.accesskeyid); + }); + t.ok(foundKey, 'list of keys contains created key'); + next(null, accKey); + }); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(SUB_UUID, ID, + function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + assert.deepEqual([accKey], listOfKeys); + next(null, accKey); + }); + }, + function deleteAccessKey(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.deleteAccessKey(SUB_UUID, accKey, ID, function (delErr) { + assert.ifError(delErr, 'deleteAccessKey error'); + next(null, accKey); + }); + }, + function getDeletedAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(SUB_UUID, accessKeyId, ID, function (error, key) { + t.ok(error instanceof restify_errors.ResourceNotFoundError, + 'getAccessKey deleted key is absent'); + t.equal(key, undefined, 'deleted key not returned'); + next(null, accKey); + }); + }, + function listDeletedAccessKey(accKey, next) { + ufds.listAccessKeys(SUB_UUID, ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + t.ok(listOfKeys, 'List of access keys'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + var foundKey = listOfKeys.some(function (key) { + return (key.accesskeyid === accKey.accesskeyid); + }); + t.ok(!foundKey, 'deleted key abesent from list of keys'); + next(); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testSubAccountAccessKeysOptions = function (t) { + const options = { + status: 'Expired', + description: 'What is the air-speed velocity of an unladen swallow?' + }; + vasync.waterfall([ + function addAccessKeyWithOptions(next) { + ufds.addAccessKey(SUB_UUID, ID, options, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, options.status, 'AccessKey Status'); + t.equal(accKey.description, options.description, + 'AccessKey description'); + next(null, accKey); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(SUB_UUID, ID, + function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + assert.deepEqual([], listOfKeys, 'no active keys'); + next(null, accKey); + }); + }, + function deleteAccessKey(accKey, next) { + t.ok(accKey, 'getAccessKey key'); + ufds.deleteAccessKey(ID, accKey, function (delErr) { + assert.ifError(delErr, 'deleteAccessKey error'); + next(null, accKey); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testAccountAccessKeysUpdate = function (t) { + var description = 'Are you suggesting coconuts migrate?'; + vasync.waterfall([ + function addAccessKey(next) { + ufds.addAccessKey(ID, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, 'Active', 'AccessKey Status'); + next(null, accKey); + }, + function updateStatus(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + status: 'Inactive' + }; + ufds.updateAccessKey(ID, accesskey, function (err, updatedKey) { + assert.ifError(err, 'updateStatus'); + t.equal(accKey.dn, updatedKey.dn, 'dn'); + t.equal(accKey.accesskeyid, updatedKey.accesskeyid, + 'accesskeyid'); + t.equal(accKey.accesskeysecret, updatedKey.accesskeysecret, + 'accesskeysecret'); + t.equal(accKey.created, updatedKey.created, 'created'); + t.equal(accesskey.status, updatedKey.status, 'status'); + t.notEqual(accKey.updated, updatedKey.updated, 'updated'); + next(null, accKey); + }); + }, + function getAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, function (err, updatedAccKey) { + assert.ifError(err, 'getAccessKey'); + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(accKey.description, updatedAccKey.description, + 'AccessKey description'); + + // Should have changed + t.equal(updatedAccKey.status, 'Inactive', + 'AccessKey Status'); + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + next(null, updatedAccKey); + }); + }, + function addDescription(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + description: description + }; + ufds.updateAccessKey(ID, accesskey, function (err, updatedKey) { + assert.ifError(err, 'addDescription'); + t.equal(accKey.dn, updatedKey.dn, 'dn'); + t.equal(accKey.created, updatedKey.created, 'created'); + t.equal(accKey.status, updatedKey.status, 'status'); + t.equal(accesskey.description, updatedKey.description, + 'description'); + t.notEqual(accKey.updated, updatedKey.updated, 'updated'); + next(null, accKey); + }); + }, + function getUpdatedAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, function (err, updatedAccKey) { + assert.ifError(err, 'getUpdatedAccessKey'); + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(updatedAccKey.status, accKey.status, + 'AccessKey Status'); + + // Should have changed + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + t.equal(updatedAccKey.description, + description, + 'AccessKey description'); + + next(null, updatedAccKey); + }); + }, + function removeDescription(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + description: null + }; + ufds.updateAccessKey(ID, accesskey, function (err, updatedKey) { + assert.ifError(err, 'removeDescription'); + t.equal(accKey.dn, updatedKey.dn, 'dn'); + t.equal(accKey.created, updatedKey.created, 'created'); + t.equal(updatedKey.description, undefined); + t.notEqual(accKey.updated, updatedKey.updated, 'updated'); + next(null, accKey); + }); + }, + function getUpdatedAccessKeyNoDesc(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(ID, accessKeyId, function (err, updatedAccKey) { + assert.ifError(err, 'getUpdatedAccessKeyNoDesc'); + + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(updatedAccKey.status, accKey.status, + 'AccessKey Status'); + + // Should have changed + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + t.equal(updatedAccKey.description, undefined, + 'AccessKey description'); + + next(null, updatedAccKey); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.testSubAccountAccessKeysUpdate = function (t) { + var description = 'You can’t expect to wield supreme executive power ' + + 'just because some watery tart threw a sword at you!'; + + vasync.waterfall([ + function addAccessKey(next) { + ufds.addAccessKey(SUB_UUID, ID, next); + }, + function checkResponse(accKey, next) { + t.ok(accKey, 'added AccessKey'); + t.ok(accKey.accesskeyid, 'AccessKeyId'); + t.ok(accKey.accesskeysecret, 'AccessKeySecret'); + t.ok(accKey.created, 'AccessKey Created'); + t.ok(accKey.updated, 'AccessKey Updated'); + t.equal(accKey.status, 'Active', 'AccessKey Status'); + next(null, accKey); + }, + function updateStatus(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + status: 'Expired' + }; + ufds.updateAccessKey(SUB_UUID, ID, accesskey, + function (err, result) { + assert.ifError(err, 'updateStatus'); + next(null, accKey); + }); + }, + function getAccessKey(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(SUB_UUID, accessKeyId, ID, + function (err, updatedAccKey) { + assert.ifError(err, 'getAccessKey'); + + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(accKey.description, updatedAccKey.description, + 'AccessKey description'); + + // Should have changed + t.equal(updatedAccKey.status, 'Expired', 'AccessKey Status'); + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + next(null, updatedAccKey); + }); + }, + function addDescription(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + description: description + }; + ufds.updateAccessKey(SUB_UUID, ID, accesskey, + function (err, updatedKey) { + assert.ifError(err, 'addDescription'); + t.equal(accKey.dn, updatedKey.dn, 'dn'); + t.equal(accKey.accesskeyid, updatedKey.accesskeyid, + 'accesskeyid'); + t.equal(accKey.accesskeysecret, updatedKey.accesskeysecret, + 'accesskeysecret'); + t.equal(accKey.created, updatedKey.created, 'created'); + t.equal(accesskey.description, updatedKey.description, + 'status'); + t.notEqual(accKey.updated, updatedKey.updated, 'updated'); + next(null, accKey); + }); + }, + function getAccessKeyDesc(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(SUB_UUID, accessKeyId, ID, + function (err, updatedAccKey) { + assert.ifError(err, 'getAccessKey'); + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(updatedAccKey.status, accKey.status, + 'AccessKey Status'); + + // Should have changed + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + t.equal(updatedAccKey.description, + description, + 'AccessKey description'); + + next(null, updatedAccKey); + }); + }, + function listActiveAccessKeys(accKey, next) { + t.ok(accKey, 'listActiveAccessKeys key'); + ufds.listActiveAccessKeys(SUB_UUID, ID, + function (listErr, listOfKeys) { + assert.ifError(listErr, 'listAccessKeys error'); + assert.deepEqual([], listOfKeys, 'no active keys'); + next(null, accKey); + }); + }, + function removeDescription(accKey, next) { + var accesskey = { + accesskeyid: accKey.accesskeyid, + description: null + }; + ufds.updateAccessKey(SUB_UUID, ID, accesskey, + function (err, result) { + assert.ifError(err, 'removeDescription'); + next(null, accKey); + }); + }, + function getAccessKeyUpdated(accKey, next) { + var accessKeyId = accKey.accesskeyid; + ufds.getAccessKey(SUB_UUID, accessKeyId, ID, + function (err, updatedAccKey) { + assert.ifError(err, 'getAccessKey'); + + // Should not have changed + t.ok(updatedAccKey, 'updatedAccKey'); + t.equal(accKey.accesskeyid, + updatedAccKey.accesskeyid, 'AccessKeyId'); + t.equal(accKey.accesskeysecret, + updatedAccKey.accesskeysecret, 'AccessKeySecret'); + t.equal(accKey.created, updatedAccKey.created, + 'AccessKey Created'); + t.equal(updatedAccKey.status, accKey.status, + 'AccessKey Status'); + + // Should have changed + t.notEqual(accKey.updated, updatedAccKey.updated, + 'AccessKey Updated'); + + t.equal(updatedAccKey.description, undefined, + 'AccessKey description'); + + next(null, updatedAccKey); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + + +// Sanity check that sub account keys and parent keys are separate +exports.testSubAccountAccessKeysChecks = function (t) { + + vasync.waterfall([ + + // wipe out any existing keys for test users and children + + function purgeParentKeys(next) { + ufds.listAccessKeys(ID, function (listErr, listOfKeys) { + vasync.forEachParallel({ + inputs: listOfKeys, + func: function deleteKeys(key, cb) { + ufds.deleteAccessKey(ID, key, cb); + } + }, next); + }); + }, + function purgeChild1Keys(_, next) { + ufds.listAccessKeys(SUB_UUID, ID, function (listErr, listOfKeys) { + vasync.forEachParallel({ + inputs: listOfKeys, + func: function deleteKeys(key, cb) { + ufds.deleteAccessKey(SUB_UUID, key, ID, cb); + } + }, next); + }); + }, + function purgeChild2Keys(_, next) { + ufds.listAccessKeys(SUB_UUID2, ID, function (listErr, listOfKeys) { + vasync.forEachParallel({ + inputs: listOfKeys, + func: function deleteKeys(key, cb) { + ufds.deleteAccessKey(SUB_UUID2, key, ID, cb); + } + }, next); + }); + }, + + // confirm all keys have been purged + + function parentKeysEmpty(_, next) { + ufds.listAccessKeys(ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'parentKeysEmpty error'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + t.equal(listOfKeys.length, 0); + next(null, {}); + }); + }, + function child1KeysEmpty(_, next) { + ufds.listAccessKeys(SUB_UUID, ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'child1KeysEmpty error'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + t.equal(listOfKeys.length, 0); + next(null, {}); + }); + }, + function child2KeysEmpty(_, next) { + ufds.listAccessKeys(SUB_UUID2, ID, function (listErr, listOfKeys) { + assert.ifError(listErr, 'child2KeysEmpty error'); + t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); + t.equal(listOfKeys.length, 0); + next(null, {}); + }); + }, + + // Create a key in the parent and one in each child account + + function addParentAccessKey(_, next) { + ufds.addAccessKey(ID, function (err, key) { + assert.ifError(err, 'addParentAccessKey'); + next(null, {parent: [key]}); + }); + }, + + function addChild1AccessKey(context, next) { + ufds.addAccessKey(SUB_UUID, ID, function (err, key) { + assert.ifError(err, 'addChild1AccessKey'); + context.child1 = [key]; + next(null, context); + }); + }, + + function addChild2AccessKey(context, next) { + ufds.addAccessKey(SUB_UUID2, ID, function (err, key) { + assert.ifError(err, 'addChild2AccessKey'); + context.child2 = [key]; + next(null, context); + }); + }, + + // Ensure keys aren't intermingled + + function listParentAccessKeys(context, next) { + ufds.listAccessKeys(ID, function (err, keys) { + assert.ifError(err, 'listParentAccessKeys'); + assert.deepEqual(context.parent, keys); + assert.notDeepEqual(context.child1, keys); + assert.notDeepEqual(context.child2, keys); + next(null, context); + }); + }, + + function listChild1AccessKeys(context, next) { + ufds.listAccessKeys(SUB_UUID, ID, function (err, keys) { + assert.ifError(err, 'listChild1AccessKeys'); + assert.deepEqual(context.child1, keys); + assert.notDeepEqual(context.child2, keys); + assert.notDeepEqual(context.parent, keys); + next(null, context); + }); + }, + + function listChild2AccessKeys(context, next) { + ufds.listAccessKeys(SUB_UUID2, ID, function (err, keys) { + assert.ifError(err, 'listChild2AccessKeys'); + assert.deepEqual(context.child2, keys); + assert.notDeepEqual(context.child1, keys); + assert.notDeepEqual(context.parent, keys); + next(null, context); + }); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +// Sanity check that account keys are separate +exports.testSubAccountAccessKeysChecks = function (t) { + + vasync.waterfall([ + + // Fetch all the keys for the main test users and subusers + function listParentAccessKeys(next) { + ufds.listAccessKeys(ID, function (err, keys) { + assert.ifError(err, 'listParentAccessKeys'); + next(null, { + a: { + parent: keys + } + }); + }); + }, + + function listChild1AccessKeys(context, next) { + ufds.listAccessKeys(SUB_UUID, ID, function (err, keys) { + assert.ifError(err, 'listChild1AccessKeys'); + context.a.child1 = keys; + next(null, context); + }); + }, + + function listChild2AccessKeys(context, next) { + ufds.listAccessKeys(SUB_UUID2, ID, function (err, keys) { + assert.ifError(err, 'listChild2AccessKeys'); + context.a.child2 = keys; + next(null, context); + }); + }, + + // Create keys in the second user account (and its subusers) + function addParentAccessKey(context, next) { + ufds.addAccessKey(IDB, function (err, key) { + assert.ifError(err, 'addParentAccessKey'); + context.b = { + parent: [key] + }; + next(null, context); + }); + }, + + function addChild1AccessKey(context, next) { + ufds.addAccessKey(SUB_UUIDB, IDB, function (err, key) { + assert.ifError(err, 'addChild1AccessKey'); + context.b.child1 = [key]; + next(null, context); + }); + }, + + function addChild2AccessKey(context, next) { + ufds.addAccessKey(SUB_UUIDB2, IDB, function (err, key) { + assert.ifError(err, 'addChild2AccessKey'); + context.b.child2 = [key]; + next(null, context); + }); + }, + + // Ensure keys aren't intermingled + function compareAccountKeys(context, next) { + + assert.notDeepEqual(context.a.parent, context.b.parent); + assert.notDeepEqual(context.a.parent, context.b.child1); + assert.notDeepEqual(context.a.parent, context.b.child2); + + assert.notDeepEqual(context.a.child1, context.b.parent); + assert.notDeepEqual(context.a.child1, context.b.child1); + assert.notDeepEqual(context.a.child1, context.b.child2); + + assert.notDeepEqual(context.a.child2, context.b.parent); + assert.notDeepEqual(context.a.child2, context.b.child1); + assert.notDeepEqual(context.a.child2, context.b.child2); + + next(); + } + ], function (err, res) { + assert.ifError(err); + t.done(); + }); +}; + +exports.tearDown = function (callback) { + ufds.close(function () { + callback(); + }); +}; \ No newline at end of file diff --git a/test/ufds.test.js b/test/ufds.test.js index db49147..fa95ee6 100644 --- a/test/ufds.test.js +++ b/test/ufds.test.js @@ -7,6 +7,7 @@ /* * Copyright 2020 Joyent, Inc. * Copyright 2023 MNX Cloud, Inc. + * Copyright 2025 Edgecast Cloud LLC. */ var assert = require('assert-plus'); @@ -134,9 +135,9 @@ exports.testGetUser = function (test) { }; ufds.add(DN, entry, function (err) { - test.ifError(err); + assert.ifError(err); ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, LOGIN); // Testing no hidden attributes are available: test.ok(!user._owner); @@ -153,7 +154,7 @@ exports.testGetUser = function (test) { exports.testGetUserByUuid = function (test) { ufds.getUser(ID, function (err, user) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, LOGIN); test.done(); }); @@ -162,7 +163,7 @@ exports.testGetUserByUuid = function (test) { exports.testGetUserByEmail = function (test) { ufds.getUserByEmail(EMAIL, function (err, user) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, LOGIN); test.done(); }); @@ -184,7 +185,7 @@ exports.testGetUserExByUuid = function (test) { searchType: 'uuid', value: ID }, function (err, user) { - test.ifError(err); + assert.ifError(err); test.strictEqual(user.login, LOGIN); test.done(); }); @@ -195,7 +196,7 @@ exports.testGetUserExByLogin = function (test) { searchType: 'login', value: LOGIN }, function (err, user) { - test.ifError(err); + assert.ifError(err); test.strictEqual(user.login, LOGIN); test.done(); }); @@ -305,7 +306,7 @@ exports.testGetUserExError2 = function (test) { exports.testEmptyListDcLocalConfig = function (test) { ufds.listDcLocalConfig(ID, function (err, cfg) { - test.ifError(err, 'err listing dc config object'); + assert.ifError(err, 'err listing dc config object'); test.equal(cfg, null, 'null dclocalconfig'); test.done(); }); @@ -317,7 +318,7 @@ exports.testAddDcLocalConfig = function (test) { defaultfabricsetup: DCLOCALCONFIG.defaultfabricsetup }; ufds.addDcLocalConfig(ID, DC, entry, function (err, cfg) { - test.ifError(err, 'no errors'); + assert.ifError(err, 'no errors'); test.ok(cfg, 'added cfg'); if (cfg) { test.equal(cfg.dn, DCLC_USER_DN, 'dn correct'); @@ -332,7 +333,7 @@ exports.testAddDcLocalConfig = function (test) { exports.testGetUserWithDcConfig = function (test) { ufds.getUser(ID, function (err, user) { - test.ifError(err, 'err getting user'); + assert.ifError(err, 'err getting user'); test.ok(user, 'user'); test.ok(user.dclocalconfig, 'has dc config'); if (user.dclocalconfig) { @@ -349,7 +350,7 @@ exports.testGetUserWithDcConfig = function (test) { exports.testGetDcLocalConfig = function (test) { ufds.getDcLocalConfig(ID, DC, function (err, cfg) { - test.ifError(err, 'getting dc config object'); + assert.ifError(err, 'getting dc config object'); test.ok(cfg, 'found config object'); if (cfg) { test.equal(cfg.dn, DCLC_USER_DN, 'dn correct'); @@ -364,7 +365,7 @@ exports.testGetDcLocalConfig = function (test) { exports.testListDcLocalConfig = function (test) { ufds.listDcLocalConfig(ID, function (err, cfg) { - test.ifError(err, 'listing dc config object'); + assert.ifError(err, 'listing dc config object'); test.ok(cfg, 'found config object'); if (cfg) { test.equal(cfg.dn, DCLC_USER_DN, 'dn correct'); @@ -383,7 +384,7 @@ exports.testUpdateDcLocalConfig = function (test) { defaultnetwork: uuidv4() }; ufds.updateDcLocalConfig(ID, DC, update, function (err, cfg) { - test.ifError(err, 'updated dc config'); + assert.ifError(err, 'updated dc config'); test.ok(cfg, 'updated config object'); if (cfg) { test.equal(cfg.dn, DCLC_USER_DN, 'dn correct'); @@ -403,7 +404,7 @@ exports.testDelUpdateDcLocalConfig = function (test) { defaultnetwork: null }; ufds.updateDcLocalConfig(ID, DC, update, function (err, cfg) { - test.ifError(err, 'updated dc config'); + assert.ifError(err, 'updated dc config'); test.ok(cfg, 'config object'); if (cfg) { test.equal(cfg.dclocalconfig, DC, @@ -416,7 +417,7 @@ exports.testDelUpdateDcLocalConfig = function (test) { exports.testDeleteDcLocalConfig = function (test) { ufds.deleteDcLocalConfig(ID, DC, function (err) { - test.ifError(err, 'deleted config ogject'); + assert.ifError(err, 'deleted config ogject'); ufds.getDcLocalConfig(ID, DC, function (err, data) { test.ok(err, 'err'); if (err) { @@ -429,10 +430,10 @@ exports.testDeleteDcLocalConfig = function (test) { exports.testAuthenticate = function (test) { ufds.authenticate(LOGIN, PWD, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user); ufds.getUser(LOGIN, function (err, user2) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, user2.login); test.done(); }); @@ -442,11 +443,11 @@ exports.testAuthenticate = function (test) { exports.testAuthenticateByUuid = function (test) { ufds.authenticate(ID, PWD, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user); test.equal(user.login, LOGIN); user.authenticate(PWD, function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -455,9 +456,9 @@ exports.testAuthenticateByUuid = function (test) { exports.testAddKey = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); user.addKey(SSH_KEY, function (err, key) { - test.ifError(err, err); + assert.ifError(err, err); test.ok(key, 'have key: ' + key); if (key) { test.equal(key.openssh, SSH_KEY); @@ -473,7 +474,7 @@ exports.testAddKey = function (test) { exports.testAddDuplicatedKeyNotAllowed = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err, 'getUser error'); + assert.ifError(err, 'getUser error'); user.addKey(SSH_KEY, function (err, key) { test.ok(err, 'add duplicated key error'); test.done(); @@ -484,15 +485,15 @@ exports.testAddDuplicatedKeyNotAllowed = function (test) { exports.testListAndGetKeys = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); user.listKeys(function (err, keys) { - test.ifError(err); + assert.ifError(err); test.ok(keys); test.ok(keys.length); test.equal(keys[0].openssh, SSH_KEY); test.equal(keys[0].name, 'mark@foo.local'); user.getKey(keys[0].fingerprint, function (err, key) { - test.ifError(err); + assert.ifError(err); test.ok(key); test.deepEqual(keys[0], key); test.done(); @@ -504,12 +505,12 @@ exports.testListAndGetKeys = function (test) { exports.testAddKeyByName = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); user.addKey({ openssh: SSH_KEY_TWO, name: 'id_rsa' }, function (err, key) { - test.ifError(err); + assert.ifError(err); test.ok(key); test.equal(key.openssh, SSH_KEY_TWO); test.equal(key.name, 'id_rsa'); @@ -521,7 +522,7 @@ exports.testAddKeyByName = function (test) { exports.testAddDuplicatedKeyByName = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err, 'getUser error'); + assert.ifError(err, 'getUser error'); user.addKey({ openssh: SSH_KEY_THREE, name: 'id_rsa' @@ -535,13 +536,13 @@ exports.testAddDuplicatedKeyByName = function (test) { exports.testDelKey = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); user.listKeys(function (err, keys) { - test.ifError(err); + assert.ifError(err); user.deleteKey(keys[0], function (err) { - test.ifError(err); + assert.ifError(err); user.deleteKey(keys[1], function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -552,26 +553,26 @@ exports.testDelKey = function (test) { exports.testUserGroups = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(!user.isAdmin()); test.ok(!user.isReader()); user.addToGroup('readers', function (err2) { - test.ifError(err2); + assert.ifError(err2); ufds.getUser(LOGIN, function (err3, user2) { - test.ifError(err3); + assert.ifError(err3); test.ok(user2.isReader()); test.deepEqual(user2.groups(), ['readers']); user2.addToGroup('operators', function (err4) { - test.ifError(err4); + assert.ifError(err4); ufds.getUser(LOGIN, function (err5, user3) { - test.ifError(err5); + assert.ifError(err5); test.ok(user3.isAdmin()); test.deepEqual(user3.groups(), ['operators', 'readers']); user3.removeFromGroup('operators', function (err6) { - test.ifError(err6); + assert.ifError(err6); ufds.getUser(LOGIN, function (err7, user4) { - test.ifError(err7); + assert.ifError(err7); test.ok(user4.isReader() && !user4.isAdmin()); test.deepEqual(user4.groups(), ['readers']); test.done(); @@ -592,23 +593,23 @@ exports.testCrudUser = function (test) { userpassword: 'secret123' }; ufds.addUser(entry, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user); test.ok(user.uuid); ufds.updateUser(user, { phone: '+1 (206) 555-1212', pwdaccountlockedtime: Date.now() + (3600 * 1000) }, function (err) { - test.ifError(err); + assert.ifError(err); user.authenticate(entry.userpassword, function (er) { test.ok(er); test.equal(er.statusCode, 401); user.unlock(function (e) { - test.ifError(e); + assert.ifError(e); user.authenticate(entry.userpassword, function (er2) { - test.ifError(er2); + assert.ifError(er2); user.destroy(function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -621,29 +622,29 @@ exports.testCrudUser = function (test) { exports.testCrudLimit = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user); user.addLimit( {datacenter: 'coal', smartos: '123'}, function (err, limit) { - test.ifError(err); + assert.ifError(err); test.ok(limit); test.ok(limit.smartos); user.listLimits(function (err, limits) { - test.ifError(err); + assert.ifError(err); test.ok(limits); test.ok(limits.length); test.ok(limits[0].smartos); limits[0].nodejs = 234; user.updateLimit(limits[0], function (err) { - test.ifError(err); + assert.ifError(err); user.getLimit(limits[0].datacenter, function (err, limit) { - test.ifError(err); + assert.ifError(err); test.ok(limit); test.ok(limit.smartos); test.ok(limit.nodejs); user.deleteLimit(limit, function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -662,10 +663,10 @@ exports.testMetadata = function (t) { var META_FMT = 'metadata=%s, uuid=%s, ou=users, o=smartdc'; ufds.getUser(LOGIN, function (err, user) { - t.ifError(err, 'testMetadata getUser error'); + assert.ifError(err, 'testMetadata getUser error'); t.ok(user); ufds.addMetadata(user, key, meta, function (err2, metadata) { - t.ifError(err2, 'testMetadata addMetadata error'); + assert.ifError(err2, 'testMetadata addMetadata error'); t.ok(metadata.cn); t.equal(key, metadata.cn); t.ok(metadata.dn); @@ -674,14 +675,14 @@ exports.testMetadata = function (t) { t.equal('capimetadata', metadata.objectclass); // CAPI-319: getMetadata w/o object ufds.getMetadata(LOGIN, key, function (err3, meta3) { - t.ifError(err3, 'testMetadata getMetadata error'); + assert.ifError(err3, 'testMetadata getMetadata error'); t.ok(meta3); // And now with object: ufds.getMetadata(user, key, function (err4, meta4) { - t.ifError(err4, 'testMetadata getMetadata error'); + assert.ifError(err4, 'testMetadata getMetadata error'); t.ok(meta4); ufds.deleteMetadata(user, key, function (er5, meta5) { - t.ifError(er5); + assert.ifError(er5); t.done(); }); }); @@ -701,14 +702,15 @@ exports.testAddSubUserToAccount = function (test) { account: ID }; ufds.addUser(entry, function (err, user) { - test.ifError(err, 'err adding user'); + assert.ifError(err, 'err adding user'); test.ok(user, 'returned new user'); test.strictEqual(user.login, SUB_LOGIN, 'login correct'); test.strictEqual(user.uuid.length, 36, 'uuid set'); test.ok(!SUB_UUID, 'SUB_UUID was already set'); SUB_UUID = user.uuid; ufds.getUser(SUB_UUID, ID, function (e1, u1) { - test.ifError(e1, 'getUser for new subuser ' + SUB_UUID + ' failed'); + assert.ifError(e1, 'getUser for new subuser ' + SUB_UUID + + ' failed'); test.equal(u1.login, SUB_LOGIN, 'sub_login correct'); test.done(); }); @@ -721,7 +723,7 @@ exports.testGetUserExSubUserByUuid = function (test) { account: ID, value: SUB_UUID }, function (err, user) { - test.ifError(err, 'getUserEx error'); + assert.ifError(err, 'getUserEx error'); test.strictEqual(user.login, SUB_LOGIN, 'expected subuser login'); test.done(); }); @@ -733,7 +735,7 @@ exports.testGetUserExSubUserByLogin = function (test) { account: ID, value: SUB_LOGIN }, function (err, user) { - test.ifError(err, 'getUserEx error'); + assert.ifError(err, 'getUserEx error'); test.strictEqual(user.login, SUB_LOGIN, 'expected subuser login'); test.done(); }); @@ -776,7 +778,7 @@ exports.testAddSubUserDcLocalConfig = function (test) { }; ufds.addDcLocalConfig(ID, SUB_UUID, DC, entry, function (err, cfg) { - test.ifError(err, 'no errors'); + assert.ifError(err, 'no errors'); test.ok(cfg, 'added cfg'); if (cfg) { test.equal(cfg.dn, util.format(DCLC_SUBUSER_FMT, @@ -790,7 +792,7 @@ exports.testAddSubUserDcLocalConfig = function (test) { exports.getSubUserWithDcLocalConfig = function (test) { ufds.getUser(SUB_UUID, ID, function (err, user) { - test.ifError(err, 'err getting user'); + assert.ifError(err, 'err getting user'); test.ok(user, 'user'); if (user && user.dclocalconfig) { test.ok(user.dclocalconfig, 'has dc config'); @@ -805,7 +807,7 @@ exports.getSubUserWithDcLocalConfig = function (test) { exports.delSubUserDcLocalConfig = function (test) { ufds.deleteDcLocalConfig(ID, SUB_UUID, DC, function (err) { - test.ifError(err, 'deleted config object'); + assert.ifError(err, 'deleted config object'); ufds.getDcLocalConfig(ID, SUB_UUID, DC, function (err, data) { test.ok(err, 'expected err'); if (err) { @@ -818,25 +820,25 @@ exports.delSubUserDcLocalConfig = function (test) { exports.testSubuserKey = function (test) { ufds.getUser(SUB_LOGIN, ID, function (err, user) { - test.ifError(err); + assert.ifError(err); user.addKey(SSH_KEY, function (err, key) { - test.ifError(err, err); + assert.ifError(err, err); test.ok(key, 'have key: ' + key); if (key) { test.equal(key.openssh, SSH_KEY); } user.listKeys(function (er2, keys) { - test.ifError(er2); + assert.ifError(er2); test.ok(keys); test.ok(keys.length); test.equal(keys[0].openssh, SSH_KEY); user.getKey(keys[0].fingerprint, user.account, function (er3, key2) { - test.ifError(er3); + assert.ifError(er3); test.ok(key2); test.deepEqual(keys[0], key2); user.deleteKey(keys[0], function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -854,10 +856,10 @@ exports.testSubUsersMetadata = function (t) { var SUB_META_FMT = 'metadata=%s, uuid=%s, uuid=%s, ou=users, o=smartdc'; ufds.getUser(SUB_LOGIN, ID, function (err, user) { - t.ifError(err, 'testMetadata getUser error'); + assert.ifError(err, 'testMetadata getUser error'); t.ok(user, 'metadata user'); ufds.addMetadata(user, key, meta, function (err2, metadata) { - t.ifError(err2, 'testMetadata addMetadata error'); + assert.ifError(err2, 'testMetadata addMetadata error'); t.ok(metadata.cn, 'metadata cn'); t.equal(key, metadata.cn, 'metadata cn value'); t.ok(metadata.dn, 'metadata dn'); @@ -870,14 +872,14 @@ exports.testSubUsersMetadata = function (t) { // CAPI-319: getMetadata w/o object ufds.getMetadata(SUB_LOGIN, key, user.account, function (err3, meta3) { - t.ifError(err3, 'testMetadata getMetadata error'); + assert.ifError(err3, 'testMetadata getMetadata error'); t.ok(meta3, 'get meta w/o object'); // And now with object: ufds.getMetadata(user, key, function (err4, meta4) { - t.ifError(err4, 'testMetadata getMetadata error'); + assert.ifError(err4, 'testMetadata getMetadata error'); t.ok(meta4, 'get meta with object'); ufds.deleteMetadata(user, key, function (er5, meta5) { - t.ifError(er5); + assert.ifError(er5); t.done(); }); }); @@ -890,29 +892,29 @@ exports.testSubUsersMetadata = function (t) { // Sub-users limits are the same than main account user limits: exports.testSubUsersLimits = function (test) { ufds.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user); user.addLimit( {datacenter: 'coal', smartos: '123'}, function (err, limit) { - test.ifError(err); + assert.ifError(err); test.ok(limit); test.ok(limit.smartos); ufds.getUser(SUB_LOGIN, ID, function (err, subuser) { - test.ifError(err, 'sub user limits getUser error'); + assert.ifError(err, 'sub user limits getUser error'); test.ok(subuser, 'subuser'); subuser.listLimits(function (err, limits) { - test.ifError(err); + assert.ifError(err); test.ok(limits); test.ok(limits.length); test.ok(limits[0].smartos); subuser.getLimit(limits[0].datacenter, function (err, limit) { - test.ifError(err); + assert.ifError(err); test.ok(limit); test.ok(limit.smartos); user.deleteLimit(limit, function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }); @@ -942,28 +944,28 @@ exports.testSubUsersCrud = function (test) { var entry = generateSubUser(); ufds.addUser(entry, function (err, user) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, entry.login); ufds.getUserByEmail(entry.email, entry.account, function (err2, user2) { - test.ifError(err2); + assert.ifError(err2); test.equal(user2.login, entry.login); ufds.updateUser(user.uuid, { phone: '+1 (206) 555-1212', pwdaccountlockedtime: Date.now() + (3600 * 1000) }, user.account, function (err) { - test.ifError(err); + assert.ifError(err); user.authenticate(entry.userpassword, function (er) { test.ok(er); test.equal(er.statusCode, 401); user.unlock(function (e) { - test.ifError(e); + assert.ifError(e); user.authenticate(entry.userpassword, function (er2) { - test.ifError(er2); + assert.ifError(er2); user.destroy(function (er3) { - test.ifError(er3); + assert.ifError(er3); test.done(); }); }); @@ -983,7 +985,7 @@ exports.testSubUsersCrudWithObject = function (test) { }; ufds.addUser(entry, function (err, su) { - test.ifError(err, 'addUser'); + assert.ifError(err, 'addUser'); test.strictEqual(su.login, entry.login, 'correct user returned'); /* @@ -993,18 +995,18 @@ exports.testSubUsersCrudWithObject = function (test) { */ test.strictEqual(su.account, entry.account, 'user has "account" set'); ufds.updateUser(su, update, su.account, function (err) { - test.ifError(err, 'updateUser'); + assert.ifError(err, 'updateUser'); ufds.getUserEx({ searchType: 'uuid', value: su.uuid, account: su.account }, function (err, su2) { - test.ifError(err, 'getUserEx'); + assert.ifError(err, 'getUserEx'); test.strictEqual(su2.phone, update.phone, 'phone updated'); su2.destroy(function (err) { - test.ifError(err, 'destroy user'); + assert.ifError(err, 'destroy user'); test.done(); }); }); @@ -1020,7 +1022,7 @@ exports.testSubUsersCrudWithObjectMismatchedAccount = function (test) { }; ufds.addUser(entry, function (err, su) { - test.ifError(err, 'addUser'); + assert.ifError(err, 'addUser'); test.strictEqual(su.login, entry.login, 'correct user returned'); /* @@ -1036,7 +1038,7 @@ exports.testSubUsersCrudWithObjectMismatchedAccount = function (test) { }, 'mismatch must trip assertion failure'); su.destroy(function (err) { - test.ifError(err, 'destroy user'); + assert.ifError(err, 'destroy user'); test.done(); }); }); @@ -1054,12 +1056,12 @@ exports.testAccountPolicies = function (test) { description: 'This is completely optional' }; ufds.addPolicy(ID, entry, function (err, policy) { - test.ifError(err, 'addPolicy error'); + assert.ifError(err, 'addPolicy error'); test.equal(policy.dn, util.format( 'policy-uuid=%s, uuid=%s, ou=users, o=smartdc', policy_uuid, ID)); ufds.listPolicies(ID, function (err, policies) { - test.ifError(err, 'listPolicies error'); + assert.ifError(err, 'listPolicies error'); test.ok(Array.isArray(policies), 'Array of policies'); test.equal(policies[0].dn, util.format( 'policy-uuid=%s, uuid=%s, ou=users, o=smartdc', @@ -1071,11 +1073,11 @@ exports.testAccountPolicies = function (test) { ]; ufds.modifyPolicy(ID, entry.uuid, entry, function (err, policy) { - test.ifError(err, 'modify policy error'); + assert.ifError(err, 'modify policy error'); test.equal(policy.rule.length, 2); ufds.deletePolicy(ID, entry.uuid, function (err) { - test.ifError(err, 'deletePolicy error'); + assert.ifError(err, 'deletePolicy error'); test.done(); }); }); @@ -1098,34 +1100,34 @@ exports.testAccountRoles = function (test) { uuid: role_uuid }; ufds.addRole(ID, entry, function (err, role) { - test.ifError(err, 'addGroup error'); + assert.ifError(err, 'addGroup error'); test.equal(role.dn, util.format( 'role-uuid=%s, uuid=%s, ou=users, o=smartdc', role_uuid, ID)); ufds.listRoles(ID, function (err, roles) { - test.ifError(err, 'listRoles error'); + assert.ifError(err, 'listRoles error'); test.ok(Array.isArray(roles), 'Array of roles'); test.equal(roles[0].dn, util.format( 'role-uuid=%s, uuid=%s, ou=users, o=smartdc', role_uuid, ID)); ufds.getUser(SUB_LOGIN, ID, function (err, subuser) { - test.ifError(err, 'sub user limits getUser error'); + assert.ifError(err, 'sub user limits getUser error'); test.ok(subuser, 'subuser'); subuser.roles(function (err, rls) { - test.ifError(err, 'sub user roles'); + assert.ifError(err, 'sub user roles'); test.ok(Array.isArray(rls), 'user roles is an array'); subuser.defaultRoles(function (err, drls) { - test.ifError(err, 'sub user default roles'); + assert.ifError(err, 'sub user default roles'); test.ok(Array.isArray(drls), 'sub user default roles is an array'); entry.description = 'This is completely optional'; ufds.modifyRole(ID, entry.uuid, entry, function (err, role) { - test.ifError(err, 'modify role error'); + assert.ifError(err, 'modify role error'); test.ok(role.description); ufds.deleteRole(ID, entry.uuid, function (err) { - test.ifError(err, 'deleteRole error'); + assert.ifError(err, 'deleteRole error'); test.done(); }); }); @@ -1149,7 +1151,7 @@ exports.testAccountDisabled = function (test) { funcs: [ function checkParent(_, cb) { ufds.getUser(ID, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(!user.disabled, 'account not disabled'); original = user; cb(err); @@ -1157,7 +1159,7 @@ exports.testAccountDisabled = function (test) { }, function checkChild(_, cb) { ufds.getUser(SUB_UUID, ID, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(!user.disabled, 'sub-account not disabled'); cb(err); }); @@ -1167,21 +1169,21 @@ exports.testAccountDisabled = function (test) { }, function afterParent(_, cb) { ufds.getUser(ID, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user.disabled, 'account disabled'); cb(err); }); }, function afterChild(_, cb) { ufds.getUser(SUB_UUID, ID, function (err, user) { - test.ifError(err); + assert.ifError(err); test.ok(user.disabled, 'sub-account disabled'); cb(err); }); } ] }, function (err, res) { - test.ifError(err); + assert.ifError(err); test.done(); }); }; @@ -1189,7 +1191,7 @@ exports.testAccountDisabled = function (test) { exports.testRemoveUserFromAccount = function (test) { ufds.deleteUser(SUB_LOGIN, ID, function (err) { - test.ifError(err); + assert.ifError(err); test.done(); }); }; @@ -1221,7 +1223,7 @@ exports.testHiddenControl = function (test) { ufds2.once('ready', function () { ufds2.removeAllListeners('error'); ufds2.getUser(LOGIN, function (err, user) { - test.ifError(err); + assert.ifError(err); test.equal(user.login, LOGIN); // Testing hidden attributes are available: test.ok(user._owner); @@ -1234,7 +1236,7 @@ exports.testHiddenControl = function (test) { ufds2.once('error', function (err) { ufds2.removeAllListeners('ready'); - test.ifError(err); + assert.ifError(err); test.done(); }); }; @@ -1251,12 +1253,12 @@ exports.testAccountResources = function (test) { uuid: res_uuid }; ufds.addResource(ID, entry, function (err, resource) { - test.ifError(err, 'addResource error'); + assert.ifError(err, 'addResource error'); test.equal(resource.dn, util.format( 'resource-uuid=%s, uuid=%s, ou=users, o=smartdc', res_uuid, ID)); ufds.listResources(ID, function (err, resources) { - test.ifError(err, 'listResources error'); + assert.ifError(err, 'listResources error'); test.ok(Array.isArray(resources), 'Array of resources'); test.equal(resources[0].dn, util.format( 'resource-uuid=%s, uuid=%s, ou=users, o=smartdc', @@ -1266,11 +1268,11 @@ exports.testAccountResources = function (test) { uuidv4(), ID)); ufds.modifyResource(ID, entry.uuid, entry, function (err, resource) { - test.ifError(err, 'modify resource error'); + assert.ifError(err, 'modify resource error'); test.equal(resource.memberrole.length, 2); ufds.deleteResource(ID, entry.uuid, function (err) { - test.ifError(err, 'deleteResource error'); + assert.ifError(err, 'deleteResource error'); test.done(); }); }); @@ -1278,35 +1280,8 @@ exports.testAccountResources = function (test) { }); }; - -exports.testAccountAccessKeys = function (t) { - ufds.addAccessKey(ID, function addCb(addErr, accKey) { - t.ifError(addErr, 'addAccessKey Error'); - t.ok(accKey, 'addded AccessKey'); - t.ok(accKey.accesskeyid, 'AccessKeyId'); - t.ok(accKey.accesskeysecret, 'AccessKeySecret'); - t.ok(accKey.created, 'AccessKey Created'); - ufds.getAccessKey(ID, accKey.accesskeyid, - function getCb(getErr, getKey) { - t.ifError(getErr, 'getAccessKey error'); - t.ok(getKey, 'getAccessKey key'); - ufds.listAccessKeys(ID, function listCb(listErr, listOfKeys) { - t.ifError(listErr, 'listAccessKeys error'); - t.ok(listOfKeys, 'List of access keys'); - t.ok(Array.isArray(listOfKeys, 'list of keys is an array')); - t.ok(listOfKeys[0], 'list of keys contains a key'); - ufds.deleteAccessKey(ID, accKey, function delCb(delErr) { - t.ifError(delErr, 'deleteAccessKey error'); - t.done(); - }); - }); - }); - }); -}; - - exports.tearDown = function (callback) { ufds.close(function () { callback(); }); -}; +}; \ No newline at end of file