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
@@ -16,10 +16,10 @@ export async function up(knex: Knex): Promise<void> {
tb.binary("encryptedBotAccessToken").notNullable(); tb.binary("encryptedBotAccessToken").notNullable();
tb.string("slackBotId").notNullable(); tb.string("slackBotId").notNullable();
tb.string("slackBotUserId").notNullable(); tb.string("slackBotUserId").notNullable();
tb.boolean("isAccessRequestNotificationEnabled").defaultTo(false); tb.boolean("isAccessRequestNotificationEnabled").notNullable().defaultTo(false);
tb.string("accessRequestChannels").defaultTo(""); tb.string("accessRequestChannels").notNullable().defaultTo("");
tb.boolean("isSecretRequestNotificationEnabled").defaultTo(false); tb.boolean("isSecretRequestNotificationEnabled").notNullable().defaultTo(false);
tb.string("secretRequestChannels").defaultTo(""); tb.string("secretRequestChannels").notNullable().defaultTo("");
tb.timestamps(true, true, true); tb.timestamps(true, true, true);
}); });
+4 -4
View File
@@ -19,10 +19,10 @@ export const SlackIntegrationsSchema = z.object({
encryptedBotAccessToken: zodBuffer, encryptedBotAccessToken: zodBuffer,
slackBotId: z.string(), slackBotId: z.string(),
slackBotUserId: z.string(), slackBotUserId: z.string(),
isAccessRequestNotificationEnabled: z.boolean().nullable().optional(), isAccessRequestNotificationEnabled: z.boolean().default(false),
accessRequestChannels: z.string().nullable().optional(), accessRequestChannels: z.string().default(""),
isSecretRequestNotificationEnabled: z.boolean().nullable().optional(), isSecretRequestNotificationEnabled: z.boolean().default(false),
secretRequestChannels: z.string().nullable().optional(), secretRequestChannels: z.string().default(""),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date() updatedAt: z.date()
}); });
@@ -169,7 +169,11 @@ export enum EventType {
GET_CERTIFICATE_TEMPLATE = "get-certificate-template", GET_CERTIFICATE_TEMPLATE = "get-certificate-template",
CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-certificate-template-est-config", CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-certificate-template-est-config",
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",
GET_SLACK_INTEGRATION = "get-slack-integration",
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
DELETE_SLACK_INTEGRATION = "delete-slack-integration"
} }
interface UserActorMetadata { 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 = export type Event =
| GetSecretsEvent | GetSecretsEvent
| GetSecretEvent | GetSecretEvent
@@ -1576,4 +1612,8 @@ export type Event =
| DeleteCertificateTemplate | DeleteCertificateTemplate
| CreateCertificateTemplateEstConfig | CreateCertificateTemplateEstConfig
| UpdateCertificateTemplateEstConfig | UpdateCertificateTemplateEstConfig
| GetCertificateTemplateEstConfig; | GetCertificateTemplateEstConfig
| AttemptCreateSlackIntegration
| UpdateSlackIntegration
| DeleteSlackIntegration
| GetSlackIntegration;
+59 -8
View File
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { SlackIntegrationsSchema } from "@app/db/schemas"; 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 { getConfig } from "@app/lib/config/env";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; 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]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
// TODO: add audit logs const url = await server.services.slack.getInstallUrl({
return server.services.slack.getInstallUrl({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.query.projectId 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]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
return server.services.slack.getSlackIntegrationByProjectId({ const slackIntegration = await server.services.slack.getSlackIntegrationByProjectId({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.query.projectId 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]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
// TODO: add audit logs const updatedSlackIntegration = await server.services.slack.updateSlackIntegration({
return server.services.slack.updateSlackIntegration({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
@@ -123,6 +146,23 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
id: req.params.slackIntegrationId, id: req.params.slackIntegrationId,
...req.body ...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]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
// TODO: add audit logs const deletedSlackIntegration = await server.services.slack.deleteSlackIntegration({
return server.services.slack.deleteSlackIntegration({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
id: req.params.slackIntegrationId 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;
} }
}); });
+1 -1
View File
@@ -162,7 +162,7 @@ export const slackServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);
if (!project) { if (!project) {
throw new NotFoundError({ throw new NotFoundError({
@@ -77,7 +77,11 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG]: [EventType.CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG]:
"Create certificate template EST configuration", "Create certificate template EST configuration",
[EventType.UPDATE_CERTIFICATE_TEMPLATE_EST_CONFIG]: [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 } = { export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
+5 -1
View File
@@ -89,5 +89,9 @@ export enum EventType {
GET_CERTIFICATE_TEMPLATE = "get-certificate-template", GET_CERTIFICATE_TEMPLATE = "get-certificate-template",
CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-certificate-template-est-config", CREATE_CERTIFICATE_TEMPLATE_EST_CONFIG = "create-certificate-template-est-config",
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",
GET_SLACK_INTEGRATION = "get-slack-integration",
UPDATE_SLACK_INTEGRATION = "update-slack-integration",
DELETE_SLACK_INTEGRATION = "delete-slack-integration"
} }
+29 -1
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 = export type Event =
| GetSecretsEvent | GetSecretsEvent
| GetSecretEvent | GetSecretEvent
@@ -817,7 +842,10 @@ export type Event =
| DeleteCertificateTemplate | DeleteCertificateTemplate
| UpdateCertificateTemplateEstConfig | UpdateCertificateTemplateEstConfig
| CreateCertificateTemplateEstConfig | CreateCertificateTemplateEstConfig
| GetCertificateTemplateEstConfig; | GetCertificateTemplateEstConfig
| UpdateSlackIntegration
| DeleteSlackIntegration
| GetSlackIntegration;
export type AuditLog = { export type AuditLog = {
id: string; id: string;
@@ -442,6 +442,23 @@ export const LogsTableRow = ({ auditLog }: Props) => {
<p>{`Certificate Template ID: ${event.metadata.certificateTemplateId}`}</p> <p>{`Certificate Template ID: ${event.metadata.certificateTemplateId}`}</p>
</Td> </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: default:
return <Td />; return <Td />;
} }