feat(webhook): implemented api for webhooks

This commit is contained in:
akhilmhdh
2023-07-13 23:21:18 +05:30
parent 8a19cfe0c6
commit 0f81c78639
18 changed files with 748 additions and 364 deletions
+38 -22
View File
@@ -30,9 +30,11 @@ import Folder from "../../models/folder";
import {
getFolderByPath,
getFolderIdFromServiceToken,
searchByFolderId
searchByFolderId,
searchByFolderIdWithDir
} from "../../services/FolderService";
import { isValidScope } from "../../helpers/secrets";
import path from "path";
/**
* Peform a batch of any specified CUD secret operations
@@ -47,14 +49,13 @@ export const batchSecrets = async (req: Request, res: Response) => {
const {
workspaceId,
environment,
requests,
secretPath
requests
}: {
workspaceId: string;
environment: string;
requests: BatchSecretRequest[];
secretPath: string;
} = req.body;
let secretPath = req.body.secretPath as string;
let folderId = req.body.folderId as string;
const createSecrets: BatchSecret[] = [];
@@ -68,10 +69,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
});
const folders = await Folder.findOne({ workspace: workspaceId, environment });
if (folders && folderId !== "root") {
const folder = searchByFolderId(folders.nodes, folderId as string);
if (!folder) throw BadRequestError({ message: "Folder not found" });
}
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
@@ -87,6 +84,15 @@ export const batchSecrets = async (req: Request, res: Response) => {
folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
}
if (folders && folderId !== "root") {
const folder = searchByFolderIdWithDir(folders.nodes, folderId as string);
if (!folder?.folder) throw BadRequestError({ message: "Folder not found" });
secretPath = path.join(
"/",
...folder.dir.map(({ name }) => name).filter((name) => name !== "root")
);
}
for await (const request of requests) {
// do a validation
@@ -319,7 +325,10 @@ export const batchSecrets = async (req: Request, res: Response) => {
// // trigger event - push secrets
await EventService.handleEvent({
event: eventPushSecrets({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
environment,
// root condition else this will be filled according to the path or folderid
secretPath: secretPath || "/"
})
});
@@ -535,7 +544,9 @@ export const createSecrets = async (req: Request, res: Response) => {
// trigger event - push secrets
await EventService.handleEvent({
event: eventPushSecrets({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
environment,
secretPath: secretPath || "/"
})
});
}, 5000);
@@ -1033,13 +1044,16 @@ export const updateSecrets = async (req: Request, res: Response) => {
Object.keys(workspaceSecretObj).forEach(async (key) => {
// trigger event - push secrets
setTimeout(async () => {
await EventService.handleEvent({
event: eventPushSecrets({
workspaceId: new Types.ObjectId(key)
})
});
}, 10000);
// This route is not used anymore thus keep it commented out as it does not expose environment
// it will end up creating a lot of requests from the server
// setTimeout(async () => {
// await EventService.handleEvent({
// event: eventPushSecrets({
// workspaceId: new Types.ObjectId(key),
// environment,
// })
// });
// }, 10000);
const updateAction = await EELogService.createAction({
name: ACTION_UPDATE_SECRETS,
@@ -1174,11 +1188,13 @@ export const deleteSecrets = async (req: Request, res: Response) => {
Object.keys(workspaceSecretObj).forEach(async (key) => {
// trigger event - push secrets
await EventService.handleEvent({
event: eventPushSecrets({
workspaceId: new Types.ObjectId(key)
})
});
// DEPRECIATED(akhilmhdh): as this would cause server to send so many request
// and this route is not used anymore thus like snapshot keeping it commented out
// await EventService.handleEvent({
// event: eventPushSecrets({
// workspaceId: new Types.ObjectId(key)
// })
// });
const deleteAction = await EELogService.createAction({
name: ACTION_DELETE_SECRETS,
userId: req.user?._id,
+91 -103
View File
@@ -1,34 +1,29 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Key, Membership, ServiceTokenData, Workspace } from "../../models";
import {
Key,
Membership,
ServiceTokenData,
Workspace,
} from "../../models";
import {
pullSecrets as pull,
v2PushSecrets as push,
reformatPullSecrets,
pullSecrets as pull,
v2PushSecrets as push,
reformatPullSecrets
} from "../../helpers/secret";
import { pushKeys } from "../../helpers/key";
import { EventService, TelemetryService } from "../../services";
import { eventPushSecrets } from "../../events";
interface V2PushSecret {
type: string; // personal or shared
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretKeyHash: string;
secretValueCiphertext: string;
secretValueIV: string;
secretValueTag: string;
secretValueHash: string;
secretCommentCiphertext?: string;
secretCommentIV?: string;
secretCommentTag?: string;
secretCommentHash?: string;
type: string; // personal or shared
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretKeyHash: string;
secretValueCiphertext: string;
secretValueIV: string;
secretValueTag: string;
secretValueHash: string;
secretCommentCiphertext?: string;
secretCommentIV?: string;
secretCommentTag?: string;
secretCommentHash?: string;
}
/**
@@ -39,7 +34,7 @@ interface V2PushSecret {
* @returns
*/
export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
// upload (encrypted) secrets to workspace with id [workspaceId]
// upload (encrypted) secrets to workspace with id [workspaceId]
const postHogClient = await TelemetryService.getPostHogClient();
let { secrets }: { secrets: V2PushSecret[] } = req.body;
const { keys, environment, channel } = req.body;
@@ -62,13 +57,13 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
environment,
secrets,
channel: channel ? channel : "cli",
ipAddress: req.realIP,
ipAddress: req.realIP
});
await pushKeys({
userId: req.user._id,
workspaceId,
keys,
keys
});
if (postHogClient) {
@@ -79,8 +74,8 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : "cli",
},
channel: channel ? channel : "cli"
}
});
}
@@ -89,12 +84,13 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
event: eventPushSecrets({
workspaceId: new Types.ObjectId(workspaceId),
environment,
}),
secretPath: "/"
})
});
return res.status(200).send({
message: "Successfully uploaded workspace secrets",
});
return res.status(200).send({
message: "Successfully uploaded workspace secrets"
});
};
/**
@@ -105,7 +101,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
* @returns
*/
export const pullSecrets = async (req: Request, res: Response) => {
let secrets;
let secrets;
const postHogClient = await TelemetryService.getPostHogClient();
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
@@ -128,7 +124,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
workspaceId,
environment,
channel: channel ? channel : "cli",
ipAddress: req.realIP,
ipAddress: req.realIP
});
if (channel !== "cli") {
@@ -144,18 +140,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : "cli",
},
channel: channel ? channel : "cli"
}
});
}
return res.status(200).send({
secrets,
});
return res.status(200).send({
secrets
});
};
export const getWorkspaceKey = async (req: Request, res: Response) => {
/*
/*
#swagger.summary = 'Return encrypted project key'
#swagger.description = 'Return encrypted project key'
@@ -183,43 +179,38 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
}
}
*/
let key;
let key;
const { workspaceId } = req.params;
key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id,
receiver: req.user._id
}).populate("sender", "+publicKey");
if (!key) throw new Error("Failed to find workspace key");
return res.status(200).json(key);
}
export const getWorkspaceServiceTokenData = async (
req: Request,
res: Response
) => {
return res.status(200).json(key);
};
export const getWorkspaceServiceTokenData = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const serviceTokenData = await ServiceTokenData
.find({
workspace: workspaceId,
})
.select("+encryptedKey +iv +tag");
const serviceTokenData = await ServiceTokenData.find({
workspace: workspaceId
}).select("+encryptedKey +iv +tag");
return res.status(200).send({
serviceTokenData,
});
}
return res.status(200).send({
serviceTokenData
});
};
/**
* Return memberships for workspace with id [workspaceId]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
/*
/*
#swagger.summary = 'Return project memberships'
#swagger.description = 'Return project memberships'
@@ -255,22 +246,22 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const memberships = await Membership.find({
workspace: workspaceId,
workspace: workspaceId
}).populate("user", "+publicKey");
return res.status(200).send({
memberships,
});
}
return res.status(200).send({
memberships
});
};
/**
* Update role of membership with id [membershipId] to role [role]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const updateWorkspaceMembership = async (req: Request, res: Response) => {
/*
/*
#swagger.summary = 'Update project membership'
#swagger.description = 'Update project membership'
@@ -323,33 +314,32 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
}
}
*/
const {
membershipId,
} = req.params;
const { membershipId } = req.params;
const { role } = req.body;
const membership = await Membership.findByIdAndUpdate(
membershipId,
{
role,
}, {
new: true,
role
},
{
new: true
}
);
return res.status(200).send({
membership,
});
}
return res.status(200).send({
membership
});
};
/**
* Delete workspace membership with id [membershipId]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const deleteWorkspaceMembership = async (req: Request, res: Response) => {
/*
/*
#swagger.summary = 'Delete project membership'
#swagger.description = 'Delete project membership'
@@ -385,23 +375,21 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
}
}
*/
const {
membershipId,
} = req.params;
const { membershipId } = req.params;
const membership = await Membership.findByIdAndDelete(membershipId);
if (!membership) throw new Error("Failed to delete workspace membership");
await Key.deleteMany({
receiver: membership.user,
workspace: membership.workspace,
workspace: membership.workspace
});
return res.status(200).send({
membership,
});
}
return res.status(200).send({
membership
});
};
/**
* Change autoCapitilzation Rule of workspace
@@ -415,18 +403,18 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
const workspace = await Workspace.findOneAndUpdate(
{
_id: workspaceId,
_id: workspaceId
},
{
autoCapitalization,
autoCapitalization
},
{
new: true,
new: true
}
);
return res.status(200).send({
message: "Successfully changed autoCapitalization setting",
workspace,
});
return res.status(200).send({
message: "Successfully changed autoCapitalization setting",
workspace
});
};