Fix commonjs import/export for patchRouterParam and make secret versioning/snapshots compatible with prev unversioned secrets

This commit is contained in:
Tuan Dang
2022-12-26 17:52:13 -05:00
parent 9f724b5ede
commit 0c6dfbe4b4
5 changed files with 67 additions and 49 deletions
+6 -4
View File
@@ -1,4 +1,6 @@
import { patchRouterParam } from './utils/patchAsyncRoutes'; // eslint-disable-next-line @typescript-eslint/no-var-requires
const { patchRouterParam } = require('./utils/patchAsyncRoutes');
import express from 'express'; import express from 'express';
import helmet from 'helmet'; import helmet from 'helmet';
import cors from 'cors'; import cors from 'cors';
@@ -6,7 +8,7 @@ import cookieParser from 'cookie-parser';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
dotenv.config(); dotenv.config();
import { PORT, NODE_ENV, SITE_URL, LICENSE_KEY } from './config'; import { PORT, NODE_ENV, SITE_URL } from './config';
import { apiLimiter } from './helpers/rateLimiter'; import { apiLimiter } from './helpers/rateLimiter';
import { import {
@@ -38,8 +40,8 @@ import { getLogger } from './utils/logger';
import { RouteNotFoundError } from './utils/errors'; import { RouteNotFoundError } from './utils/errors';
import { requestErrorHandler } from './middleware/requestErrorHandler'; import { requestErrorHandler } from './middleware/requestErrorHandler';
//* Patch Async route params to handle Promise Rejections // patch async route params to handle Promise Rejections
patchRouterParam() patchRouterParam();
export const app = express(); export const app = express();
@@ -19,10 +19,8 @@ class EESecretService {
* @param {String} obj.workspaceId * @param {String} obj.workspaceId
*/ */
static async takeSecretSnapshot({ static async takeSecretSnapshot({
licenseKey,
workspaceId workspaceId
}: { }: {
licenseKey: string;
workspaceId: string; workspaceId: string;
}) { }) {
if (!EELicenseService.isLicenseValid) return; if (!EELicenseService.isLicenseValid) return;
+54 -39
View File
@@ -56,6 +56,7 @@ const pushSecrets = async ({
environment: string; environment: string;
secrets: PushSecret[]; secrets: PushSecret[];
}): Promise<void> => { }): Promise<void> => {
// TODO: clean up function and fix up types
try { try {
// construct useful data structures // construct useful data structures
const oldSecrets = await pullSecrets({ const oldSecrets = await pullSecrets({
@@ -63,10 +64,11 @@ const pushSecrets = async ({
workspaceId, workspaceId,
environment environment
}); });
const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) => const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) =>
({ ...accumulator, [`${s.type}-${s.secretKeyHash}`]: s }) ({ ...accumulator, [`${s.type}-${s.secretKeyHash}`]: s })
, {}); , {});
const newSecretsObj = secrets.reduce((accumulator, s) => const newSecretsObj: any = secrets.reduce((accumulator, s) =>
({ ...accumulator, [`${s.type}-${s.hashKey}`]: s }) ({ ...accumulator, [`${s.type}-${s.hashKey}`]: s })
, {}); , {});
@@ -79,8 +81,6 @@ const pushSecrets = async ({
if (toDelete.length > 0) { if (toDelete.length > 0) {
await Secret.deleteMany({ await Secret.deleteMany({
_id: { $in: toDelete } _id: { $in: toDelete }
}, {
rawResult: true
}); });
await SecretVersion.updateMany({ await SecretVersion.updateMany({
@@ -89,31 +89,48 @@ const pushSecrets = async ({
isDeleted: true isDeleted: true
}); });
} }
// handle modifying secrets where type or value changed const toUpdate = oldSecrets
const toUpdate = secrets
.filter((s) => { .filter((s) => {
if (`${s.type}-${s.hashKey}` in oldSecretsObj) { if (`${s.type}-${s.secretKeyHash}` in newSecretsObj) {
if (s.hashValue !== oldSecretsObj[`${s.type}-${s.hashKey}`].secretValueHash) { if (s.secretValueHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashValue) {
// case: filter secrets where value changed // case: filter secrets where value changed
return true; return true;
} }
}
if (!s.version) {
// case: filter (legacy) secrets that were not versioned
return true;
}
}
return false; return false;
}); });
const operations = toUpdate const operations = toUpdate
.map((s) => { .map((s) => {
const {
ciphertextValue,
ivValue,
tagValue,
hashValue
} = newSecretsObj[`${s.type}-${s.secretKeyHash}`];
const update: Update = { const update: Update = {
secretValueCiphertext: s.ciphertextValue, secretValueCiphertext: ciphertextValue,
secretValueIV: s.ivValue, secretValueIV: ivValue,
secretValueTag: s.tagValue, secretValueTag: tagValue,
secretValueHash: s.hashValue, secretValueHash: hashValue
$inc: { }
if (!s.version) {
// case: (legacy) secret was not versioned
update.version = 1;
} else {
update['$inc'] = {
version: 1 version: 1
} }
}; }
if (s.type === SECRET_PERSONAL) { if (s.type === SECRET_PERSONAL) {
// attach user associated with the personal secret // attach user associated with the personal secret
@@ -123,7 +140,7 @@ const pushSecrets = async ({
return { return {
updateOne: { updateOne: {
filter: { filter: {
_id: oldSecretsObj[`${s.type}-${s.hashKey}`]._id _id: oldSecretsObj[`${s.type}-${s.secretKeyHash}`]._id
}, },
update update
} }
@@ -134,28 +151,26 @@ const pushSecrets = async ({
// (EE) add secret versions for updated secrets // (EE) add secret versions for updated secrets
await EESecretService.addSecretVersions({ await EESecretService.addSecretVersions({
secretVersions: toUpdate.map(({ secretVersions: toUpdate.map(({
_id,
version,
type, type,
ciphertextKey, secretKeyHash,
ivKey, }) => {
tagKey, const newSecret = newSecretsObj[`${type}-${secretKeyHash}`];
hashKey, return ({
ciphertextValue, secret: _id,
ivValue, version: version ? version + 1 : 1,
tagValue, isDeleted: false,
hashValue secretKeyCiphertext: newSecret.ciphertextKey,
}) => ({ secretKeyIV: newSecret.ivKey,
secret: oldSecretsObj[`${type}-${hashKey}`]._id, secretKeyTag: newSecret.tagKey,
version: oldSecretsObj[`${type}-${hashKey}`].version + 1, secretKeyHash: newSecret.hashKey,
isDeleted: false, secretValueCiphertext: newSecret.ciphertextValue,
secretKeyCiphertext: ciphertextKey, secretValueIV: newSecret.ivValue,
secretKeyIV: ivKey, secretValueTag: newSecret.tagValue,
secretKeyTag: tagKey, secretValueHash: newSecret.hashValue
secretKeyHash: hashKey, })
secretValueCiphertext: ciphertextValue, })
secretValueIV: ivValue,
secretValueTag: tagValue,
secretValueHash: hashValue
}))
}); });
// handle adding new secrets // handle adding new secrets
@@ -166,6 +181,7 @@ const pushSecrets = async ({
const newSecrets = await Secret.insertMany( const newSecrets = await Secret.insertMany(
toAdd.map((s, idx) => { toAdd.map((s, idx) => {
const obj: any = { const obj: any = {
version: 1,
workspace: workspaceId, workspace: workspaceId,
type: toAdd[idx].type, type: toAdd[idx].type,
environment, environment,
@@ -217,7 +233,6 @@ const pushSecrets = async ({
// (EE) take a secret snapshot // (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({ await EESecretService.takeSecretSnapshot({
licenseKey: LICENSE_KEY,
workspaceId workspaceId
}) })
} catch (err) { } catch (err) {
+1 -2
View File
@@ -29,8 +29,7 @@ const secretSchema = new Schema<ISecret>(
{ {
version: { version: {
type: Number, type: Number,
default: 1, required: true
required: true
}, },
workspace: { workspace: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
+6 -2
View File
@@ -45,7 +45,7 @@ function wrap(fn) {
return copyFnProps(fn, newFn); return copyFnProps(fn, newFn);
} }
export function patchRouterParam() { function patchRouterParam() {
const originalParam = Router.prototype.constructor.param; const originalParam = Router.prototype.constructor.param;
Router.prototype.constructor.param = function param(name, fn) { Router.prototype.constructor.param = function param(name, fn) {
fn = wrap(fn); fn = wrap(fn);
@@ -62,4 +62,8 @@ Object.defineProperty(Layer.prototype, 'handle', {
fn = wrap(fn); fn = wrap(fn);
this.__handle = fn; this.__handle = fn;
}, },
}); });
module.exports = {
patchRouterParam
};