And workspace-environment specific integrations syncs to secrets v3 endpoints, add PostHog

This commit is contained in:
Tuan Dang
2023-04-17 14:23:56 +03:00
parent 338d287d35
commit 763ec1aa0f
18 changed files with 281 additions and 294 deletions
+4 -4
View File
@@ -151,7 +151,7 @@ const getSecretsHelper = async ({
workspaceId,
environment
}: {
workspaceId: string;
workspaceId: Types.ObjectId;
environment: string;
}) => {
const content = {} as any;
@@ -196,7 +196,7 @@ const getSecretsHelper = async ({
* @param {String} obj.workspaceId - id of workspace
* @returns {String} key - decrypted workspace key
*/
const getKey = async ({ workspaceId }: { workspaceId: string }) => {
const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => {
let key;
try {
const botKey = await BotKey.findOne({
@@ -245,7 +245,7 @@ const encryptSymmetricHelper = async ({
workspaceId,
plaintext
}: {
workspaceId: string;
workspaceId: Types.ObjectId;
plaintext: string;
}) => {
@@ -282,7 +282,7 @@ const decryptSymmetricHelper = async ({
iv,
tag
}: {
workspaceId: string;
workspaceId: Types.ObjectId;
ciphertext: string;
iv: string;
tag: string;
+10 -4
View File
@@ -1,11 +1,13 @@
import { Bot, IBot } from '../models';
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import { Bot, IBot } from '../models';
import { EVENT_PUSH_SECRETS } from '../variables';
import { IntegrationService } from '../services';
interface Event {
name: string;
workspaceId: string;
workspaceId: Types.ObjectId;
environment?: string;
payload: any;
}
@@ -22,7 +24,10 @@ const handleEventHelper = async ({
}: {
event: Event;
}) => {
const { workspaceId } = event;
const {
workspaceId,
environment
} = event;
// TODO: moduralize bot check into separate function
const bot = await Bot.findOne({
@@ -36,7 +41,8 @@ const handleEventHelper = async ({
switch (event.name) {
case EVENT_PUSH_SECRETS:
IntegrationService.syncIntegrations({
workspaceId
workspaceId,
environment
});
break;
}
+14 -9
View File
@@ -217,14 +217,19 @@ const handleOAuthExchangeHelper = async ({
* @param {Object} obj.workspaceId - id of workspace
*/
const syncIntegrationsHelper = async ({
workspaceId
workspaceId,
environment
}: {
workspaceId: string;
workspaceId: Types.ObjectId;
environment?: string;
}) => {
let integrations;
try {
integrations = await Integration.find({
workspace: workspaceId,
...(environment ? {
environment
} : {}),
isActive: true,
app: { $ne: null }
});
@@ -234,7 +239,7 @@ const syncIntegrationsHelper = async ({
for await (const integration of integrations) {
// get workspace, environment (shared) secrets
const secrets = await BotService.getSecrets({ // issue here?
workspaceId: integration.workspace.toString(),
workspaceId: integration.workspace,
environment: integration.environment
});
@@ -281,7 +286,7 @@ const syncIntegrationsHelper = async ({
if (!integrationAuth) throw UnauthorizedRequestError({message: 'Failed to locate Integration Authentication credentials'});
refreshToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.refreshCiphertext as string,
iv: integrationAuth.refreshIV as string,
tag: integrationAuth.refreshTag as string
@@ -318,7 +323,7 @@ const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrati
if (!integrationAuth) throw UnauthorizedRequestError({message: 'Failed to locate Integration Authentication credentials'});
accessToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.accessCiphertext as string,
iv: integrationAuth.accessIV as string,
tag: integrationAuth.accessTag as string
@@ -340,7 +345,7 @@ const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrati
if (integrationAuth?.accessIdCiphertext && integrationAuth?.accessIdIV && integrationAuth?.accessIdTag) {
accessId = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.accessIdCiphertext as string,
iv: integrationAuth.accessIdIV as string,
tag: integrationAuth.accessIdTag as string
@@ -386,7 +391,7 @@ const setIntegrationAuthRefreshHelper = async ({
if (!integrationAuth) throw new Error('Failed to find integration auth');
const obj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
plaintext: refreshToken
});
@@ -435,14 +440,14 @@ const setIntegrationAuthAccessHelper = async ({
if (!integrationAuth) throw new Error('Failed to find integration auth');
const encryptedAccessTokenObj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
plaintext: accessToken
});
let encryptedAccessIdObj;
if (accessId) {
encryptedAccessIdObj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace.toString(),
workspaceId: integrationAuth.workspace,
plaintext: accessId
});
}
+138 -17
View File
@@ -6,21 +6,18 @@ import {
UpdateSecretParams,
DeleteSecretParams
} from '../interfaces/services/SecretService';
import {
AuthData
} from '../interfaces/middleware';
import {
User,
IUser,
ServiceAccount,
IServiceAccount,
ServiceTokenData,
IServiceTokenData,
Secret,
ISecret,
SecretBlindIndexData,
} from '../models';
import {
IAction,
SecretVersion
} from '../ee/models';
import { SecretVersion } from '../ee/models';
import {
validateMembership
} from '../helpers/membership';
@@ -55,8 +52,12 @@ import {
} from '../variables';
import crypto from 'crypto';
import * as argon2 from 'argon2';
import { decryptSymmetric } from '../utils/crypto';
import {
encryptSymmetric,
decryptSymmetric
} from '../utils/crypto';
import { getEncryptionKey } from '../config';
import { TelemetryService } from '../services';
import {
EESecretService,
EELogService
@@ -80,10 +81,7 @@ const validateClientForSecret = async ({
acceptedRoles,
requiredPermissions
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
},
authData: AuthData;
secretId: Types.ObjectId;
acceptedRoles: Array<'admin' | 'member'>;
requiredPermissions: string[];
@@ -157,10 +155,7 @@ const validateClientForSecrets = async ({
secretIds,
requiredPermissions
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
},
authData: AuthData;
secretIds: Types.ObjectId[];
requiredPermissions: string[];
}) => {
@@ -222,6 +217,39 @@ const validateClientForSecrets = async ({
});
}
/**
* Create secret blind index data containing encrypted blind index salt
* for workspace with id [workspaceId]
* @param {Object} obj
* @param {Types.ObjectId} obj.workspaceId
*/
const createSecretBlindIndexDataHelper = async ({
workspaceId
}: {
workspaceId: Types.ObjectId;
}) => {
// initialize random blind index salt for workspace
const salt = crypto.randomBytes(16).toString('base64');
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: getEncryptionKey()
});
const secretBlindIndexData = await new SecretBlindIndexData({
workspace: workspaceId,
encryptedSaltCiphertext,
saltIV,
saltTag
}).save();
return secretBlindIndexData;
}
/**
* Generate blind index for secret with name [secretName]
* for workspace with id [workspaceId]
@@ -389,6 +417,24 @@ const createSecretHelper = async ({
await EESecretService.takeSecretSnapshot({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: TelemetryService.getDistinctId({
authData
}),
properties: {
numberOfSecrets: 1,
environment,
workspaceId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
}
});
}
return secret;
}
@@ -430,6 +476,7 @@ const getSecretsHelper = async ({
}
}));
// (EE) create (audit) log
const action = await EELogService.createAction({
name: ACTION_READ_SECRETS,
...getAuthDataPayloadIdObj(authData),
@@ -445,6 +492,24 @@ const getSecretsHelper = async ({
ipAddress: authData.authIP
});
const postHogClient = TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets pulled',
distinctId: TelemetryService.getDistinctId({
authData
}),
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
}
});
}
return secrets;
}
@@ -501,6 +566,7 @@ const getSecretHelper = async ({
if (!secret) throw SecretNotFoundError();
// (EE) create (audit) log
const action = await EELogService.createAction({
name: ACTION_READ_SECRETS,
...getAuthDataPayloadIdObj(authData),
@@ -516,6 +582,24 @@ const getSecretHelper = async ({
ipAddress: authData.authIP
});
const postHogClient = TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets pull',
distinctId: TelemetryService.getDistinctId({
authData
}),
properties: {
numberOfSecrets: 1,
environment,
workspaceId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
}
});
}
return secret;
}
@@ -610,7 +694,7 @@ const updateSecretHelper = async ({
secretValueTag
});
// // (EE) add version for new secret
// (EE) add version for new secret
await EESecretService.addSecretVersions({
secretVersions: [secretVersion]
});
@@ -636,6 +720,24 @@ const updateSecretHelper = async ({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: TelemetryService.getDistinctId({
authData
}),
properties: {
numberOfSecrets: 1,
environment,
workspaceId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
}
});
}
return secret;
}
@@ -728,6 +830,24 @@ const deleteSecretHelper = async ({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: TelemetryService.getDistinctId({
authData
}),
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
}
});
}
return ({
secrets,
secret
@@ -737,6 +857,7 @@ const deleteSecretHelper = async ({
export {
validateClientForSecret,
validateClientForSecrets,
createSecretBlindIndexDataHelper,
generateSecretBlindIndexHelper,
createSecretHelper,
getSecretsHelper,
+3 -16
View File
@@ -30,6 +30,7 @@ import {
} from '../variables';
import { getEncryptionKey } from '../config';
import { encryptSymmetric } from '../utils/crypto';
import { SecretService } from '../services';
/**
* Validate authenticated clients for workspace with id [workspaceId] based
@@ -158,23 +159,9 @@ const createWorkspace = async ({
});
// initialize blind index salt for workspace
const salt = crypto.randomBytes(16).toString('base64');
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: getEncryptionKey()
await SecretService.createSecretBlindIndexData({
workspaceId: workspace._id
});
await new SecretBlindIndexData({
workspace: workspace._id,
encryptedSaltCiphertext,
saltIV,
saltTag
}).save();
} catch (err) {
Sentry.setUser(null);