mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 16:26:16 +00:00
misc: slack integration reinstall
This commit is contained in:
@@ -171,6 +171,7 @@ export enum EventType {
|
|||||||
UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "update-certificate-template-est-config",
|
UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "update-certificate-template-est-config",
|
||||||
GET_CERTIFICATE_TEMPLATE_EST_CONFIG = "get-certificate-template-est-config",
|
GET_CERTIFICATE_TEMPLATE_EST_CONFIG = "get-certificate-template-est-config",
|
||||||
ATTEMPT_CREATE_SLACK_INTEGRATION = "attempt-create-slack-integration",
|
ATTEMPT_CREATE_SLACK_INTEGRATION = "attempt-create-slack-integration",
|
||||||
|
ATTEMPT_REINSTALL_SLACK_INTEGRATION = "attempt-reinstall-slack-integration",
|
||||||
GET_SLACK_INTEGRATION = "get-slack-integration",
|
GET_SLACK_INTEGRATION = "get-slack-integration",
|
||||||
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
|
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
|
||||||
DELETE_SLACK_INTEGRATION = "delete-slack-integration",
|
DELETE_SLACK_INTEGRATION = "delete-slack-integration",
|
||||||
@@ -1460,6 +1461,13 @@ interface AttemptCreateSlackIntegration {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AttemptReinstallSlackIntegration {
|
||||||
|
type: EventType.ATTEMPT_REINSTALL_SLACK_INTEGRATION;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateSlackIntegration {
|
interface UpdateSlackIntegration {
|
||||||
type: EventType.UPDATE_SLACK_INTEGRATION;
|
type: EventType.UPDATE_SLACK_INTEGRATION;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -1634,6 +1642,7 @@ export type Event =
|
|||||||
| UpdateCertificateTemplateEstConfig
|
| UpdateCertificateTemplateEstConfig
|
||||||
| GetCertificateTemplateEstConfig
|
| GetCertificateTemplateEstConfig
|
||||||
| AttemptCreateSlackIntegration
|
| AttemptCreateSlackIntegration
|
||||||
|
| AttemptReinstallSlackIntegration
|
||||||
| UpdateSlackIntegration
|
| UpdateSlackIntegration
|
||||||
| DeleteSlackIntegration
|
| DeleteSlackIntegration
|
||||||
| GetSlackIntegration
|
| GetSlackIntegration
|
||||||
|
|||||||
@@ -56,6 +56,50 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/reinstall",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
querystring: z.object({
|
||||||
|
slackIntegrationId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.string()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const url = await server.services.slack.getReinstallUrl({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
id: req.query.slackIntegrationId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.ATTEMPT_REINSTALL_SLACK_INTEGRATION,
|
||||||
|
metadata: {
|
||||||
|
id: req.query.slackIntegrationId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/",
|
url: "/",
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ import { TSlackIntegrationDALFactory } from "./slack-integration-dal";
|
|||||||
import {
|
import {
|
||||||
TCompleteSlackIntegrationDTO,
|
TCompleteSlackIntegrationDTO,
|
||||||
TDeleteSlackIntegrationDTO,
|
TDeleteSlackIntegrationDTO,
|
||||||
|
TGetReinstallUrlDTO,
|
||||||
TGetSlackInstallUrlDTO,
|
TGetSlackInstallUrlDTO,
|
||||||
TGetSlackIntegrationByIdDTO,
|
TGetSlackIntegrationByIdDTO,
|
||||||
TGetSlackIntegrationByOrgDTO,
|
TGetSlackIntegrationByOrgDTO,
|
||||||
|
TReinstallSlackIntegrationDTO,
|
||||||
TUpdateSlackIntegrationDTO
|
TUpdateSlackIntegrationDTO
|
||||||
} from "./slack-types";
|
} from "./slack-types";
|
||||||
|
|
||||||
@@ -66,6 +68,38 @@ export const slackServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reinstallSlackIntegration = async ({
|
||||||
|
id,
|
||||||
|
teamId,
|
||||||
|
teamName,
|
||||||
|
slackUserId,
|
||||||
|
slackAppId,
|
||||||
|
botAccessToken,
|
||||||
|
slackBotId,
|
||||||
|
slackBotUserId
|
||||||
|
}: TReinstallSlackIntegrationDTO) => {
|
||||||
|
const slackIntegration = await slackIntegrationDAL.findById(id);
|
||||||
|
|
||||||
|
const { encryptor: orgDataKeyEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
orgId: slackIntegration.orgId,
|
||||||
|
type: KmsDataKey.Organization
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cipherTextBlob: encryptedBotAccessToken } = orgDataKeyEncryptor({
|
||||||
|
plainText: Buffer.from(botAccessToken, "utf8")
|
||||||
|
});
|
||||||
|
|
||||||
|
await slackIntegrationDAL.updateById(id, {
|
||||||
|
teamId,
|
||||||
|
teamName,
|
||||||
|
slackUserId,
|
||||||
|
slackAppId,
|
||||||
|
slackBotId,
|
||||||
|
slackBotUserId,
|
||||||
|
encryptedBotAccessToken
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getSlackInstaller = async () => {
|
const getSlackInstaller = async () => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -89,11 +123,25 @@ export const slackServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const metadata = JSON.parse(installation.metadata || "") as {
|
const metadata = JSON.parse(installation.metadata || "") as {
|
||||||
|
id?: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (metadata.id) {
|
||||||
|
return reinstallSlackIntegration({
|
||||||
|
id: metadata.id,
|
||||||
|
teamId: installation.team?.id || "",
|
||||||
|
teamName: installation.team?.name || "",
|
||||||
|
slackUserId: installation.user.id,
|
||||||
|
slackAppId: installation.appId || "",
|
||||||
|
botAccessToken: installation.bot?.token || "",
|
||||||
|
slackBotId: installation.bot?.id || "",
|
||||||
|
slackBotUserId: installation.bot?.userId || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return completeSlackIntegration({
|
return completeSlackIntegration({
|
||||||
orgId: metadata.orgId,
|
orgId: metadata.orgId,
|
||||||
slug: metadata.slug,
|
slug: metadata.slug,
|
||||||
@@ -154,6 +202,39 @@ export const slackServiceFactory = ({
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getReinstallUrl = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TGetReinstallUrlDTO) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
const slackIntegration = await slackIntegrationDAL.findById(id);
|
||||||
|
|
||||||
|
if (!slackIntegration) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: "Slack integration not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
slackIntegration.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
|
const installer = await getSlackInstaller();
|
||||||
|
const url = await installer.generateInstallUrl({
|
||||||
|
scopes: ["chat:write.public", "chat:write", "channels:read", "groups:read", "im:read", "mpim:read"],
|
||||||
|
metadata: JSON.stringify({
|
||||||
|
id,
|
||||||
|
orgId: slackIntegration.orgId
|
||||||
|
}),
|
||||||
|
redirectUri: `${appCfg.SITE_URL}/api/v1/workflow-integrations/slack/oauth_redirect`
|
||||||
|
});
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
const getSlackIntegrationsByOrg = async ({
|
const getSlackIntegrationsByOrg = async ({
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
@@ -265,6 +346,7 @@ export const slackServiceFactory = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getInstallUrl,
|
getInstallUrl,
|
||||||
|
getReinstallUrl,
|
||||||
getSlackIntegrationsByOrg,
|
getSlackIntegrationsByOrg,
|
||||||
getSlackIntegrationById,
|
getSlackIntegrationById,
|
||||||
completeSlackIntegration,
|
completeSlackIntegration,
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ export type TGetSlackInstallUrlDTO = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
} & Omit<TOrgPermission, "orgId">;
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
|
export type TGetReinstallUrlDTO = {
|
||||||
|
id: string;
|
||||||
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
export type TGetSlackIntegrationByOrgDTO = Omit<TOrgPermission, "orgId">;
|
export type TGetSlackIntegrationByOrgDTO = Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
export type TGetSlackIntegrationByIdDTO = { id: string } & Omit<TOrgPermission, "orgId">;
|
export type TGetSlackIntegrationByIdDTO = { id: string } & Omit<TOrgPermission, "orgId">;
|
||||||
@@ -31,6 +35,17 @@ export type TCompleteSlackIntegrationDTO = {
|
|||||||
slackBotUserId: string;
|
slackBotUserId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TReinstallSlackIntegrationDTO = {
|
||||||
|
id: string;
|
||||||
|
teamId: string;
|
||||||
|
teamName: string;
|
||||||
|
slackUserId: string;
|
||||||
|
slackAppId: string;
|
||||||
|
botAccessToken: string;
|
||||||
|
slackBotId: string;
|
||||||
|
slackBotUserId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export enum SlackTriggerFeature {
|
export enum SlackTriggerFeature {
|
||||||
SECRET_APPROVAL = "secret-approval",
|
SECRET_APPROVAL = "secret-approval",
|
||||||
ACCESS_REQUEST = "access-request"
|
ACCESS_REQUEST = "access-request"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export {
|
|||||||
} from "./mutation";
|
} from "./mutation";
|
||||||
export {
|
export {
|
||||||
fetchSlackInstallUrl,
|
fetchSlackInstallUrl,
|
||||||
|
fetchSlackReinstallUrl,
|
||||||
useGetSlackIntegrationById,
|
useGetSlackIntegrationById,
|
||||||
useGetSlackIntegrations
|
useGetSlackIntegrations
|
||||||
} from "./queries";
|
} from "./queries";
|
||||||
|
|||||||
@@ -26,6 +26,20 @@ export const fetchSlackInstallUrl = async ({
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchSlackReinstallUrl = async ({
|
||||||
|
slackIntegrationId
|
||||||
|
}: {
|
||||||
|
slackIntegrationId: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await apiRequest.get<string>("/api/v1/workflow-integrations/slack/reinstall", {
|
||||||
|
params: {
|
||||||
|
slackIntegrationId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchSlackIntegrations = async () => {
|
export const fetchSlackIntegrations = async () => {
|
||||||
const { data } = await apiRequest.get<SlackIntegration[]>("/api/v1/workflow-integrations/slack");
|
const { data } = await apiRequest.get<SlackIntegration[]>("/api/v1/workflow-integrations/slack");
|
||||||
|
|
||||||
|
|||||||
+42
-1
@@ -1,3 +1,4 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faEllipsis, faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faEllipsis, faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@@ -24,7 +25,11 @@ import {
|
|||||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { withPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useDeleteSlackIntegration, useGetSlackIntegrations } from "@app/hooks/api";
|
import {
|
||||||
|
fetchSlackReinstallUrl,
|
||||||
|
useDeleteSlackIntegration,
|
||||||
|
useGetSlackIntegrations
|
||||||
|
} from "@app/hooks/api";
|
||||||
import { WorkflowIntegrationPlatform } from "@app/hooks/api/workflowIntegrations/types";
|
import { WorkflowIntegrationPlatform } from "@app/hooks/api/workflowIntegrations/types";
|
||||||
|
|
||||||
import { AddWorkflowIntegrationForm } from "./AddWorkflowIntegrationForm";
|
import { AddWorkflowIntegrationForm } from "./AddWorkflowIntegrationForm";
|
||||||
@@ -39,6 +44,7 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
|||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
|
const router = useRouter();
|
||||||
const { data: slackIntegrations, isLoading: isSlackIntegrationsLoading } =
|
const { data: slackIntegrations, isLoading: isSlackIntegrationsLoading } =
|
||||||
useGetSlackIntegrations(currentOrg?.id);
|
useGetSlackIntegrations(currentOrg?.id);
|
||||||
const { mutateAsync: deleteSlackIntegration } = useDeleteSlackIntegration();
|
const { mutateAsync: deleteSlackIntegration } = useDeleteSlackIntegration();
|
||||||
@@ -63,6 +69,18 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const triggerReinstall = async (platform: WorkflowIntegrationPlatform, id: string) => {
|
||||||
|
if (platform === WorkflowIntegrationPlatform.SLACK) {
|
||||||
|
const slackReinstallUrl = await fetchSlackReinstallUrl({
|
||||||
|
slackIntegrationId: id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (slackReinstallUrl) {
|
||||||
|
router.push(slackReinstallUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isIntegrationsLoading = isSlackIntegrationsLoading;
|
const isIntegrationsLoading = isSlackIntegrationsLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -132,6 +150,29 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
|||||||
>
|
>
|
||||||
More details
|
More details
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgPermissionActions.Create}
|
||||||
|
an={OrgPermissionSubjects.Settings}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
disabled={!isAllowed}
|
||||||
|
className={twMerge(
|
||||||
|
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||||
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
triggerReinstall(
|
||||||
|
WorkflowIntegrationPlatform.SLACK,
|
||||||
|
slackIntegration.id
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reinstall
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
<OrgPermissionCan
|
<OrgPermissionCan
|
||||||
I={OrgPermissionActions.Delete}
|
I={OrgPermissionActions.Delete}
|
||||||
an={OrgPermissionSubjects.Settings}
|
an={OrgPermissionSubjects.Settings}
|
||||||
|
|||||||
Reference in New Issue
Block a user