feat: implemented service token support for folder and secret import api

This commit is contained in:
Akhil Mohan
2023-08-08 22:20:36 +05:30
parent 221e601173
commit 8330890087
4 changed files with 303 additions and 89 deletions
@@ -1,8 +1,12 @@
import { Request, Response } from "express";
import { Secret } from "../../models";
import { Types } from "mongoose";
import Folder from "../../models/folder";
import { BadRequestError } from "../../utils/errors";
import path from "path";
import { EventType, FolderVersion } from "../../ee/models";
import { EEAuditLogService, EESecretService } from "../../ee/services";
import { validateMembership } from "../../helpers/membership";
import { isValidScope } from "../../helpers/secrets";
import { Secret, ServiceTokenData } from "../../models";
import Folder, { TFolderRootSchema } from "../../models/folder";
import {
appendFolder,
deleteFolderById,
@@ -11,31 +15,50 @@ import {
getFolderByPath,
getFolderPath,
getParentFromFolderId,
searchByFolderId,
searchByFolderIdWithDir,
validateFolderName,
validateFolderName
} from "../../services/FolderService";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { ADMIN, MEMBER } from "../../variables";
import { validateMembership } from "../../helpers/membership";
import { EventType, FolderVersion } from "../../ee/models";
import { EEAuditLogService, EESecretService } from "../../ee/services";
const getFolderWithPathFromId = (folders: TFolderRootSchema, parentFolderId: string) => {
const search = searchByFolderIdWithDir(folders.nodes, parentFolderId);
if (!search) {
throw { message: "Folder permission denied" };
}
const { folder, dir } = search;
const folderPath = path.join(
"/",
...dir.filter(({ name }) => name !== "root").map(({ name }) => name)
);
return { folder, folderPath, dir };
};
// verify workspace id/environment
export const createFolder = async (req: Request, res: Response) => {
const { workspaceId, environment, folderName, parentFolderId } = req.body;
if (!validateFolderName(folderName)) {
throw BadRequestError({
message: "Folder name cannot contain spaces. Only underscore and dashes",
message: "Folder name cannot contain spaces. Only underscore and dashes"
});
}
const folders = await Folder.findOne({
workspace: workspaceId,
environment,
environment
}).lean();
// space has no folders initialized
if (!folders) {
if (req.authData.authPayload instanceof ServiceTokenData) {
// root check
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, "/");
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const id = generateFolderId();
const folder = new Folder({
workspace: workspaceId,
@@ -44,19 +67,19 @@ export const createFolder = async (req: Request, res: Response) => {
id: "root",
name: "root",
version: 1,
children: [{ id, name: folderName, children: [], version: 1 }],
},
children: [{ id, name: folderName, children: [], version: 1 }]
}
});
await folder.save();
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: folder.nodes,
nodes: folder.nodes
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
environment
});
await EEAuditLogService.createAuditLog(
@@ -81,20 +104,37 @@ export const createFolder = async (req: Request, res: Response) => {
const folder = appendFolder(folders.nodes, { folderName, parentFolderId });
await Folder.findByIdAndUpdate(folders._id, folders);
const parentFolder = searchByFolderId(folders.nodes, parentFolderId);
const { folder: parentFolder, folderPath: parentFolderPath } = getFolderWithPathFromId(
folders,
parentFolderId || "root"
);
if (req.authData.authPayload instanceof ServiceTokenData) {
// root check
const isValidScopeAccess = isValidScope(
req.authData.authPayload,
environment,
parentFolderPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
await Folder.findByIdAndUpdate(folders._id, folders);
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolderId,
folderId: parentFolderId
});
const folderPath = await getFolderPath(folders, folder.id);
@@ -121,18 +161,25 @@ export const createFolder = async (req: Request, res: Response) => {
export const updateFolderById = async (req: Request, res: Response) => {
const { folderId } = req.params;
const { name, workspaceId, environment } = req.body;
if (!validateFolderName(name)) {
throw BadRequestError({
message: "Folder name cannot contain spaces. Only underscore and dashes"
});
}
const folders = await Folder.findOne({ workspace: workspaceId, environment });
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(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER]
});
}
const parentFolder = getParentFromFolderId(folders.nodes, folderId);
if (!parentFolder) {
@@ -144,7 +191,15 @@ export const updateFolderById = async (req: Request, res: Response) => {
throw BadRequestError({ message: "The folder doesn't exist" });
}
const oldFolderName = folder.name;
if (req.authData.authPayload instanceof ServiceTokenData) {
const { folderPath: secretPath } = getFolderWithPathFromId(folders, parentFolder.id);
// root check
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
parentFolder.version += 1;
folder.name = name;
@@ -152,14 +207,14 @@ export const updateFolderById = async (req: Request, res: Response) => {
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolder.id,
folderId: parentFolder.id
});
const folderPath = await getFolderPath(folders, folder.id);
@@ -183,7 +238,7 @@ export const updateFolderById = async (req: Request, res: Response) => {
return res.json({
message: "Successfully updated folder",
folder: { name: folder.name, id: folder.id },
folder: { name: folder.name, id: folder.id }
});
};
@@ -196,12 +251,14 @@ export const deleteFolder = async (req: Request, res: Response) => {
throw BadRequestError({ message: "The folder doesn't exist" });
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER]
});
}
const folderPath = await getFolderPath(folders, folderId);
@@ -211,6 +268,14 @@ export const deleteFolder = async (req: Request, res: Response) => {
}
const { deletedNode: delFolder, parent: parentFolder } = delOp;
if (req.authData.authPayload instanceof ServiceTokenData) {
const { folderPath: secretPath } = getFolderWithPathFromId(folders, parentFolder.id);
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
parentFolder.version += 1;
const delFolderIds = getAllFolderIds(delFolder);
@@ -218,21 +283,21 @@ export const deleteFolder = async (req: Request, res: Response) => {
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
if (delFolderIds.length) {
await Secret.deleteMany({
folder: { $in: delFolderIds.map(({ id }) => id) },
workspace: workspaceId,
environment,
environment
});
}
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolder.id,
folderId: parentFolder.id
});
await EEAuditLogService.createAuditLog(
@@ -256,13 +321,12 @@ export const deleteFolder = async (req: Request, res: Response) => {
// TODO: validate workspace
export const getFolders = async (req: Request, res: Response) => {
const { workspaceId, environment, parentFolderId, parentFolderPath } =
req.query as {
workspaceId: string;
environment: string;
parentFolderId?: string;
parentFolderPath?: string;
};
const { workspaceId, environment, parentFolderId, parentFolderPath } = req.query as {
workspaceId: string;
environment: string;
parentFolderId?: string;
parentFolderPath?: string;
};
const folders = await Folder.findOne({ workspace: workspaceId, environment });
if (!folders) {
@@ -270,16 +334,29 @@ export const getFolders = async (req: Request, res: Response) => {
return;
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER]
});
}
// if instead of parentFolderId given a path like /folder1/folder2
if (parentFolderPath) {
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(
req.authData.authPayload,
environment,
parentFolderPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const folder = getFolderByPath(folders.nodes, parentFolderPath);
if (!folder) {
res.send({ folders: [], dir: [] });
return;
@@ -287,27 +364,36 @@ export const getFolders = async (req: Request, res: Response) => {
// dir is not needed at present as this is only used in overview section of secrets
res.send({
folders: folder.children.map(({ id, name }) => ({ id, name })),
dir: [{ name: folder.name, id: folder.id }],
dir: [{ name: folder.name, id: folder.id }]
});
}
if (!parentFolderId) {
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, "/");
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const rootFolders = folders.nodes.children.map(({ id, name }) => ({
id,
name,
name
}));
res.send({ folders: rootFolders });
return;
}
const folderBySearch = searchByFolderIdWithDir(folders.nodes, parentFolderId);
if (!folderBySearch) {
throw BadRequestError({ message: "The folder doesn't exist" });
const { folder, folderPath, dir } = getFolderWithPathFromId(folders, parentFolderId);
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, folderPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const { folder, dir } = folderBySearch;
res.send({
folders: folder.children.map(({ id, name }) => ({ id, name })),
dir,
dir
});
};