This commit is contained in:
Tuan Dang
2023-06-17 10:35:49 +07:00
22 changed files with 795 additions and 473 deletions

View File

@@ -11,6 +11,7 @@ import {
validateFolderName,
generateFolderId,
getParentFromFolderId,
getFolderByPath,
} from "../../services/FolderService";
import { ADMIN, MEMBER } from "../../variables";
import { validateMembership } from "../../helpers/membership";
@@ -177,11 +178,13 @@ export const deleteFolder = async (req: Request, res: Response) => {
// TODO: validate workspace
export const getFolders = async (req: Request, res: Response) => {
const { workspaceId, environment, parentFolderId } = req.query as {
workspaceId: string;
environment: string;
parentFolderId?: 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) {
@@ -196,6 +199,20 @@ export const getFolders = async (req: Request, res: Response) => {
acceptedRoles: [ADMIN, MEMBER],
});
// if instead of parentFolderId given a path like /folder1/folder2
if (parentFolderPath) {
const folder = getFolderByPath(folders.nodes, parentFolderPath);
if (!folder) {
res.send({ folders: [], dir: [] });
return;
}
// 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 }],
});
}
if (!parentFolderId) {
const rootFolders = folders.nodes.children.map(({ id, name }) => ({
id,

View File

@@ -700,11 +700,15 @@ export const getSecrets = async (req: Request, res: Response) => {
(!folders && folderId && folderId !== "root") ||
(!folders && secretPath)
) {
throw BadRequestError({ message: "Folder not found" });
res.send({ secrets: [] });
return;
}
if (folders && folderId !== "root") {
const folder = searchByFolderId(folders.nodes, folderId as string);
if (!folder) throw BadRequestError({ message: "Folder not found" });
if (!folder) {
res.send({ secrets: [] });
return;
}
}
if (req.authData.authPayload instanceof ServiceTokenData) {
@@ -720,10 +724,11 @@ export const getSecrets = async (req: Request, res: Response) => {
}
if (folders && secretPath) {
if (!folders) throw BadRequestError({ message: "Folder not found" });
// avoid throwing error and send empty list
const folder = getFolderByPath(folders.nodes, secretPath as string);
if (!folder) {
throw BadRequestError({ message: "Secret path not found" });
res.send({ secrets: [] });
return;
}
folderId = folder.id;
}

View File

@@ -34,7 +34,7 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
secret,
key
});
return rep;
})
});
@@ -88,7 +88,7 @@ export const createSecretRaw = async (req: Request, res: Response) => {
secretComment,
secretPath = "/"
} = req.body;
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
});
@@ -102,12 +102,12 @@ export const createSecretRaw = async (req: Request, res: Response) => {
plaintext: secretValue,
key
});
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretComment,
key
});
});
const secret = await SecretService.createSecret({
secretName,
workspaceId: new Types.ObjectId(workspaceId),
@@ -135,7 +135,7 @@ export const createSecretRaw = async (req: Request, res: Response) => {
const secretWithoutBlindIndex = secret.toObject();
delete secretWithoutBlindIndex.secretBlindIndex;
return res.status(200).send({
secret: repackageSecretToRaw({
secret: secretWithoutBlindIndex,
@@ -202,11 +202,11 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
*/
export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
const { secretName } = req.params;
const {
workspaceId,
environment,
type,
secretPath = "/"
const {
workspaceId,
environment,
type,
secretPath = "/"
} = req.body;
const { secret } = await SecretService.deleteSecret({
@@ -391,11 +391,11 @@ export const updateSecretByName = async (req: Request, res: Response) => {
*/
export const deleteSecretByName = async (req: Request, res: Response) => {
const { secretName } = req.params;
const {
workspaceId,
environment,
type,
secretPath = "/"
const {
workspaceId,
environment,
type,
secretPath = "/"
} = req.body;
const { secret } = await SecretService.deleteSecret({

View File

@@ -1,16 +1,16 @@
import rateLimit from 'express-rate-limit';
const MongoStore = require('rate-limit-mongo');
// const MongoStore = require('rate-limit-mongo');
// 200 per minute
export const apiLimiter = rateLimit({
store: new MongoStore({
uri: process.env.MONGO_URL,
expireTimeMs: 1000 * 60,
collectionName: "expressRateRecords-apiLimiter",
errorHandler: console.error.bind(null, 'rate-limit-mongo')
}),
windowMs: 1000 * 60,
max: 200,
// store: new MongoStore({
// uri: process.env.MONGO_URL,
// expireTimeMs: 1000 * 60,
// collectionName: "expressRateRecords-apiLimiter",
// errorHandler: console.error.bind(null, 'rate-limit-mongo')
// }),
windowMs: 60 * 1000,
max: 240,
standardHeaders: true,
legacyHeaders: false,
skip: (request) => {
@@ -23,14 +23,14 @@ export const apiLimiter = rateLimit({
// 50 requests per 1 hours
const authLimit = rateLimit({
store: new MongoStore({
uri: process.env.MONGO_URL,
expireTimeMs: 1000 * 60 * 60,
errorHandler: console.error.bind(null, 'rate-limit-mongo'),
collectionName: "expressRateRecords-authLimit",
}),
windowMs: 1000 * 60 * 60,
max: 50,
// store: new MongoStore({
// uri: process.env.MONGO_URL,
// expireTimeMs: 1000 * 60 * 60,
// errorHandler: console.error.bind(null, 'rate-limit-mongo'),
// collectionName: "expressRateRecords-authLimit",
// }),
windowMs: 60 * 1000,
max: 10,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req, res) => {
@@ -40,14 +40,14 @@ const authLimit = rateLimit({
// 5 requests per 1 hour
export const passwordLimiter = rateLimit({
store: new MongoStore({
uri: process.env.MONGO_URL,
expireTimeMs: 1000 * 60 * 60,
errorHandler: console.error.bind(null, 'rate-limit-mongo'),
collectionName: "expressRateRecords-passwordLimiter",
}),
windowMs: 1000 * 60 * 60,
max: 5,
// store: new MongoStore({
// uri: process.env.MONGO_URL,
// expireTimeMs: 1000 * 60 * 60,
// errorHandler: console.error.bind(null, 'rate-limit-mongo'),
// collectionName: "expressRateRecords-passwordLimiter",
// }),
windowMs: 60 * 60 * 1000,
max: 10,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req, res) => {

View File

@@ -57,7 +57,7 @@ import { getFolderIdFromServiceToken } from "../services/FolderService";
export const repackageSecretToRaw = ({
secret,
key
}:{
}: {
secret: ISecret;
key: string;
}) => {
@@ -76,8 +76,8 @@ export const repackageSecretToRaw = ({
key
});
let secretComment: string = '';
let secretComment: string = '';
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
secretComment = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
@@ -86,7 +86,7 @@ export const repackageSecretToRaw = ({
key
});
}
return ({
_id: secret._id,
version: secret.version,
@@ -503,7 +503,7 @@ export const getSecretsHelper = async ({
folder: folderId,
type: SECRET_PERSONAL,
...getAuthDataPayloadUserObj(authData),
}).lean();
}).populate("tags").lean();
// concat with shared secrets
secrets = secrets.concat(
@@ -515,7 +515,7 @@ export const getSecretsHelper = async ({
secretBlindIndex: {
$nin: secrets.map((secret) => secret.secretBlindIndex),
},
}).lean()
}).populate("tags").lean()
);
// (EE) create (audit) log
@@ -553,7 +553,7 @@ export const getSecretsHelper = async ({
},
});
}
return secrets;
};
@@ -652,7 +652,7 @@ export const getSecretHelper = async ({
},
});
}
return secret;
};
@@ -843,7 +843,7 @@ export const deleteSecretHelper = async ({
// if using service token filter towards the folderId by secretpath
if (authData.authPayload instanceof ServiceTokenData) {
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
if (secretPath !== serviceTkScopedSecretPath) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
@@ -909,12 +909,12 @@ export const deleteSecretHelper = async ({
});
action && (await EELogService.createLog({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP,
}));
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP,
}));
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
@@ -941,7 +941,7 @@ export const deleteSecretHelper = async ({
},
});
}
return ({
secrets,
secret

View File

@@ -63,6 +63,7 @@ router.get(
query("workspaceId").exists().isString().trim(),
query("environment").exists().isString().trim(),
query("parentFolderId").optional().isString().trim(),
query("parentFolderPath").optional().isString().trim(),
validateRequest,
getFolders
);