misc: addressed review comments

This commit is contained in:
Sheen Capadngan
2024-09-11 00:11:19 +08:00
parent ffaf145317
commit 7a36badb23
13 changed files with 212 additions and 275 deletions
@@ -1,11 +1,6 @@
import { PackRule, unpackRules } from "@casl/ability/extra"; import { PackRule, unpackRules } from "@casl/ability/extra";
import { UnauthorizedError } from "@app/lib/errors"; import { UnauthorizedError } from "@app/lib/errors";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
import { triggerSlackNotification } from "@app/services/slack/slack-fns";
import { SlackTriggerFeature } from "@app/services/slack/slack-types";
import { TVerifyPermission } from "./access-approval-request-types"; import { TVerifyPermission } from "./access-approval-request-types";
@@ -56,67 +51,3 @@ export const verifyRequestedPermissions = ({ permissions }: TVerifyPermission) =
accessTypes: requestedPermissions.filter(filterUnique) accessTypes: requestedPermissions.filter(filterUnique)
}; };
}; };
export const triggerAccessRequestSlackNotif = async ({
projectId,
projectName,
requesterFullName,
isTemporary,
requesterEmail,
secretPath,
environment,
permissions,
approvalUrl,
projectDAL,
kmsService,
projectSlackConfigDAL
}: {
projectId: string;
projectName: string;
requesterFullName: string;
isTemporary: boolean;
requesterEmail: string;
secretPath: string;
environment: string;
permissions: string[];
approvalUrl: string;
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectWithOrg">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
}) => {
const messageBody = `${requesterFullName} (${requesterEmail}) has requested ${
isTemporary ? "temporary" : "permanent"
} access to ${secretPath} in the ${environment} environment of ${projectName}.
The following permissions are requested: ${permissions.join(", ")}
View the request and approve or deny it <${approvalUrl}|here>.`;
const payloadBlocks = [
{
type: "header",
text: {
type: "plain_text",
text: "New access approval request pending for review",
emoji: true
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: messageBody
}
}
];
await triggerSlackNotification({
projectId,
projectDAL,
kmsService,
payloadMessage: messageBody,
projectSlackConfigDAL,
payloadBlocks,
feature: SlackTriggerFeature.ACCESS_REQUEST
});
};
@@ -10,6 +10,8 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal"; import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
import { triggerSlackNotification } from "@app/services/slack/slack-fns";
import { SlackTriggerFeature } from "@app/services/slack/slack-types";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
@@ -20,7 +22,7 @@ import { TPermissionServiceFactory } from "../permission/permission-service";
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal"; import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types"; import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal"; import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
import { triggerAccessRequestSlackNotif, verifyRequestedPermissions } from "./access-approval-request-fns"; import { verifyRequestedPermissions } from "./access-approval-request-fns";
import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-request-reviewer-dal"; import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-request-reviewer-dal";
import { import {
ApprovalStatus, ApprovalStatus,
@@ -178,19 +180,24 @@ export const accessApprovalRequestServiceFactory = ({
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`; const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
const approvalUrl = `${cfg.SITE_URL}/project/${project.id}/approval`; const approvalUrl = `${cfg.SITE_URL}/project/${project.id}/approval`;
await triggerAccessRequestSlackNotif({ await triggerSlackNotification({
projectId: project.id, projectId: project.id,
projectName: project.name, projectSlackConfigDAL,
requesterFullName,
isTemporary,
requesterEmail: requestedByUser.email as string,
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl,
projectDAL, projectDAL,
kmsService, kmsService,
projectSlackConfigDAL notification: {
type: SlackTriggerFeature.ACCESS_REQUEST,
payload: {
projectName: project.name,
requesterFullName,
isTemporary,
requesterEmail: requestedByUser.email as string,
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl
}
}
}); });
await smtpService.sendMail({ await smtpService.sendMail({
@@ -1,12 +1,7 @@
import { TSecretApprovalRequests } from "@app/db/schemas"; import { TSecretApprovalRequests } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
import { triggerSlackNotification } from "@app/services/slack/slack-fns";
import { SlackTriggerFeature } from "@app/services/slack/slack-types";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal"; import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
@@ -18,68 +13,6 @@ type TSendApprovalEmails = {
secretApprovalRequest: TSecretApprovalRequests; secretApprovalRequest: TSecretApprovalRequests;
}; };
type TTriggerSecretApprovalSlackNotif = {
environment: string;
projectId: string;
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectWithOrg">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
secretApprovalRequest: TSecretApprovalRequests;
secretPath: string;
userDAL: Pick<TUserDALFactory, "findById">;
};
export const triggerSecretApprovalSlackNotif = async ({
projectId,
projectDAL,
kmsService,
secretApprovalRequest,
projectSlackConfigDAL,
userDAL,
environment,
secretPath
}: TTriggerSecretApprovalSlackNotif) => {
const appCfg = getConfig();
const project = await projectDAL.findProjectWithOrg(projectId);
const user = await userDAL.findById(secretApprovalRequest.committerUserId);
const messageBody = `A secret approval request has been opened by ${user.email}.
*Environment*: ${environment}
*Secret path*: ${secretPath || "/"}
View the complete details <${appCfg.SITE_URL}/project/${project.id}/approval?requestId=${
secretApprovalRequest.id
}|here>.`;
const payloadBlocks = [
{
type: "header",
text: {
type: "plain_text",
text: "Secret approval request",
emoji: true
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: messageBody
}
}
];
await triggerSlackNotification({
projectId,
projectDAL,
kmsService,
payloadMessage: messageBody,
projectSlackConfigDAL,
payloadBlocks,
feature: SlackTriggerFeature.SECRET_APPROVAL
});
};
export const sendApprovalEmailsFn = async ({ export const sendApprovalEmailsFn = async ({
secretApprovalPolicyDAL, secretApprovalPolicyDAL,
projectDAL, projectDAL,
@@ -48,6 +48,8 @@ import {
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal"; import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
import { triggerSlackNotification } from "@app/services/slack/slack-fns";
import { SlackTriggerFeature } from "@app/services/slack/slack-types";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
@@ -57,7 +59,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/pr
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal"; import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
import { TSecretSnapshotServiceFactory } from "../secret-snapshot/secret-snapshot-service"; import { TSecretSnapshotServiceFactory } from "../secret-snapshot/secret-snapshot-service";
import { TSecretApprovalRequestDALFactory } from "./secret-approval-request-dal"; import { TSecretApprovalRequestDALFactory } from "./secret-approval-request-dal";
import { sendApprovalEmailsFn, triggerSecretApprovalSlackNotif } from "./secret-approval-request-fns"; import { sendApprovalEmailsFn } from "./secret-approval-request-fns";
import { TSecretApprovalRequestReviewerDALFactory } from "./secret-approval-request-reviewer-dal"; import { TSecretApprovalRequestReviewerDALFactory } from "./secret-approval-request-reviewer-dal";
import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal"; import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal";
import { import {
@@ -1073,15 +1075,22 @@ export const secretApprovalRequestServiceFactory = ({
}); });
const env = await projectEnvDAL.findOne({ id: policy.envId }); const env = await projectEnvDAL.findOne({ id: policy.envId });
await triggerSecretApprovalSlackNotif({ const user = await userDAL.findById(secretApprovalRequest.committerUserId);
await triggerSlackNotification({
projectId, projectId,
secretPath: policy.secretPath as string,
environment: env.name,
projectDAL, projectDAL,
kmsService, kmsService,
secretApprovalRequest, projectSlackConfigDAL,
userDAL, notification: {
projectSlackConfigDAL type: SlackTriggerFeature.SECRET_APPROVAL,
payload: {
userEmail: user.email as string,
environment: env.name,
secretPath,
projectId,
requestId: secretApprovalRequest.id
}
}
}); });
await sendApprovalEmailsFn({ await sendApprovalEmailsFn({
@@ -1346,16 +1355,23 @@ export const secretApprovalRequestServiceFactory = ({
return { ...doc, commits: approvalCommits }; return { ...doc, commits: approvalCommits };
}); });
const user = await userDAL.findById(secretApprovalRequest.committerUserId);
const env = await projectEnvDAL.findOne({ id: policy.envId }); const env = await projectEnvDAL.findOne({ id: policy.envId });
await triggerSecretApprovalSlackNotif({ await triggerSlackNotification({
secretPath: policy.secretPath as string,
environment: env.name,
projectId, projectId,
projectDAL, projectDAL,
kmsService, kmsService,
secretApprovalRequest, projectSlackConfigDAL,
userDAL, notification: {
projectSlackConfigDAL type: SlackTriggerFeature.SECRET_APPROVAL,
payload: {
userEmail: user.email as string,
environment: env.name,
secretPath,
projectId,
requestId: secretApprovalRequest.id
}
}
}); });
await sendApprovalEmailsFn({ await sendApprovalEmailsFn({
@@ -130,29 +130,6 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
} }
}); });
server.route({
method: "GET",
url: "/integrations/slack/bot-creation-url",
config: {
rateLimit: readLimit
},
schema: {
response: {
200: z.string()
}
},
onRequest: (req, res, done) => {
verifyAuth([AuthMode.JWT])(req, res, () => {
verifySuperAdmin(req, res, done);
});
},
handler: async () => {
const url = await server.services.superAdmin.getCustomSlackBotCreationUrl();
return url;
}
});
server.route({ server.route({
method: "GET", method: "GET",
url: "/integrations/slack/config", url: "/integrations/slack/config",
+88 -52
View File
@@ -1,53 +1,14 @@
import { Block, WebClient } from "@slack/web-api"; import { WebClient } from "@slack/web-api";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types"; import { KmsDataKey } from "../kms/kms-types";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TProjectSlackConfigDALFactory } from "./project-slack-config-dal"; import { TProjectSlackConfigDALFactory } from "./project-slack-config-dal";
import { SlackTriggerFeature } from "./slack-types"; import { SlackTriggerFeature, TSlackNotification } from "./slack-types";
export const getCustomSlackBotManifest = () => {
const appCfg = getConfig();
return {
display_information: {
name: "Infisical",
description: "Get real-time Infisical updates in Slack",
background_color: "#c2d62b",
long_description: `This Slack application is designed specifically for use with your self-hosted Infisical instance, allowing seamless integration between your Infisical projects and your Slack workspace. With this integration, your team can stay up-to-date with the latest events, changes, and notifications directly inside Slack.
- Notifications: Receive real-time updates and alerts about critical events in your Infisical projects. Whether it's a new project being created, updates to secrets, or changes to your team's configuration, you will be promptly notified within the designated Slack channels of your choice.
- Customization: Tailor the notifications to your team's specific needs by configuring which types of events trigger alerts and in which channels they are sent.
- Collaboration: Keep your entire team in the loop with notifications that help facilitate more efficient collaboration by ensuring that everyone is aware of important developments in your Infisical projects.
By integrating Infisical with Slack, you can enhance your workflow by combining the power of secure secrets management with the communication capabilities of Slack.`
},
features: {
app_home: {
home_tab_enabled: false,
messages_tab_enabled: false,
messages_tab_read_only_enabled: true
},
bot_user: {
display_name: "Infisical",
always_online: true
}
},
oauth_config: {
redirect_urls: [`${appCfg.SITE_URL}/api/v1/workflow-integrations/slack/oauth_redirect`],
scopes: {
bot: ["chat:write.public", "chat:write", "channels:read", "groups:read", "im:read", "mpim:read"]
}
},
settings: {
org_deploy_enabled: false,
socket_mode_enabled: false,
token_rotation_enabled: false
}
};
};
export const fetchSlackChannels = async (botKey: string) => { export const fetchSlackChannels = async (botKey: string) => {
const slackChannels: { const slackChannels: {
@@ -80,23 +41,98 @@ export const fetchSlackChannels = async (botKey: string) => {
return slackChannels; return slackChannels;
}; };
const buildSlackPayload = (notification: TSlackNotification) => {
const appCfg = getConfig();
switch (notification.type) {
case SlackTriggerFeature.SECRET_APPROVAL: {
const { payload } = notification;
const messageBody = `A secret approval request has been opened by ${payload.userEmail}.
*Environment*: ${payload.environment}
*Secret path*: ${payload.secretPath || "/"}
View the complete details <${appCfg.SITE_URL}/project/${payload.projectId}/approval?requestId=${
payload.requestId
}|here>.`;
const payloadBlocks = [
{
type: "header",
text: {
type: "plain_text",
text: "Secret approval request",
emoji: true
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: messageBody
}
}
];
return {
payloadMessage: messageBody,
payloadBlocks
};
}
case SlackTriggerFeature.ACCESS_REQUEST: {
const { payload } = notification;
const messageBody = `${payload.requesterFullName} (${payload.requesterEmail}) has requested ${
payload.isTemporary ? "temporary" : "permanent"
} access to ${payload.secretPath} in the ${payload.environment} environment of ${payload.projectName}.
The following permissions are requested: ${payload.permissions.join(", ")}
View the request and approve or deny it <${payload.approvalUrl}|here>.`;
const payloadBlocks = [
{
type: "header",
text: {
type: "plain_text",
text: "New access approval request pending for review",
emoji: true
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: messageBody
}
}
];
return {
payloadMessage: messageBody,
payloadBlocks
};
}
default: {
throw new BadRequestError({
message: "Slack notification type not supported."
});
}
}
};
export const triggerSlackNotification = async ({ export const triggerSlackNotification = async ({
projectId, projectId,
payloadBlocks, notification,
payloadMessage,
projectSlackConfigDAL, projectSlackConfigDAL,
projectDAL, projectDAL,
kmsService, kmsService
feature
}: { }: {
projectId: string; projectId: string;
payloadBlocks: Block[]; notification: TSlackNotification;
payloadMessage: string;
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">; projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
projectDAL: Pick<TProjectDALFactory, "findById">; projectDAL: Pick<TProjectDALFactory, "findById">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
feature: SlackTriggerFeature;
}) => { }) => {
const { payloadMessage, payloadBlocks } = buildSlackPayload(notification);
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);
const slackIntegration = await projectSlackConfigDAL.getIntegrationDetailsByProject(project.id); const slackIntegration = await projectSlackConfigDAL.getIntegrationDetailsByProject(project.id);
@@ -105,12 +141,12 @@ export const triggerSlackNotification = async ({
} }
let targetChannelIds: string[] = []; let targetChannelIds: string[] = [];
if (feature === SlackTriggerFeature.ACCESS_REQUEST) { if (notification.type === SlackTriggerFeature.ACCESS_REQUEST) {
targetChannelIds = slackIntegration.accessRequestChannels?.split(", ") || []; targetChannelIds = slackIntegration.accessRequestChannels?.split(", ") || [];
if (!targetChannelIds.length || !slackIntegration.isAccessRequestNotificationEnabled) { if (!targetChannelIds.length || !slackIntegration.isAccessRequestNotificationEnabled) {
return; return;
} }
} else if (feature === SlackTriggerFeature.SECRET_APPROVAL) { } else if (notification.type === SlackTriggerFeature.SECRET_APPROVAL) {
targetChannelIds = slackIntegration.secretRequestChannels?.split(", ") || []; targetChannelIds = slackIntegration.secretRequestChannels?.split(", ") || [];
if (!targetChannelIds.length || !slackIntegration.isSecretRequestNotificationEnabled) { if (!targetChannelIds.length || !slackIntegration.isSecretRequestNotificationEnabled) {
return; return;
@@ -136,6 +172,6 @@ export const triggerSlackNotification = async ({
text: payloadMessage, text: payloadMessage,
blocks: payloadBlocks blocks: payloadBlocks
}) })
.catch((err) => void logger.error(err)); .catch((err) => logger.error(err));
} }
}; };
+1 -1
View File
@@ -158,7 +158,7 @@ export const slackServiceFactory = ({
message: `Invalid Slack configuration. ${ message: `Invalid Slack configuration. ${
appCfg.isCloud appCfg.isCloud
? "Please contact the Infisical team." ? "Please contact the Infisical team."
: "Contact your instance admin to setup Slack integration in the Admin settings." : "Contact your instance admin to setup Slack integration in the Admin settings. Your configuration is missing Slack client ID and secret."
}` }`
}); });
} }
+25
View File
@@ -52,3 +52,28 @@ export enum SlackTriggerFeature {
SECRET_APPROVAL = "secret-approval", SECRET_APPROVAL = "secret-approval",
ACCESS_REQUEST = "access-request" ACCESS_REQUEST = "access-request"
} }
export type TSlackNotification =
| {
type: SlackTriggerFeature.SECRET_APPROVAL;
payload: {
userEmail: string;
environment: string;
secretPath: string;
requestId: string;
projectId: string;
};
}
| {
type: SlackTriggerFeature.ACCESS_REQUEST;
payload: {
requesterFullName: string;
requesterEmail: string;
isTemporary: boolean;
secretPath: string;
environment: string;
projectName: string;
permissions: string[];
approvalUrl: string;
};
};
@@ -12,7 +12,6 @@ import { TAuthLoginFactory } from "../auth/auth-login-service";
import { AuthMethod } from "../auth/auth-type"; import { AuthMethod } from "../auth/auth-type";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { TOrgServiceFactory } from "../org/org-service"; import { TOrgServiceFactory } from "../org/org-service";
import { getCustomSlackBotManifest } from "../slack/slack-fns";
import { TUserDALFactory } from "../user/user-dal"; import { TUserDALFactory } from "../user/user-dal";
import { TSuperAdminDALFactory } from "./super-admin-dal"; import { TSuperAdminDALFactory } from "./super-admin-dal";
import { LoginMethod, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types"; import { LoginMethod, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
@@ -90,9 +89,7 @@ export const superAdminServiceFactory = ({
data: TSuperAdminUpdate & { slackClientId?: string; slackClientSecret?: string }, data: TSuperAdminUpdate & { slackClientId?: string; slackClientSecret?: string },
userId: string userId: string
) => { ) => {
const updatedData = { const updatedData = data;
...data
};
if (data.enabledLoginMethods) { if (data.enabledLoginMethods) {
const superAdminUser = await userDAL.findById(userId); const superAdminUser = await userDAL.findById(userId);
@@ -263,12 +260,6 @@ export const superAdminServiceFactory = ({
return user; return user;
}; };
const getCustomSlackBotCreationUrl = async () => {
return `https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(
JSON.stringify(getCustomSlackBotManifest())
)}`;
};
const getAdminSlackConfig = async () => { const getAdminSlackConfig = async () => {
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID); const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
@@ -301,7 +292,6 @@ export const superAdminServiceFactory = ({
adminSignUp, adminSignUp,
getUsers, getUsers,
deleteUser, deleteUser,
getCustomSlackBotCreationUrl,
getAdminSlackConfig getAdminSlackConfig
}; };
}; };
+1 -6
View File
@@ -4,9 +4,4 @@ export {
useUpdateAdminSlackConfig, useUpdateAdminSlackConfig,
useUpdateServerConfig useUpdateServerConfig
} from "./mutation"; } from "./mutation";
export { export { useAdminGetUsers, useGetAdminSlackConfig, useGetServerConfig } from "./queries";
useAdminGetUsers,
useGetAdminSlackConfig,
useGetCustomSlackAppCreationUrl,
useGetServerConfig
} from "./queries";
-13
View File
@@ -12,7 +12,6 @@ export const adminStandaloneKeys = {
export const adminQueryKeys = { export const adminQueryKeys = {
serverConfig: () => ["server-config"] as const, serverConfig: () => ["server-config"] as const,
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const, getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
getCustomSlackAppCreationUrl: () => ["custom-slack-app-creation-url"] as const,
getAdminSlackConfig: () => ["admin-slack-config"] as const getAdminSlackConfig: () => ["admin-slack-config"] as const
}; };
@@ -62,18 +61,6 @@ export const useAdminGetUsers = (filters: AdminGetUsersFilters) => {
}); });
}; };
export const useGetCustomSlackAppCreationUrl = () =>
useQuery({
queryKey: adminQueryKeys.getCustomSlackAppCreationUrl(),
queryFn: async () => {
const { data } = await apiRequest.get<string>(
"/api/v1/admin/integrations/slack/bot-creation-url"
);
return data;
}
});
export const useGetAdminSlackConfig = () => export const useGetAdminSlackConfig = () =>
useQuery({ useQuery({
queryKey: adminQueryKeys.getAdminSlackConfig(), queryKey: adminQueryKeys.getAdminSlackConfig(),
@@ -121,7 +121,11 @@ export const SlackIntegrationForm = ({ id, onClose }: Props) => {
</FormControl> </FormControl>
)} )}
<div className="mt-6 flex items-center space-x-4"> <div className="mt-6 flex items-center space-x-4">
<Button type="submit" isLoading={isSubmitting || isConnectLoading} isDisabled={!isDirty}> <Button
type="submit"
isLoading={isSubmitting || isConnectLoading}
isDisabled={!isDirty || isConnectLoading || isSubmitting}
>
{slackIntegration ? "Save" : "Connect Slack"} {slackIntegration ? "Save" : "Connect Slack"}
</Button> </Button>
<Button variant="outline_bg" onClick={onClose}> <Button variant="outline_bg" onClick={onClose}>
@@ -5,11 +5,7 @@ import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input } from "@app/components/v2"; import { Button, FormControl, Input } from "@app/components/v2";
import { import { useGetAdminSlackConfig, useUpdateServerConfig } from "@app/hooks/api";
useGetAdminSlackConfig,
useGetCustomSlackAppCreationUrl,
useUpdateServerConfig
} from "@app/hooks/api";
const slackFormSchema = z.object({ const slackFormSchema = z.object({
clientId: z.string(), clientId: z.string(),
@@ -18,6 +14,45 @@ const slackFormSchema = z.object({
type TSlackForm = z.infer<typeof slackFormSchema>; type TSlackForm = z.infer<typeof slackFormSchema>;
const getCustomSlackAppCreationUrl = () =>
`https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(
JSON.stringify({
display_information: {
name: "Infisical",
description: "Get real-time Infisical updates in Slack",
background_color: "#c2d62b",
long_description: `This Slack application is designed specifically for use with your self-hosted Infisical instance, allowing seamless integration between your Infisical projects and your Slack workspace. With this integration, your team can stay up-to-date with the latest events, changes, and notifications directly inside Slack.
- Notifications: Receive real-time updates and alerts about critical events in your Infisical projects. Whether it's a new project being created, updates to secrets, or changes to your team's configuration, you will be promptly notified within the designated Slack channels of your choice.
- Customization: Tailor the notifications to your team's specific needs by configuring which types of events trigger alerts and in which channels they are sent.
- Collaboration: Keep your entire team in the loop with notifications that help facilitate more efficient collaboration by ensuring that everyone is aware of important developments in your Infisical projects.
By integrating Infisical with Slack, you can enhance your workflow by combining the power of secure secrets management with the communication capabilities of Slack.`
},
features: {
app_home: {
home_tab_enabled: false,
messages_tab_enabled: false,
messages_tab_read_only_enabled: true
},
bot_user: {
display_name: "Infisical",
always_online: true
}
},
oauth_config: {
redirect_urls: [`${window.origin}/api/v1/workflow-integrations/slack/oauth_redirect`],
scopes: {
bot: ["chat:write.public", "chat:write"]
}
},
settings: {
org_deploy_enabled: false,
socket_mode_enabled: false,
token_rotation_enabled: false
}
})
)}`;
export const IntegrationPanel = () => { export const IntegrationPanel = () => {
const { const {
control, control,
@@ -28,9 +63,7 @@ export const IntegrationPanel = () => {
resolver: zodResolver(slackFormSchema) resolver: zodResolver(slackFormSchema)
}); });
const { data: customSlackAppCreationUrl } = useGetCustomSlackAppCreationUrl();
const { data: adminSlackConfig } = useGetAdminSlackConfig(); const { data: adminSlackConfig } = useGetAdminSlackConfig();
const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig(); const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig();
useEffect(() => { useEffect(() => {
@@ -63,7 +96,10 @@ export const IntegrationPanel = () => {
Step 1: Create your Infisical Slack App Step 1: Create your Infisical Slack App
</div> </div>
<div className="mb-6"> <div className="mb-6">
<Button colorSchema="secondary" onClick={() => window.open(customSlackAppCreationUrl)}> <Button
colorSchema="secondary"
onClick={() => window.open(getCustomSlackAppCreationUrl())}
>
Create Slack App Create Slack App
</Button> </Button>
</div> </div>