UNPKG

@cipherstash/jseql

Version:

Encrypted Query Language JavaScript Library

523 lines (517 loc) 14 kB
import { LockContext, logger } from "./chunk-2GFWYC6S.js"; // src/ffi/index.ts import { newClient, encrypt as ffiEncrypt, decrypt as ffiDecrypt, encryptBulk as ffiEncryptBulk, decryptBulk as ffiDecryptBulk } from "@cipherstash/jseql-ffi"; // src/ffi/env-check.ts var message = ""; var errorMessage = (message2) => `Initialization error: ${message2}`; var checkEnvironmentVariables = () => { if (!process.env.CS_WORKSPACE_ID) { message = errorMessage( 'The environment variable "CS_WORKSPACE_ID" must be set. You can find your workspace ID in the CipherStash dashboard.' ); logger.error(message); throw new Error(`[jseql]: ${message}`); } if (!process.env.CS_CLIENT_ID || !process.env.CS_CLIENT_KEY) { message = errorMessage( 'The environment variables "CS_CLIENT_ID" and "CS_CLIENT_KEY" must be set. You must use the CipherStash CLI to generate a new client key pair.' ); logger.error(message); throw new Error(`[jseql]: ${message}`); } if (!process.env.CS_CLIENT_ACCESS_KEY) { message = errorMessage( 'The environment variable "CS_CLIENT_ACCESS_KEY" must be set. Generate a new access token in the CipherStash dashboard or CLI.' ); logger.error(message); throw new Error(`[jseql]: ${message}`); } }; // src/ffi/payload-helpers.ts var getLockContextPayload = (lockContext) => { const context = lockContext.getLockContext(); if (!context.ctsToken?.accessToken) { throw new Error( "[jseql]: LockContext must be initialized with a valid CTS token before using it." ); } return { lockContext: context.context }; }; var normalizeBulkDecryptPayloads = (encryptedPayloads) => encryptedPayloads?.reduce((acc, encryptedPayload) => { const payload = { ciphertext: encryptedPayload.c }; acc.push(payload); return acc; }, []); var normalizeBulkEncryptPayloads = (plaintexts, column) => plaintexts.reduce((acc, plaintext) => { const payload = { plaintext: plaintext.plaintext, column }; acc.push(payload); return acc; }, []); var normalizeBulkDecryptPayloadsWithLockContext = (encryptedPayloads, lockContext) => encryptedPayloads?.reduce((acc, encryptedPayload) => { const payload = { ciphertext: encryptedPayload.c, ...getLockContextPayload(lockContext) }; acc.push(payload); return acc; }, []); var normalizeBulkEncryptPayloadsWithLockContext = (plaintexts, column, lockContext) => plaintexts.reduce((acc, plaintext) => { const payload = { plaintext: plaintext.plaintext, column, ...getLockContextPayload(lockContext) }; acc.push(payload); return acc; }, []); // src/ffi/index.ts var noClientError = () => new Error( "The EQL client has not been initialized. Please call init() before using the client." ); var EncryptOperation = class { client; plaintext; column; table; constructor(client, plaintext, opts) { this.client = client; this.plaintext = plaintext; this.column = opts.column; this.table = opts.table; } withLockContext(lockContext) { return new EncryptOperationWithLockContext(this, lockContext); } /** Implement the PromiseLike interface so `await` works. */ then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } /** Actual encryption logic, deferred until `then()` is called. */ async execute() { if (!this.client) { throw noClientError(); } if (this.plaintext === null) { return null; } logger.debug("Encrypting data WITHOUT a lock context", { column: this.column, table: this.table }); const val = await ffiEncrypt(this.client, this.plaintext, this.column); return { c: val }; } getOperation() { return { client: this.client, plaintext: this.plaintext, column: this.column, table: this.table }; } }; var EncryptOperationWithLockContext = class { operation; lockContext; constructor(operation, lockContext) { this.operation = operation; this.lockContext = lockContext; } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { const { client, plaintext, column, table } = this.operation.getOperation(); if (!client) { throw noClientError(); } if (plaintext === null) { return null; } logger.debug("Encrypting data WITH a lock context"); const context = this.lockContext?.getLockContext(); if (!context?.success) { throw new Error(`[jseql]: ${context?.error}`); } const val = await ffiEncrypt( client, plaintext, column, context.context, context.ctsToken ); return { c: val }; } }; var DecryptOperation = class { client; encryptedPayload; constructor(client, encryptedPayload) { this.client = client; this.encryptedPayload = encryptedPayload; } withLockContext(lockContext) { return new DecryptOperationWithLockContext(this, lockContext); } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { if (!this.client) { throw noClientError(); } if (this.encryptedPayload === null) { return null; } logger.debug("Decrypting data WITHOUT a lock context"); return await ffiDecrypt(this.client, this.encryptedPayload.c); } getOperation() { return { client: this.client, encryptedPayload: this.encryptedPayload }; } }; var DecryptOperationWithLockContext = class { operation; lockContext; constructor(operation, lockContext) { this.operation = operation; this.lockContext = lockContext; } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { const { client, encryptedPayload } = this.operation.getOperation(); if (!client) { throw noClientError(); } if (encryptedPayload === null) { return null; } logger.debug("Decrypting data WITH a lock context"); const context = this.lockContext?.getLockContext(); if (!context?.success) { throw new Error(`[jseql]: ${context?.error}`); } return await ffiDecrypt( client, encryptedPayload.c, context.context, context.ctsToken ); } }; var BulkEncryptOperation = class { client; plaintexts; column; table; constructor(client, plaintexts, opts) { this.client = client; this.plaintexts = plaintexts; this.column = opts.column; this.table = opts.table; } withLockContext(lockContext) { return new BulkEncryptOperationWithLockContext(this, lockContext); } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { if (!this.client) { throw noClientError(); } if (!this.plaintexts || this.plaintexts.length === 0) { return null; } const encryptPayloads = normalizeBulkEncryptPayloads( this.plaintexts, this.column ); logger.debug("Bulk encrypting data WITHOUT a lock context", { column: this.column, table: this.table }); const encryptedData = await ffiEncryptBulk(this.client, encryptPayloads); return encryptedData.map((enc, index) => ({ c: enc, id: this.plaintexts[index].id })); } getOperation() { return { client: this.client, plaintexts: this.plaintexts, column: this.column, table: this.table }; } }; var BulkEncryptOperationWithLockContext = class { operation; lockContext; constructor(operation, lockContext) { this.operation = operation; this.lockContext = lockContext; } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { const { client, plaintexts, column, table } = this.operation.getOperation(); if (!client) { throw noClientError(); } if (!plaintexts || plaintexts.length === 0) { return null; } const encryptPayloads = normalizeBulkEncryptPayloadsWithLockContext( plaintexts, column, this.lockContext ); logger.debug("Bulk encrypting data WITH a lock context", { column, table }); const context = this.lockContext.getLockContext(); if (!context.success) { throw new Error(`[jseql]: ${context?.error}`); } const encryptedData = await ffiEncryptBulk( client, encryptPayloads, context.ctsToken ); return encryptedData.map((enc, index) => ({ c: enc, id: plaintexts[index].id })); } }; var BulkDecryptOperation = class { client; encryptedPayloads; constructor(client, encryptedPayloads) { this.client = client; this.encryptedPayloads = encryptedPayloads; } withLockContext(lockContext) { return new BulkDecryptOperationWithLockContext(this, lockContext); } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { if (!this.client) { throw noClientError(); } if (!this.encryptedPayloads) { return null; } const decryptPayloads = normalizeBulkDecryptPayloads(this.encryptedPayloads); if (!decryptPayloads) { return null; } logger.debug("Bulk decrypting data WITHOUT a lock context"); const decryptedData = await ffiDecryptBulk(this.client, decryptPayloads); return decryptedData.map((dec, index) => { if (!this.encryptedPayloads) return null; return { plaintext: dec, id: this.encryptedPayloads[index].id }; }); } getOperation() { return { client: this.client, encryptedPayloads: this.encryptedPayloads }; } }; var BulkDecryptOperationWithLockContext = class { operation; lockContext; constructor(operation, lockContext) { this.operation = operation; this.lockContext = lockContext; } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { const { client, encryptedPayloads } = this.operation.getOperation(); if (!client) { throw noClientError(); } if (!encryptedPayloads) { return null; } const decryptPayloads = normalizeBulkDecryptPayloadsWithLockContext( encryptedPayloads, this.lockContext ); if (!decryptPayloads) { return null; } logger.debug("Bulk decrypting data WITH a lock context"); const context = this.lockContext.getLockContext(); if (!context.success) { throw new Error(`[jseql]: ${context?.error}`); } const decryptedData = await ffiDecryptBulk( client, decryptPayloads, context.ctsToken ); return decryptedData.map((dec, index) => { if (!encryptedPayloads) return null; return { plaintext: dec, id: encryptedPayloads[index].id }; }); } }; var EqlClient = class { client; workspaceId; constructor() { checkEnvironmentVariables(); logger.info( "Successfully initialized the EQL client with your defined environment variables." ); this.workspaceId = process.env.CS_WORKSPACE_ID; } async init() { const c = await newClient(); this.client = c; return this; } /** * Encryption - returns a thenable object. * Usage: * await eqlClient.encrypt(plaintext, { column, table }) * await eqlClient.encrypt(plaintext, { column, table }).withLockContext(lockContext) */ encrypt(plaintext, opts) { if (!this.client) { throw noClientError(); } return new EncryptOperation(this.client, plaintext, opts); } /** * Decryption - returns a thenable object. * Usage: * await eqlClient.decrypt(encryptedPayload) * await eqlClient.decrypt(encryptedPayload).withLockContext(lockContext) */ decrypt(encryptedPayload) { if (!this.client) { throw noClientError(); } return new DecryptOperation(this.client, encryptedPayload); } /** * Bulk Encrypt - returns a thenable object. * Usage: * await eqlClient.bulkEncrypt([{ plaintext, id }, ...], { column, table }) * await eqlClient * .bulkEncrypt([{ plaintext, id }, ...], { column, table }) * .withLockContext(lockContext) */ bulkEncrypt(plaintexts, opts) { if (!this.client) { throw noClientError(); } return new BulkEncryptOperation(this.client, plaintexts, opts); } /** * Bulk Decrypt - returns a thenable object. * Usage: * await eqlClient.bulkDecrypt(encryptedPayloads) * await eqlClient.bulkDecrypt(encryptedPayloads).withLockContext(lockContext) */ bulkDecrypt(encryptedPayloads) { if (!this.client) { throw noClientError(); } return new BulkDecryptOperation(this.client, encryptedPayloads); } /** e.g., debugging or environment info */ clientInfo() { return { workspaceId: this.workspaceId }; } }; // src/eql/index.ts var createEqlPayload = ({ plaintext, table, column, schemaVersion = 1, queryType = null }) => { const payload = { v: schemaVersion, k: "pt", p: plaintext ?? "", i: { t: table, c: column } }; if (queryType) { payload.q = queryType; } logger.debug("Creating the EQL payload", payload); return payload; }; var getPlaintext = (payload) => { if (payload?.p && payload?.k === "pt") { logger.debug("Returning the plaintext data from the EQL payload", payload); return { failure: false, plaintext: payload.p }; } logger.error("No plaintext data found in the EQL payload", payload ?? {}); return { failure: true, error: new Error("No plaintext data found in the EQL payload") }; }; // src/index.ts var eql = () => { const client = new EqlClient(); return client.init(); }; export { LockContext, createEqlPayload, eql, getPlaintext }; //# sourceMappingURL=index.js.map