Add more audit log events

This commit is contained in:
Tuan Dang
2023-08-08 09:50:49 +07:00
parent 648968c453
commit 2591161272
19 changed files with 1117 additions and 53 deletions
@@ -3,7 +3,9 @@ import { Types } from "mongoose";
import { standardRequest } from "../../config/request";
import { getApps, getTeams, revokeAccess } from "../../integrations";
import { Bot, IntegrationAuth } from "../../models";
import { EventType } from "../../ee/models";
import { IntegrationService } from "../../services";
import { EEAuditLogService } from "../../ee/services";
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
@@ -62,6 +64,19 @@ export const oAuthExchange = async (req: Request, res: Response) => {
environment: environments[0].slug
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.AUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth
});
@@ -129,6 +144,19 @@ export const saveIntegrationAccessToken = async (req: Request, res: Response) =>
});
if (!integrationAuth) throw new Error("Failed to save integration access token");
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.AUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth
@@ -530,6 +558,23 @@ export const deleteIntegrationAuth = async (req: Request, res: Response) => {
integrationAuth: req.integrationAuth,
accessToken: req.accessToken
});
if (!integrationAuth) return res.status(400).send({
message: "Failed to find integration authorization"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UNAUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth
@@ -6,6 +6,8 @@ import { eventStartIntegration } from "../../events";
import Folder from "../../models/folder";
import { getFolderByPath } from "../../services/FolderService";
import { BadRequestError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
/**
* Create/initialize an (empty) integration for integration authorization
@@ -74,6 +76,25 @@ export const createIntegration = async (req: Request, res: Response) => {
})
});
}
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_INTEGRATION,
metadata: {
integrationId: integration._id.toString(),
integration: integration.integration,
environment: integration.environment,
secretPath,
app: integration.app,
targetEnvironment: integration.targetEnvironment,
targetEnvironmentId: integration.targetEnvironmentId
}
},
{
workspaceId: integration.workspace
}
);
return res.status(200).send({
integration
@@ -148,8 +169,7 @@ export const updateIntegration = async (req: Request, res: Response) => {
};
/**
* Delete integration with id [integrationId] and deactivate bot if there are
* no integrations left
* Delete integration with id [integrationId]
* @param req
* @param res
* @returns
@@ -163,6 +183,25 @@ export const deleteIntegration = async (req: Request, res: Response) => {
if (!integration) throw new Error("Failed to find integration");
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_INTEGRATION,
metadata: {
integrationId: integration._id.toString(),
integration: integration.integration,
environment: integration.environment,
secretPath: integration.secretPath,
app: integration.app,
targetEnvironment: integration.targetEnvironment,
targetEnvironmentId: integration.targetEnvironmentId
}
},
{
workspaceId: integration.workspace
}
);
return res.status(200).send({
integration
});
@@ -1,9 +1,12 @@
import { Request, Response } from "express";
import { Key, Membership, MembershipOrg, User } from "../../models";
import { Types } from "mongoose";
import { Key, Membership, MembershipOrg, User, IUser } from "../../models";
import { EventType } from "../../ee/models";
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
import { sendMail } from "../../helpers/nodemailer";
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
import { getSiteURL } from "../../config";
import { EEAuditLogService } from "../../ee/services";
/**
* Check that user is a member of workspace with id [workspaceId]
@@ -36,11 +39,11 @@ export const validateMembership = async (req: Request, res: Response) => {
*/
export const deleteMembership = async (req: Request, res: Response) => {
const { membershipId } = req.params;
// check if membership to delete exists
const membershipToDelete = await Membership.findOne({
_id: membershipId
}).populate("user");
}).populate<{ user: IUser }>("user");
if (!membershipToDelete) {
throw new Error("Failed to delete workspace membership that doesn't exist");
@@ -66,6 +69,20 @@ export const deleteMembership = async (req: Request, res: Response) => {
const deletedMembership = await deleteMember({
membershipId: membershipToDelete._id.toString()
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.REMOVE_WORKSPACE_MEMBER,
metadata: {
userId: membershipToDelete.user._id.toString(),
email: membershipToDelete.user.email
}
},
{
workspaceId: membership.workspace
}
);
return res.status(200).send({
deletedMembership
@@ -140,7 +157,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
const inviteeMembership = await Membership.findOne({
user: invitee._id,
workspace: workspaceId
});
}).populate<{ user: IUser }>("user");
if (inviteeMembership) throw new Error("Failed to add existing member of workspace");
@@ -181,6 +198,20 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
}
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.ADD_WORKSPACE_MEMBER,
metadata: {
userId: invitee._id.toString(),
email: invitee.email
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.status(200).send({
invitee,
latestKey
@@ -1,5 +1,6 @@
import { Request, Response } from "express";
import { Secret } from "../../models";
import { Types } from "mongoose";
import Folder from "../../models/folder";
import { BadRequestError } from "../../utils/errors";
import {
@@ -8,6 +9,7 @@ import {
generateFolderId,
getAllFolderIds,
getFolderByPath,
getFolderPath,
getParentFromFolderId,
searchByFolderId,
searchByFolderIdWithDir,
@@ -15,10 +17,9 @@ import {
} from "../../services/FolderService";
import { ADMIN, MEMBER } from "../../variables";
import { validateMembership } from "../../helpers/membership";
import { FolderVersion } from "../../ee/models";
import { EESecretService } from "../../ee/services";
import { FolderVersion, EventType } from "../../ee/models";
import { EESecretService, EEAuditLogService } from "../../ee/services";
// TODO
// verify workspace id/environment
export const createFolder = async (req: Request, res: Response) => {
const { workspaceId, environment, folderName, parentFolderId } = req.body;
@@ -33,6 +34,7 @@ export const createFolder = async (req: Request, res: Response) => {
environment,
}).lean();
// space has no folders initialized
if (!folders) {
const id = generateFolderId();
const folder = new Folder({
@@ -56,13 +58,32 @@ export const createFolder = async (req: Request, res: Response) => {
workspaceId,
environment,
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_FOLDER,
metadata: {
environment,
folderId: id,
folderName,
folderPath: `root/${folderName}`
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({ folder: { id, name: folderName } });
}
const folder = appendFolder(folders.nodes, { folderName, parentFolderId });
await Folder.findByIdAndUpdate(folders._id, folders);
const parentFolder = searchByFolderId(folders.nodes, parentFolderId);
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
@@ -75,6 +96,24 @@ export const createFolder = async (req: Request, res: Response) => {
environment,
folderId: parentFolderId,
});
const folderPath = await getFolderPath(folders, folder.id);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_FOLDER,
metadata: {
environment,
folderId: folder.id,
folderName,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({ folder });
};
@@ -87,7 +126,7 @@ export const updateFolderById = async (req: Request, res: Response) => {
if (!folders) {
throw BadRequestError({ message: "The folder doesn't exist" });
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
@@ -100,10 +139,12 @@ export const updateFolderById = async (req: Request, res: Response) => {
throw BadRequestError({ message: "The folder doesn't exist" });
}
const folder = parentFolder.children.find(({ id }) => id === folderId);
if (!folder) {
throw BadRequestError({ message: "The folder doesn't exist" });
}
const oldFolderName = folder.name;
parentFolder.version += 1;
folder.name = name;
@@ -121,6 +162,25 @@ export const updateFolderById = async (req: Request, res: Response) => {
folderId: parentFolder.id,
});
const folderPath = await getFolderPath(folders, folder.id);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_FOLDER,
metadata: {
environment,
folderId: folder.id,
oldFolderName,
newFolderName: name,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({
message: "Successfully updated folder",
folder: { name: folder.name, id: folder.id },
@@ -143,6 +203,8 @@ export const deleteFolder = async (req: Request, res: Response) => {
acceptedRoles: [ADMIN, MEMBER],
});
const folderPath = await getFolderPath(folders, folderId);
const delOp = deleteFolderById(folders.nodes, folderId);
if (!delOp) {
throw BadRequestError({ message: "The folder doesn't exist" });
@@ -173,6 +235,22 @@ export const deleteFolder = async (req: Request, res: Response) => {
folderId: parentFolder.id,
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_FOLDER ,
metadata: {
environment,
folderId,
folderName: delFolder.name,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
res.send({ message: "successfully deleted folders", folders: delFolderIds });
};
@@ -4,7 +4,9 @@ import { client, getRootEncryptionKey } from "../../config";
import { validateMembership } from "../../helpers";
import Webhook from "../../models/webhooks";
import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService";
import { BadRequestError } from "../../utils/errors";
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
import { ADMIN, ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64, MEMBER } from "../../variables";
export const createWebhook = async (req: Request, res: Response) => {
@@ -27,6 +29,23 @@ export const createWebhook = async (req: Request, res: Response) => {
}
await webhook.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_WEBHOOK,
metadata: {
webhookId: webhook._id.toString(),
environment,
secretPath,
webhookUrl,
isDisabled: false
}
},
{
workspaceId
}
);
return res.status(200).send({
webhook,
@@ -54,6 +73,23 @@ export const updateWebhook = async (req: Request, res: Response) => {
}
await webhook.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_WEBHOOK_STATUS,
metadata: {
webhookId: webhook._id.toString(),
environment: webhook.environment,
secretPath: webhook.secretPath,
webhookUrl: webhook.url,
isDisabled
}
},
{
workspaceId: webhook.workspace
}
);
return res.status(200).send({
webhook,
message: "successfully updated webhook"
@@ -62,9 +98,10 @@ export const updateWebhook = async (req: Request, res: Response) => {
export const deleteWebhook = async (req: Request, res: Response) => {
const { webhookId } = req.params;
const webhook = await Webhook.findById(webhookId);
let webhook = await Webhook.findById(webhookId);
if (!webhook) {
throw BadRequestError({ message: "Webhook not found!!" });
throw ResourceNotFoundError({ message: "Webhook not found!!" });
}
await validateMembership({
@@ -72,8 +109,29 @@ export const deleteWebhook = async (req: Request, res: Response) => {
workspaceId: webhook.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
webhook = await Webhook.findByIdAndDelete(webhookId);
await webhook.deleteOne();
if (!webhook) {
throw ResourceNotFoundError({ message: "Webhook not found!!" });
}
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_WEBHOOK,
metadata: {
webhookId: webhook._id.toString(),
environment: webhook.environment,
secretPath: webhook.secretPath,
webhookUrl: webhook.url,
isDisabled: webhook.isDisabled
}
},
{
workspaceId: webhook.workspace
}
);
return res.status(200).send({
message: "successfully removed webhook"
@@ -8,8 +8,8 @@ import {
ServiceTokenData,
Workspace,
} from "../../models";
import { SecretVersion } from "../../ee/models";
import { EELicenseService } from "../../ee/services";
import { SecretVersion, EventType } from "../../ee/models";
import { EELicenseService, EEAuditLogService } from "../../ee/services";
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
import _ from "lodash";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
@@ -61,6 +61,20 @@ export const createWorkspaceEnvironment = async (
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_ENVIRONMENT,
metadata: {
name: environmentName,
slug: environmentSlug
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully created new environment",
workspace: workspaceId,
@@ -110,6 +124,8 @@ export const renameWorkspaceEnvironment = async (
if (envIndex === -1) {
throw new Error("Invalid environment given");
}
const oldEnvironment = workspace.environments[envIndex];
workspace.environments[envIndex].name = environmentName;
workspace.environments[envIndex].slug = environmentSlug.toLowerCase();
@@ -142,8 +158,23 @@ export const renameWorkspaceEnvironment = async (
},
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
)
);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_ENVIRONMENT,
metadata: {
oldName: oldEnvironment.name,
newName: environmentName,
oldSlug: oldEnvironment.slug,
newSlug: environmentSlug.toLowerCase()
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully update environment",
@@ -179,6 +210,8 @@ export const deleteWorkspaceEnvironment = async (
if (envIndex === -1) {
throw new Error("Invalid environment given");
}
const oldEnvironment = workspace.environments[envIndex];
workspace.environments.splice(envIndex, 1);
await workspace.save();
@@ -218,6 +251,20 @@ export const deleteWorkspaceEnvironment = async (
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_ENVIRONMENT,
metadata: {
name: oldEnvironment.name,
slug: oldEnvironment.slug
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully deleted environment",
workspace: workspaceId,
@@ -4,7 +4,8 @@ import bcrypt from "bcrypt";
import { ServiceTokenData } from "../../models";
import { getSaltRounds } from "../../config";
import { BadRequestError } from "../../utils/errors";
import { ActorType } from "../../ee/models";
import { ActorType, EventType } from "../../ee/models";
import { EEAuditLogService } from "../../ee/services";
/**
* Return service token data associated with service token on request
@@ -99,6 +100,20 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
if (!serviceTokenData) throw new Error("Failed to find service token data");
const serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_SERVICE_TOKEN,
metadata: {
name,
scopes
}
},
{
workspaceId
}
);
return res.status(200).send({
serviceToken,
@@ -116,6 +131,24 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
const { serviceTokenDataId } = req.params;
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
if (!serviceTokenData) return res.status(200).send({
message: "Failed to delete service token"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_SERVICE_TOKEN,
metadata: {
name: serviceTokenData.name,
scopes: serviceTokenData?.scopes
}
},
{
workspaceId: serviceTokenData.workspace
}
);
return res.status(200).send({
serviceTokenData