Begin developing bot, event, and integration pipeline

This commit is contained in:
Tuan Dang
2022-12-06 00:23:16 -05:00
parent c5a422fe64
commit 46fe724012
48 changed files with 1402 additions and 176 deletions
+96
View File
@@ -0,0 +1,96 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Bot, BotKey } from '../models';
import { createBot } from '../helpers/bot';
interface BotKey {
encryptedKey: string;
nonce: string;
}
/**
* Return bot for workspace with id [workspaceId]. If a workspace bot doesn't exist,
* then create and return a new bot.
* @param req
* @param res
* @returns
*/
export const getBotByWorkspaceId = async (req: Request, res: Response) => {
let bot;
try {
const { workspaceId } = req.body;
bot = await Bot.findOne({
workspace: workspaceId
});
if (!bot) {
// case: bot doesn't exist for workspace with id [workspaceId]
// -> create a new bot and return it
bot = await createBot({
name: 'Infisical Bot',
workspaceId
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get bot for workspace'
});
}
return res.status(200).send({
bot
});
};
/**
* Return bot with id [req.bot._id] with active state set to [isActive].
* @param req
* @param res
* @returns
*/
export const setBotActiveState = async (req: Request, res: Response) => {
let bot;
try {
const { isActive, botKey }: { isActive: boolean, botKey: BotKey } = req.body;
if (isActive) {
// bot state set to active -> share workspace key with bot
await new BotKey({
encryptedKey: botKey.encryptedKey,
nonce: botKey.nonce,
sender: req.user._id,
receiver: req.bot._id,
workspace: req.bot.workspace
}).save();
} else {
// case: bot state set to inactive -> delete bot's workspace key
await BotKey.deleteOne({
bot: req.bot._id
});
}
bot = await Bot.findOneAndUpdate({
_id: req.bot._id
}, {
isActive
}, {
new: true
});
if (!bot) throw new Error('Failed to update bot active state');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update bot active state'
});
}
return res.status(200).send({
bot
});
};
+2
View File
@@ -1,4 +1,5 @@
import * as authController from './authController';
import * as botController from './botController';
import * as integrationAuthController from './integrationAuthController';
import * as integrationController from './integrationController';
import * as keyController from './keyController';
@@ -16,6 +17,7 @@ import * as workspaceController from './workspaceController';
export {
authController,
botController,
integrationAuthController,
integrationController,
keyController,
@@ -7,6 +7,8 @@ import { processOAuthTokenRes } from '../helpers/integrationAuth';
import { INTEGRATION_SET, ENV_DEV } from '../variables';
import { OAUTH_CLIENT_SECRET_HEROKU, OAUTH_TOKEN_URL_HEROKU } from '../config';
import { IntegrationService } from '../services';
/**
* Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId]
* Note: integration [integration] must be set up compatible/designed for OAuth2
@@ -14,58 +16,64 @@ import { OAUTH_CLIENT_SECRET_HEROKU, OAUTH_TOKEN_URL_HEROKU } from '../config';
* @param res
* @returns
*/
export const integrationAuthOauthExchange = async (
export const oAuthExchange = async (
req: Request,
res: Response
) => {
try {
let clientSecret;
// let clientSecret;
const { workspaceId, code, integration } = req.body;
if (!INTEGRATION_SET.has(integration))
throw new Error('Failed to validate integration');
// use correct client secret
switch (integration) {
case 'heroku':
clientSecret = OAUTH_CLIENT_SECRET_HEROKU;
}
// TODO: unfinished - make compatible with other integration types
const res = await axios.post(
OAUTH_TOKEN_URL_HEROKU!,
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_secret: clientSecret
} as any)
);
const integrationAuth = await processOAuthTokenRes({
await IntegrationService.handleOAuthExchange({
workspaceId,
integration,
res
code
});
// create or replace integration
const integrationObj = await Integration.findOneAndUpdate(
{ workspace: workspaceId, integration },
{
workspace: workspaceId,
environment: ENV_DEV,
isActive: false,
app: null,
integration,
integrationAuth: integrationAuth._id
},
{ upsert: true, new: true }
);
// // use correct client secret
// switch (integration) {
// case 'heroku':
// clientSecret = OAUTH_CLIENT_SECRET_HEROKU;
// }
// // TODO: unfinished - make compatible with other integration types
// const res = await axios.post( // this response may be different for each integration
// OAUTH_TOKEN_URL_HEROKU!,
// new URLSearchParams({
// grant_type: 'authorization_code',
// code: code,
// client_secret: clientSecret
// } as any)
// );
// const integrationAuth = await processOAuthTokenRes({
// workspaceId,
// integration,
// res
// });
// // create or replace integration
// const integrationObj = await Integration.findOneAndUpdate(
// { workspace: workspaceId, integration },
// {
// workspace: workspaceId,
// environment: ENV_DEV,
// isActive: false,
// app: null,
// integration,
// integrationAuth: integrationAuth._id
// },
// { upsert: true, new: true }
// );
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get OAuth2 token'
message: 'Failed to get OAuth2 code-token exchange'
});
}
@@ -49,6 +49,7 @@ export const getIntegrations = async (req: Request, res: Response) => {
});
};
// TODO: deprecate
/**
* Sync secrets [secrets] to integration with id [integrationId]
* @param req
-10
View File
@@ -17,16 +17,6 @@ export const uploadKey = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const { key } = req.body;
// validate membership of sender
const senderMembership = await findMembership({
user: req.user._id,
workspace: workspaceId
});
if (!senderMembership) {
throw new Error('Failed sender membership validation for workspace');
}
// validate membership of receiver
const receiverMembership = await findMembership({
user: key.userId,
+13 -3
View File
@@ -7,8 +7,9 @@ import {
reformatPullSecrets
} from '../helpers/secret';
import { pushKeys } from '../helpers/key';
import { eventPushSecrets } from '../events';
import { EventService } from '../services';
import { ENV_SET } from '../variables';
import { postHogClient } from '../services';
interface PushSecret {
@@ -60,7 +61,16 @@ export const pushSecrets = async (req: Request, res: Response) => {
workspaceId,
keys
});
// trigger event
EventService.handleEvent({
event: eventPushSecrets({
workspaceId,
environment,
secrets
})
});
if (postHogClient) {
postHogClient.capture({
event: 'secrets pushed',
@@ -192,7 +202,7 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
};
if (postHogClient) {
// capture secrets pushed event in production
// capture secrets pulled event in production
postHogClient.capture({
distinctId: req.serviceToken.user.email,
event: 'secrets pulled',