misc: audit logs for slack integration management

This commit is contained in:
Sheen Capadngan
2024-09-02 23:15:00 +08:00
parent f509464947
commit 9120367562
9 changed files with 166 additions and 22 deletions

View File

@@ -16,10 +16,10 @@ export async function up(knex: Knex): Promise<void> {
tb.binary("encryptedBotAccessToken").notNullable();
tb.string("slackBotId").notNullable();
tb.string("slackBotUserId").notNullable();
tb.boolean("isAccessRequestNotificationEnabled").defaultTo(false);
tb.string("accessRequestChannels").defaultTo("");
tb.boolean("isSecretRequestNotificationEnabled").defaultTo(false);
tb.string("secretRequestChannels").defaultTo("");
tb.boolean("isAccessRequestNotificationEnabled").notNullable().defaultTo(false);
tb.string("accessRequestChannels").notNullable().defaultTo("");
tb.boolean("isSecretRequestNotificationEnabled").notNullable().defaultTo(false);
tb.string("secretRequestChannels").notNullable().defaultTo("");
tb.timestamps(true, true, true);
});

View File

@@ -19,10 +19,10 @@ export const SlackIntegrationsSchema = z.object({
encryptedBotAccessToken: zodBuffer,
slackBotId: z.string(),
slackBotUserId: z.string(),
isAccessRequestNotificationEnabled: z.boolean().nullable().optional(),
accessRequestChannels: z.string().nullable().optional(),
isSecretRequestNotificationEnabled: z.boolean().nullable().optional(),
secretRequestChannels: z.string().nullable().optional(),
isAccessRequestNotificationEnabled: z.boolean().default(false),
accessRequestChannels: z.string().default(""),
isSecretRequestNotificationEnabled: z.boolean().default(false),
secretRequestChannels: z.string().default(""),
createdAt: z.date(),
updatedAt: z.date()
});

View File

@@ -169,7 +169,11 @@ export enum EventType {
GET_CERTIFICATE_TEMPLATE = "get-certificate-template",
CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-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",
GET_SLACK_INTEGRATION = "get-slack-integration",
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
DELETE_SLACK_INTEGRATION = "delete-slack-integration"
}
interface UserActorMetadata {
@@ -1446,6 +1450,38 @@ interface GetCertificateTemplateEstConfig {
};
}
interface AttemptCreateSlackIntegration {
type: EventType.ATTEMPT_CREATE_SLACK_INTEGRATION;
metadata: {
projectId?: string;
}; // no metadata
}
interface UpdateSlackIntegration {
type: EventType.UPDATE_SLACK_INTEGRATION;
metadata: {
id: string;
isAccessRequestNotificationEnabled: boolean;
accessRequestChannels: string;
isSecretRequestNotificationEnabled: boolean;
secretRequestChannels: string;
};
}
interface DeleteSlackIntegration {
type: EventType.DELETE_SLACK_INTEGRATION;
metadata: {
id: string;
};
}
interface GetSlackIntegration {
type: EventType.GET_SLACK_INTEGRATION;
metadata: {
id: string;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@@ -1576,4 +1612,8 @@ export type Event =
| DeleteCertificateTemplate
| CreateCertificateTemplateEstConfig
| UpdateCertificateTemplateEstConfig
| GetCertificateTemplateEstConfig;
| GetCertificateTemplateEstConfig
| AttemptCreateSlackIntegration
| UpdateSlackIntegration
| DeleteSlackIntegration
| GetSlackIntegration;

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { SlackIntegrationsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { getConfig } from "@app/lib/config/env";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -31,14 +32,24 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
// TODO: add audit logs
return server.services.slack.getInstallUrl({
const url = await server.services.slack.getInstallUrl({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.projectId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.projectId,
event: {
type: EventType.ATTEMPT_CREATE_SLACK_INTEGRATION,
metadata: {}
}
});
return url;
}
});
@@ -70,13 +81,26 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
return server.services.slack.getSlackIntegrationByProjectId({
const slackIntegration = await server.services.slack.getSlackIntegrationByProjectId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.projectId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.projectId,
event: {
type: EventType.GET_SLACK_INTEGRATION,
metadata: {
id: slackIntegration?.id
}
}
});
return slackIntegration;
}
});
@@ -114,8 +138,7 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
// TODO: add audit logs
return server.services.slack.updateSlackIntegration({
const updatedSlackIntegration = await server.services.slack.updateSlackIntegration({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
@@ -123,6 +146,23 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
id: req.params.slackIntegrationId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: updatedSlackIntegration.projectId,
event: {
type: EventType.UPDATE_SLACK_INTEGRATION,
metadata: {
id: updatedSlackIntegration.id,
isAccessRequestNotificationEnabled: updatedSlackIntegration.isAccessRequestNotificationEnabled,
accessRequestChannels: updatedSlackIntegration.accessRequestChannels,
isSecretRequestNotificationEnabled: updatedSlackIntegration.isSecretRequestNotificationEnabled,
secretRequestChannels: updatedSlackIntegration.secretRequestChannels
}
}
});
return updatedSlackIntegration;
}
});
@@ -154,15 +194,26 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
// TODO: add audit logs
return server.services.slack.deleteSlackIntegration({
const deletedSlackIntegration = await server.services.slack.deleteSlackIntegration({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.slackIntegrationId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: deletedSlackIntegration.projectId,
event: {
type: EventType.DELETE_SLACK_INTEGRATION,
metadata: {
id: deletedSlackIntegration.id
}
}
});
return deletedSlackIntegration;
}
});

