box-node-sdk
Version:
Official SDK for Box Platform APIs
241 lines • 9.66 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultPrivateKeyDecryptor = exports.Hash = exports.utilLib = exports.FormData = exports.ByteStream = exports.Buffer = void 0;
exports.generateByteBuffer = generateByteBuffer;
exports.generateReadableStreamFromFile = generateReadableStreamFromFile;
exports.generateByteStreamFromBuffer = generateByteStreamFromBuffer;
exports.decodeBase64ByteStream = decodeBase64ByteStream;
exports.stringToByteStream = stringToByteStream;
exports.readByteStream = readByteStream;
exports.iterateChunks = iterateChunks;
exports.createJwtAssertion = createJwtAssertion;
exports.readTextFromFile = readTextFromFile;
exports.createAgent = createAgent;
exports.jsonStringifyWithEscapedUnicode = jsonStringifyWithEscapedUnicode;
exports.computeWebhookSignature = computeWebhookSignature;
exports.compareSignatures = compareSignatures;
exports.random = random;
exports.calculateMD5Hash = calculateMD5Hash;
exports.getEnvVar = getEnvVar;
exports.setEnvVar = setEnvVar;
const buffer_1 = require("buffer");
Object.defineProperty(exports, "Buffer", { enumerable: true, get: function () { return buffer_1.Buffer; } });
const stream_1 = require("stream");
Object.defineProperty(exports, "ByteStream", { enumerable: true, get: function () { return stream_1.Readable; } });
const jose_1 = require("jose");
const crypto_1 = __importDefault(require("crypto"));
const fs_1 = __importDefault(require("fs"));
const proxy_agent_1 = require("proxy-agent");
const form_data_1 = __importDefault(require("form-data"));
Object.defineProperty(exports, "FormData", { enumerable: true, get: function () { return form_data_1.default; } });
const util_1 = __importDefault(require("util"));
exports.utilLib = util_1.default;
class Hash {
hash;
algorithm;
constructor({ algorithm }) {
this.algorithm = algorithm;
this.hash = crypto_1.default.createHash(algorithm);
}
async updateHash(data) {
this.hash.update(data);
}
async digestHash(encoding = 'base64') {
return this.hash.digest(encoding);
}
}
exports.Hash = Hash;
function generateByteBuffer(size) {
return crypto_1.default.randomBytes(size);
}
function generateReadableStreamFromFile(file, chunkSize = 1024 * 1024) {
throw new Error('This function is only supported in the browser');
}
function generateByteStreamFromBuffer(buffer) {
const buf = buffer_1.Buffer.isBuffer(buffer) ? buffer : buffer_1.Buffer.from(buffer);
return stream_1.Readable.from(buf);
}
function decodeBase64ByteStream(data) {
return stream_1.Readable.from(buffer_1.Buffer.from(data, 'base64'));
}
function stringToByteStream(data) {
return stream_1.Readable.from(buffer_1.Buffer.from(data, 'ascii'));
}
async function readByteStream(byteStream) {
const buffers = [];
for await (const data of byteStream) {
buffers.push(data);
}
return buffer_1.Buffer.concat(buffers);
}
async function* iterateChunks(stream, chunkSize, fileSize) {
let buffers = [];
let totalSize = 0;
let consumedSize = 0;
while (consumedSize < fileSize && !stream.readableEnded) {
for await (const chunk of stream) {
const data = buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(chunk);
if (!buffer_1.Buffer.isBuffer(data)) {
throw new Error('Expecting a chunk of stream to be a Buffer');
}
consumedSize += data.length;
buffers.push(data);
totalSize += data.length;
if (totalSize < chunkSize) {
continue;
}
const buffer = buffer_1.Buffer.concat(buffers);
let start = 0;
while (totalSize >= chunkSize) {
yield generateByteStreamFromBuffer(buffer.subarray(start, start + chunkSize));
start += chunkSize;
totalSize -= chunkSize;
}
buffers = totalSize > 0 ? [buffer.subarray(start)] : [];
}
}
if (consumedSize !== fileSize) {
throw new Error(`Stream size ${consumedSize} does not match expected file size ${fileSize}`);
}
if (totalSize > 0) {
yield generateByteStreamFromBuffer(buffer_1.Buffer.concat(buffers));
}
}
class DefaultPrivateKeyDecryptor {
constructor(fields) { }
decryptPrivateKey(encryptedPrivateKey, passphrase) {
const privateKey = crypto_1.default.createPrivateKey({
key: encryptedPrivateKey,
format: 'pem',
type: 'pkcs8',
passphrase: passphrase,
});
const pem = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString();
return pem;
}
}
exports.DefaultPrivateKeyDecryptor = DefaultPrivateKeyDecryptor;
/**
* Creates a JWT assertion.
*
* @param claims
* @param key
* @param options
* @returns
*/
async function createJwtAssertion(claims, key, options) {
const pem = options.privateKeyDecryptor?.decryptPrivateKey(key.key, key.passphrase);
if (!pem) {
throw new Error(`Decrypted jwt private key is empty`);
}
const pkcs8 = await (0, jose_1.importPKCS8)(pem, options.algorithm || 'RS256');
let signer = new jose_1.SignJWT(claims);
signer = options.audience ? signer.setAudience(options.audience) : signer;
signer = options.expiresIn
? signer.setExpirationTime(options.expiresIn)
: signer;
signer = options.issuer ? signer.setIssuer(options.issuer) : signer;
signer = options.jwtid ? signer.setJti(options.jwtid) : signer;
signer = options.notBefore ? signer.setNotBefore(options.notBefore) : signer;
signer = options.subject ? signer.setSubject(options.subject) : signer;
signer = options.algorithm
? signer.setProtectedHeader({ alg: options.algorithm })
: signer;
signer = signer.setIssuedAt();
return await signer.sign(pkcs8);
}
/**
* Reads a text file and returns its content.
*/
function readTextFromFile(filepath) {
return fs_1.default.readFileSync(filepath, 'utf8');
}
/**
* Create web agent from proxy agent options.
*/
function createAgent(options, proxyConfig) {
let agentOptions = options;
if (proxyConfig && proxyConfig.url) {
if (!proxyConfig.url.startsWith('http')) {
throw new Error('Invalid proxy URL');
}
const proxyHost = proxyConfig.url.split('//')[1];
const proxyAuth = proxyConfig.username && proxyConfig.password
? `${proxyConfig.username}:${proxyConfig.password}@`
: '';
const proxyUrl = `http://${proxyAuth}${proxyHost}`;
agentOptions = Object.assign({ getProxyForUrl: (url) => proxyUrl }, options || {});
}
return agentOptions ? new proxy_agent_1.ProxyAgent(agentOptions) : new proxy_agent_1.ProxyAgent();
}
/**
* Stringify JSON with escaped multibyte Unicode characters and slashes to ensure computed signatures match PHP's default behavior
*
* @param {Object} body - The parsed JSON object
* @returns {string} - Stringified JSON with escaped multibyte Unicode characters
* @private
*/
function jsonStringifyWithEscapedUnicode(body) {
return body
.replace(/[\u007f-\uffff]/g, (char) => `\\u${`0000${char.charCodeAt(0).toString(16)}`.slice(-4)}`)
.replace(/(?<!\\)\//g, '\\/');
}
/**
* Compute the message signature
* @see {@Link https://developer.box.com/en/guides/webhooks/handle/setup-signatures/}
*
* @param {string} body - The request body of the webhook message
* @param {Object} headers - The request headers of the webhook message
* @param {string} signatureKey - The signature to verify the message with
* @param {string} escapeBody - Indicates if payload should be escaped or left as is
* @returns {?string} - The message signature (or null, if it can't be computed)
* @private
*/
async function computeWebhookSignature(body, headers, signatureKey, escapeBody = false) {
if (headers['box-signature-version'] !== '1') {
return null;
}
if (headers['box-signature-algorithm'] !== 'HmacSHA256') {
return null;
}
let signature = null;
const escapedBody = escapeBody ? jsonStringifyWithEscapedUnicode(body) : body;
let hmac = crypto_1.default.createHmac('sha256', signatureKey);
hmac.update(escapedBody);
hmac.update(headers['box-delivery-timestamp']);
signature = hmac.digest('base64');
return signature;
}
async function compareSignatures(expectedSignature, receivedSignature) {
if (!expectedSignature || !receivedSignature) {
return false;
}
const expectedBuffer = buffer_1.Buffer.from(expectedSignature, 'base64');
const receivedBuffer = buffer_1.Buffer.from(receivedSignature, 'base64');
if (expectedBuffer.length !== receivedBuffer.length) {
return false;
}
return crypto_1.default.timingSafeEqual(expectedBuffer, receivedBuffer);
}
function random(min, max) {
return Math.random() * (max - min) + min;
}
async function calculateMD5Hash(data) {
return crypto_1.default.createHash('sha1').update(data).digest('hex');
}
function getEnvVar(name) {
if (typeof process === 'undefined' || !process.env) {
throw new Error('This function requires a Node.js environment');
}
return process.env[name] || '';
}
function setEnvVar(name, value) {
if (typeof process === 'undefined' || !process.env) {
throw new Error('This function requires a Node.js environment');
}
process.env[name] = value;
}
//# sourceMappingURL=utilsNode.js.map