mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 09:35:37 +00:00
feat: integrated secret approval request
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { Block, WebClient } from "@slack/web-api";
|
||||
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TSlackIntegrationDALFactory } from "./slack-integration-dal";
|
||||
import { SlackTriggerFeature } from "./slack-types";
|
||||
|
||||
export const triggerSlackNotification = async ({
|
||||
projectId,
|
||||
payloadBlocks,
|
||||
payloadMessage,
|
||||
slackIntegrationDAL,
|
||||
projectDAL,
|
||||
kmsService,
|
||||
feature
|
||||
}: {
|
||||
projectId: string;
|
||||
payloadBlocks: Block[];
|
||||
payloadMessage: string;
|
||||
slackIntegrationDAL: Pick<TSlackIntegrationDALFactory, "findOne">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
feature: SlackTriggerFeature;
|
||||
}) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
const slackIntegration = await slackIntegrationDAL.findOne({
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!slackIntegration) {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetChannels: string[] = [];
|
||||
if (feature === SlackTriggerFeature.ACCESS_REQUEST) {
|
||||
targetChannels = slackIntegration.accessRequestChannels?.split(",") || [];
|
||||
if (!targetChannels.length || !slackIntegration.isAccessRequestNotificationEnabled) {
|
||||
return;
|
||||
}
|
||||
} else if (feature === SlackTriggerFeature.SECRET_APPROVAL) {
|
||||
targetChannels = slackIntegration.secretRequestChannels?.split(",") || [];
|
||||
if (!targetChannels.length || !slackIntegration.isSecretRequestNotificationEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: project.orgId
|
||||
});
|
||||
|
||||
const botKey = orgDataKeyDecryptor({
|
||||
cipherTextBlob: slackIntegration.encryptedBotAccessToken
|
||||
}).toString("utf8");
|
||||
|
||||
const targetChannelSet = new Set<string>(targetChannels);
|
||||
const slackWebClient = new WebClient(botKey);
|
||||
const channelIdsToSendNotif: string[] = [];
|
||||
let cursor;
|
||||
|
||||
do {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const response = await slackWebClient.conversations.list({
|
||||
cursor,
|
||||
limit: 1000,
|
||||
types: "public_channel,private_channel"
|
||||
});
|
||||
|
||||
response.channels?.forEach((channel) => {
|
||||
if (channel.name_normalized && targetChannelSet.has(channel.name_normalized)) {
|
||||
channelIdsToSendNotif.push(channel.id as string);
|
||||
}
|
||||
});
|
||||
|
||||
// Set the cursor for the next page
|
||||
cursor = response.response_metadata?.next_cursor;
|
||||
} while (cursor); // Continue while there is a cursor
|
||||
|
||||
for await (const conversationId of channelIdsToSendNotif) {
|
||||
// we send both text and blocks for compatibility with barebone clients
|
||||
await slackWebClient.chat.postMessage({
|
||||
channel: conversationId,
|
||||
text: payloadMessage,
|
||||
blocks: payloadBlocks
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -102,9 +102,12 @@ export const slackServiceFactory = ({
|
||||
slackBotUserId: installation.bot?.userId || ""
|
||||
});
|
||||
},
|
||||
// for our use-case we don't need to implement this because this will only be used
|
||||
// when listening for events from slack
|
||||
fetchInstallation: () => {
|
||||
return {} as never;
|
||||
},
|
||||
// for our use-case we don't need to implement this yet
|
||||
deleteInstallation: () => {
|
||||
return {} as never;
|
||||
}
|
||||
@@ -132,7 +135,7 @@ export const slackServiceFactory = ({
|
||||
|
||||
const installer = await getSlackInstaller();
|
||||
const url = await installer.generateInstallUrl({
|
||||
scopes: ["chat:write"],
|
||||
scopes: ["chat:write.public", "chat:write", "channels:read", "groups:read", "im:read", "mpim:read"],
|
||||
metadata: JSON.stringify({
|
||||
projectId: project.id
|
||||
}),
|
||||
|
||||
@@ -12,3 +12,8 @@ export type TCompleteSlackIntegrationDTO = {
|
||||
slackBotId: string;
|
||||
slackBotUserId: string;
|
||||
};
|
||||
|
||||
export enum SlackTriggerFeature {
|
||||
SECRET_APPROVAL = "secret-approval",
|
||||
ACCESS_REQUEST = "access-request"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user