View File

@@ -162,7 +162,7 @@ export const slackServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
const project = await projectDAL.findById(projectId);
if (!project) {
throw new NotFoundError({

View File

@@ -77,7 +77,11 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG]:
"Create certificate template EST configuration",
[EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG]:
"Update certificate template EST configuration"
"Update certificate template EST configuration",
[EventType.UPDATE_SLACK_INTEGRATION]: "Update slack integration",
[EventType.DELETE_SLACK_INTEGRATION]: "Delete slack integration",
[EventType.GET_SLACK_INTEGRATION]: "Get slack integration",
[EventType.ATTEMPT_CREATE_SLACK_INTEGRATION]: "Initiate create slack integration flow"
};
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {

View File

@@ -89,5 +89,9 @@ export enum EventType {
GET_CERTIFICATE_TEMPLATE = "get-certificate-template",
CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-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",
GET_SLACK_INTEGRATION = "get-slack-integration",
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
DELETE_SLACK_INTEGRATION = "delete-slack-integration"
}

View File

@@ -742,6 +742,31 @@ interface GetCertificateTemplateEstConfig {
};
}
interface UpdateSlackIntegration {
type: EventType.UPDATE_SLACK_INTEGRATION;
metadata: {
id: string;
isAccessRequestNotificationEnabled: boolean;
accessRequestChannels: string;
isSecretRequestNotificationEnabled: boolean;
secretRequestChannels: string;
};
}
interface DeleteSlackIntegration {
type: EventType.DELETE_SLACK_INTEGRATION;
metadata: {
id: string;
};
}
interface GetSlackIntegration {
type: EventType.GET_SLACK_INTEGRATION;
metadata: {
id: string;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@@ -817,7 +842,10 @@ export type Event =
| DeleteCertificateTemplate
| UpdateCertificateTemplateEstConfig
| CreateCertificateTemplateEstConfig
| GetCertificateTemplateEstConfig;
| GetCertificateTemplateEstConfig
| UpdateSlackIntegration
| DeleteSlackIntegration
| GetSlackIntegration;
export type AuditLog = {
id: string;

View File

@@ -442,6 +442,23 @@ export const LogsTableRow = ({ auditLog }: Props) => {
<p>{`Certificate Template ID: ${event.metadata.certificateTemplateId}`}</p>
</Td>
);
case EventType.UPDATE_SLACK_INTEGRATION:
return (
<Td>
<p>{`Slack integration ID: ${event.metadata.id}`}</p>
<p>{`Access Request Notification Status: ${event.metadata.isAccessRequestNotificationEnabled}`}</p>
<p>{`Access Request Channels: ${event.metadata.accessRequestChannels}`}</p>
<p>{`Secret Approval Request Notification Status: ${event.metadata.isSecretRequestNotificationEnabled}`}</p>
<p>{`Secret Request Channels: ${event.metadata.secretRequestChannels}`}</p>
</Td>
);
case EventType.DELETE_SLACK_INTEGRATION:
case EventType.GET_SLACK_INTEGRATION:
return (
<Td>
<p>{`Slack integration ID: ${event.metadata.id}`}</p>
</Td>
);
default:
return <Td />;
}