mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
Finish preliminary v3/secrets/raw endpoints
This commit is contained in:
@@ -2,6 +2,240 @@ import { Request, Response } from "express";
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { SecretService, EventService } from "../../services";
|
import { SecretService, EventService } from "../../services";
|
||||||
import { eventPushSecrets } from "../../events";
|
import { eventPushSecrets } from "../../events";
|
||||||
|
import { BotService } from "../../services";
|
||||||
|
import { repackageSecretToRaw } from "../../helpers/secrets";
|
||||||
|
import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return secrets for workspace with id [workspaceId] and environment
|
||||||
|
* [environment] in plaintext
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const getSecretsRaw = async (req: Request, res: Response) => {
|
||||||
|
const workspaceId = req.query.workspaceId as string;
|
||||||
|
const environment = req.query.environment as string;
|
||||||
|
const secretPath = req.query.secretPath as string;
|
||||||
|
|
||||||
|
const secrets = await SecretService.getSecrets({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
authData: req.authData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secrets: secrets.map((secret) => {
|
||||||
|
const rep = repackageSecretToRaw({
|
||||||
|
secret,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return secret with name [secretName] in plaintext
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
|
const { secretName } = req.params;
|
||||||
|
const workspaceId = req.query.workspaceId as string;
|
||||||
|
const environment = req.query.environment as string;
|
||||||
|
const secretPath = req.query.secretPath as string;
|
||||||
|
const type = req.query.type as "shared" | "personal" | undefined;
|
||||||
|
|
||||||
|
const secret = await SecretService.getSecret({
|
||||||
|
secretName,
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
secretPath,
|
||||||
|
authData: req.authData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secret: repackageSecretToRaw({
|
||||||
|
secret,
|
||||||
|
key
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create secret with name [secretName] in plaintext
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const createSecretRaw = async (req: Request, res: Response) => {
|
||||||
|
const { secretName } = req.params;
|
||||||
|
const {
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
secretValue,
|
||||||
|
secretComment,
|
||||||
|
secretPath = "/"
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
|
plaintext: secretName,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
|
plaintext: secretValue,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
|
plaintext: secretComment,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await SecretService.createSecret({
|
||||||
|
secretName,
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
authData: req.authData,
|
||||||
|
secretKeyCiphertext: secretKeyEncrypted.ciphertext,
|
||||||
|
secretKeyIV: secretKeyEncrypted.iv,
|
||||||
|
secretKeyTag: secretKeyEncrypted.tag,
|
||||||
|
secretValueCiphertext: secretValueEncrypted.ciphertext,
|
||||||
|
secretValueIV: secretValueEncrypted.iv,
|
||||||
|
secretValueTag: secretValueEncrypted.tag,
|
||||||
|
secretPath,
|
||||||
|
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
||||||
|
secretCommentIV: secretCommentEncrypted.iv,
|
||||||
|
secretCommentTag: secretCommentEncrypted.tag
|
||||||
|
});
|
||||||
|
|
||||||
|
await EventService.handleEvent({
|
||||||
|
event: eventPushSecrets({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretWithoutBlindIndex = secret.toObject();
|
||||||
|
delete secretWithoutBlindIndex.secretBlindIndex;
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secret: repackageSecretToRaw({
|
||||||
|
secret: secretWithoutBlindIndex,
|
||||||
|
key
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update secret with name [secretName]
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
|
const { secretName } = req.params;
|
||||||
|
const {
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
secretValue,
|
||||||
|
secretPath = "/",
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
|
plaintext: secretValue,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await SecretService.updateSecret({
|
||||||
|
secretName,
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
authData: req.authData,
|
||||||
|
secretValueCiphertext: secretValueEncrypted.ciphertext,
|
||||||
|
secretValueIV: secretValueEncrypted.iv,
|
||||||
|
secretValueTag: secretValueEncrypted.tag,
|
||||||
|
secretPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
await EventService.handleEvent({
|
||||||
|
event: eventPushSecrets({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secret: repackageSecretToRaw({
|
||||||
|
secret,
|
||||||
|
key
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete secret with name [secretName]
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
|
const { secretName } = req.params;
|
||||||
|
const {
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
secretPath = "/"
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
const { secret } = await SecretService.deleteSecret({
|
||||||
|
secretName,
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
authData: req.authData,
|
||||||
|
secretPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
await EventService.handleEvent({
|
||||||
|
event: eventPushSecrets({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
|
environment,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secret: repackageSecretToRaw({
|
||||||
|
secret,
|
||||||
|
key
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secrets for workspace with id [workspaceId] and environment
|
* Get secrets for workspace with id [workspaceId] and environment
|
||||||
@@ -27,7 +261,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secret with name [secretName]
|
* Return secret with name [secretName]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
@@ -58,59 +292,6 @@ export const getSecretByName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecret = async (req: Request, res: Response) => {
|
export const createSecret = async (req: Request, res: Response) => {
|
||||||
<<<<<<< HEAD
|
|
||||||
const { secretName } = req.params;
|
|
||||||
const {
|
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
type,
|
|
||||||
secretKeyCiphertext,
|
|
||||||
secretKeyIV,
|
|
||||||
secretKeyTag,
|
|
||||||
secretValue,
|
|
||||||
secretValueCiphertext,
|
|
||||||
secretValueIV,
|
|
||||||
secretValueTag,
|
|
||||||
secretComment,
|
|
||||||
secretCommentCiphertext,
|
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const secret = await SecretService.createSecret({
|
|
||||||
secretName,
|
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
|
||||||
environment,
|
|
||||||
type,
|
|
||||||
authData: req.authData,
|
|
||||||
secretKeyCiphertext,
|
|
||||||
secretKeyIV,
|
|
||||||
secretKeyTag,
|
|
||||||
secretValue,
|
|
||||||
secretValueCiphertext,
|
|
||||||
secretValueIV,
|
|
||||||
secretValueTag,
|
|
||||||
secretComment,
|
|
||||||
secretCommentCiphertext,
|
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag
|
|
||||||
});
|
|
||||||
|
|
||||||
await EventService.handleEvent({
|
|
||||||
event: eventPushSecrets({
|
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
|
||||||
environment
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const secretWithoutBlindIndex = secret.toObject();
|
|
||||||
delete secretWithoutBlindIndex.secretBlindIndex;
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
secret: secretWithoutBlindIndex
|
|
||||||
});
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -141,13 +322,9 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
secretPath,
|
secretPath,
|
||||||
...(secretCommentCiphertext && secretCommentIV && secretCommentTag
|
secretCommentCiphertext,
|
||||||
? {
|
secretCommentIV,
|
||||||
secretCommentCiphertext,
|
secretCommentTag
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
@@ -164,7 +341,7 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
secret: secretWithoutBlindIndex,
|
secret: secretWithoutBlindIndex,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
>>>>>>> origin
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update secret with name [secretName]
|
* Update secret with name [secretName]
|
||||||
@@ -214,7 +391,12 @@ export const updateSecretByName = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const deleteSecretByName = async (req: Request, res: Response) => {
|
export const deleteSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const { workspaceId, environment, type, secretPath = "/" } = req.body;
|
const {
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
type,
|
||||||
|
secretPath = "/"
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
const { secret } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
secretName,
|
secretName,
|
||||||
|
|||||||
+66
-143
@@ -37,7 +37,7 @@ import {
|
|||||||
encryptSymmetric128BitHexKeyUTF8,
|
encryptSymmetric128BitHexKeyUTF8,
|
||||||
decryptSymmetric128BitHexKeyUTF8,
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
} from '../utils/crypto';
|
} from '../utils/crypto';
|
||||||
import { BotService, TelemetryService } from '../services';
|
import { TelemetryService } from '../services';
|
||||||
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
|
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
|
||||||
import { EESecretService, EELogService } from "../ee/services";
|
import { EESecretService, EELogService } from "../ee/services";
|
||||||
import {
|
import {
|
||||||
@@ -46,6 +46,60 @@ import {
|
|||||||
} from "../utils/auth";
|
} from "../utils/auth";
|
||||||
import { getFolderIdFromServiceToken } from "../services/FolderService";
|
import { getFolderIdFromServiceToken } from "../services/FolderService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object containing secret [secret] but with its value, key, comment decrypted.
|
||||||
|
*
|
||||||
|
* Precondition: the workspace for secret [secret] must have E2EE disabled
|
||||||
|
* @param {ISecret} secret - secret to repackage to raw
|
||||||
|
* @param {String} key - symmetric key to use to decrypt secret
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const repackageSecretToRaw = ({
|
||||||
|
secret,
|
||||||
|
key
|
||||||
|
}:{
|
||||||
|
secret: ISecret;
|
||||||
|
key: string;
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
|
iv: secret.secretKeyIV,
|
||||||
|
tag: secret.secretKeyTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretValueCiphertext,
|
||||||
|
iv: secret.secretValueIV,
|
||||||
|
tag: secret.secretValueTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
let secretComment: string = '';
|
||||||
|
|
||||||
|
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
||||||
|
secretComment = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretCommentCiphertext,
|
||||||
|
iv: secret.secretCommentIV,
|
||||||
|
tag: secret.secretCommentTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ({
|
||||||
|
_id: secret._id,
|
||||||
|
version: secret.version,
|
||||||
|
workspace: secret.workspace,
|
||||||
|
type: secret.type,
|
||||||
|
environment: secret.environment,
|
||||||
|
user: secret.user,
|
||||||
|
secretKey,
|
||||||
|
secretValue,
|
||||||
|
secretComment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret blind index data containing encrypted blind index [salt]
|
* Create secret blind index data containing encrypted blind index [salt]
|
||||||
* for workspace with id [workspaceId]
|
* for workspace with id [workspaceId]
|
||||||
@@ -244,23 +298,6 @@ export const generateSecretBlindIndexHelper = async ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// secretName,
|
|
||||||
// workspaceId,
|
|
||||||
// environment,
|
|
||||||
// type,
|
|
||||||
// authData,
|
|
||||||
// secretKeyCiphertext,
|
|
||||||
// secretKeyIV,
|
|
||||||
// secretKeyTag,
|
|
||||||
// secretValue,
|
|
||||||
// secretValueCiphertext,
|
|
||||||
// secretValueIV,
|
|
||||||
// secretValueTag,
|
|
||||||
// secretCommentCiphertext,
|
|
||||||
// secretCommentIV,
|
|
||||||
// secretCommentTag,
|
|
||||||
// folderId,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret with name [secretName]
|
* Create secret with name [secretName]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@@ -280,11 +317,9 @@ export const createSecretHelper = async ({
|
|||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
secretValue,
|
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
secretComment,
|
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
@@ -340,52 +375,6 @@ export const createSecretHelper = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// can generate secretKeyCiphertext etc. if not E2EE.
|
|
||||||
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
|
|
||||||
|
|
||||||
if (!isWorkspaceE2EE) {
|
|
||||||
// if workspace is not end-to-end encrypted, then decrypt
|
|
||||||
// secret and return it in plaintext
|
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secretName) {
|
|
||||||
const encryptedSecretKey = encryptSymmetric128BitHexKeyUTF8({
|
|
||||||
plaintext: secretName,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
secretKeyCiphertext = encryptedSecretKey.ciphertext;
|
|
||||||
secretKeyIV = encryptedSecretKey.iv;
|
|
||||||
secretKeyTag = encryptedSecretKey.tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secretValue) {
|
|
||||||
const encryptedSecretValue = encryptSymmetric128BitHexKeyUTF8({
|
|
||||||
plaintext: secretValue,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
secretValueCiphertext = encryptedSecretValue.ciphertext;
|
|
||||||
secretValueIV = encryptedSecretValue.iv;
|
|
||||||
secretValueTag = encryptedSecretValue.tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secretComment) {
|
|
||||||
const encryptedSecretComment = encryptSymmetric128BitHexKeyUTF8({
|
|
||||||
plaintext: secretComment,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
secretCommentCiphertext = encryptedSecretComment.ciphertext;
|
|
||||||
secretCommentIV = encryptedSecretComment.iv;
|
|
||||||
secretCommentTag = encryptedSecretComment.tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// create secret
|
// create secret
|
||||||
const secret = await new Secret({
|
const secret = await new Secret({
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -565,44 +554,7 @@ export const getSecretsHelper = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
|
return secrets;
|
||||||
|
|
||||||
if (!isWorkspaceE2EE) {
|
|
||||||
// if workspace is not end-to-end encrypted, then decrypt
|
|
||||||
// secret and return it in plaintext
|
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
|
|
||||||
return secrets.map((secret) => {
|
|
||||||
const secretName = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretKeyCiphertext,
|
|
||||||
iv: secret.secretKeyIV,
|
|
||||||
tag: secret.secretKeyTag,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretValueCiphertext,
|
|
||||||
iv: secret.secretValueIV,
|
|
||||||
tag: secret.secretValueTag,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
return ({
|
|
||||||
...secret,
|
|
||||||
secretName,
|
|
||||||
secretValue
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return secrets.map((secret) => ({
|
|
||||||
...secret,
|
|
||||||
secretName: null,
|
|
||||||
secretValue: null
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -701,35 +653,7 @@ export const getSecretHelper = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
|
return secret;
|
||||||
|
|
||||||
if (!isWorkspaceE2EE) {
|
|
||||||
// if workspace is not end-to-end encrypted, then decrypt
|
|
||||||
// secret and return it in plaintext
|
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
|
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretValueCiphertext,
|
|
||||||
iv: secret.secretValueIV,
|
|
||||||
tag: secret.secretValueTag,
|
|
||||||
key
|
|
||||||
});
|
|
||||||
|
|
||||||
return ({
|
|
||||||
...secret,
|
|
||||||
secretName,
|
|
||||||
secretValue
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ({
|
|
||||||
...secret,
|
|
||||||
secretName: null,
|
|
||||||
secretValue: null
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -919,6 +843,7 @@ export const deleteSecretHelper = async ({
|
|||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
@@ -938,7 +863,7 @@ export const deleteSecretHelper = async ({
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
});
|
}).lean();
|
||||||
|
|
||||||
secret = await Secret.findOneAndDelete({
|
secret = await Secret.findOneAndDelete({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
@@ -946,7 +871,7 @@ export const deleteSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
});
|
}).lean();
|
||||||
|
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
@@ -962,7 +887,7 @@ export const deleteSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData),
|
||||||
});
|
}).lean();
|
||||||
|
|
||||||
if (secret) {
|
if (secret) {
|
||||||
secrets = [secret];
|
secrets = [secret];
|
||||||
@@ -983,9 +908,7 @@ export const deleteSecretHelper = async ({
|
|||||||
secretIds: secrets.map((secret) => secret._id),
|
secretIds: secrets.map((secret) => secret._id),
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
action && (await EELogService.createLog({
|
||||||
action &&
|
|
||||||
(await EELogService.createLog({
|
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
actions: [action],
|
actions: [action],
|
||||||
@@ -1018,9 +941,9 @@ export const deleteSecretHelper = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return ({
|
||||||
secrets,
|
secrets,
|
||||||
secret,
|
secret
|
||||||
};
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ export interface CreateSecretParams {
|
|||||||
environment: string;
|
environment: string;
|
||||||
type: "shared" | "personal";
|
type: "shared" | "personal";
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
secretKeyCiphertext?: string;
|
secretKeyCiphertext: string;
|
||||||
secretKeyIV?: string;
|
secretKeyIV: string;
|
||||||
secretKeyTag?: string;
|
secretKeyTag: string;
|
||||||
secretValue?: string;
|
secretValueCiphertext: string;
|
||||||
secretValueCiphertext?: string;
|
secretValueIV: string;
|
||||||
secretValueIV?: string;
|
secretValueTag: string;
|
||||||
secretValueTag?: string;
|
|
||||||
secretComment?: string;
|
|
||||||
secretCommentCiphertext?: string;
|
secretCommentCiphertext?: string;
|
||||||
secretCommentIV?: string;
|
secretCommentIV?: string;
|
||||||
secretCommentTag?: string;
|
secretCommentTag?: string;
|
||||||
|
|||||||
@@ -16,13 +16,15 @@ const requireWorkspaceAuth = ({
|
|||||||
locationWorkspaceId,
|
locationWorkspaceId,
|
||||||
locationEnvironment = undefined,
|
locationEnvironment = undefined,
|
||||||
requiredPermissions = [],
|
requiredPermissions = [],
|
||||||
requireBlindIndicesEnabled = false
|
requireBlindIndicesEnabled = false,
|
||||||
|
requireE2EEOff = true
|
||||||
}: {
|
}: {
|
||||||
acceptedRoles: Array<'admin' | 'member'>;
|
acceptedRoles: Array<'admin' | 'member'>;
|
||||||
locationWorkspaceId: req;
|
locationWorkspaceId: req;
|
||||||
locationEnvironment?: req | undefined;
|
locationEnvironment?: req | undefined;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
requireBlindIndicesEnabled?: boolean;
|
requireBlindIndicesEnabled?: boolean;
|
||||||
|
requireE2EEOff?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return async (req: Request, res: Response, next: NextFunction) => {
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const workspaceId = req[locationWorkspaceId]?.workspaceId;
|
const workspaceId = req[locationWorkspaceId]?.workspaceId;
|
||||||
@@ -35,7 +37,8 @@ const requireWorkspaceAuth = ({
|
|||||||
environment,
|
environment,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions,
|
||||||
requireBlindIndicesEnabled
|
requireBlindIndicesEnabled,
|
||||||
|
requireE2EEOff
|
||||||
});
|
});
|
||||||
|
|
||||||
if (membership) {
|
if (membership) {
|
||||||
|
|||||||
@@ -20,6 +20,142 @@ import {
|
|||||||
PERMISSION_READ_SECRETS,
|
PERMISSION_READ_SECRETS,
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/raw",
|
||||||
|
query("workspaceId").exists().isString().trim(),
|
||||||
|
query("workspaceId").exists().isString().trim(),
|
||||||
|
query("environment").exists().isString().trim(),
|
||||||
|
query("secretPath").default("/").isString().trim(),
|
||||||
|
validateRequest,
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [
|
||||||
|
AUTH_MODE_JWT,
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: "query",
|
||||||
|
locationEnvironment: "query",
|
||||||
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: true
|
||||||
|
}),
|
||||||
|
secretsController.getSecretsRaw
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/raw/:secretName",
|
||||||
|
param("secretName").exists().isString().trim(),
|
||||||
|
query("workspaceId").exists().isString().trim(),
|
||||||
|
query("environment").exists().isString().trim(),
|
||||||
|
query("secretPath").default("/").isString().trim(),
|
||||||
|
query("type").optional().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
validateRequest,
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [
|
||||||
|
AUTH_MODE_JWT,
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: "query",
|
||||||
|
locationEnvironment: "query",
|
||||||
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: true
|
||||||
|
}),
|
||||||
|
secretsController.getSecretByNameRaw
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/raw/:secretName",
|
||||||
|
body("workspaceId").exists().isString().trim(),
|
||||||
|
body("environment").exists().isString().trim(),
|
||||||
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
body("secretValue").exists().isString().trim(),
|
||||||
|
body("secretComment").default("").isString().trim(),
|
||||||
|
body("secretPath").default("/").isString().trim(),
|
||||||
|
validateRequest,
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [
|
||||||
|
AUTH_MODE_JWT,
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: "body",
|
||||||
|
locationEnvironment: "body",
|
||||||
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: true
|
||||||
|
}),
|
||||||
|
secretsController.createSecretRaw
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/raw/:secretName",
|
||||||
|
param("secretName").exists().isString().trim(),
|
||||||
|
body("workspaceId").exists().isString().trim(),
|
||||||
|
body("environment").exists().isString().trim(),
|
||||||
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
body("secretValue").exists().isString().trim(),
|
||||||
|
body("secretPath").default("/").isString().trim(),
|
||||||
|
validateRequest,
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [
|
||||||
|
AUTH_MODE_JWT,
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: "body",
|
||||||
|
locationEnvironment: "body",
|
||||||
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: true
|
||||||
|
}),
|
||||||
|
secretsController.updateSecretByNameRaw
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/raw/:secretName",
|
||||||
|
param("secretName").exists().isString().trim(),
|
||||||
|
body("workspaceId").exists().isString().trim(),
|
||||||
|
body("environment").exists().isString().trim(),
|
||||||
|
body("secretPath").default("/").isString().trim(),
|
||||||
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
validateRequest,
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [
|
||||||
|
AUTH_MODE_JWT,
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: "body",
|
||||||
|
locationEnvironment: "body",
|
||||||
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: true
|
||||||
|
}),
|
||||||
|
secretsController.deleteSecretByNameRaw
|
||||||
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
query("workspaceId").exists().isString().trim(),
|
query("workspaceId").exists().isString().trim(),
|
||||||
@@ -40,6 +176,7 @@ router.get(
|
|||||||
locationEnvironment: "query",
|
locationEnvironment: "query",
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
requireBlindIndicesEnabled: true,
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: false
|
||||||
}),
|
}),
|
||||||
secretsController.getSecrets
|
secretsController.getSecrets
|
||||||
);
|
);
|
||||||
@@ -74,6 +211,7 @@ router.post(
|
|||||||
locationEnvironment: "body",
|
locationEnvironment: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
requireBlindIndicesEnabled: true,
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: false
|
||||||
}),
|
}),
|
||||||
secretsController.createSecret
|
secretsController.createSecret
|
||||||
);
|
);
|
||||||
@@ -129,6 +267,7 @@ router.patch(
|
|||||||
locationEnvironment: "body",
|
locationEnvironment: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
requireBlindIndicesEnabled: true,
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: false
|
||||||
}),
|
}),
|
||||||
secretsController.updateSecretByName
|
secretsController.updateSecretByName
|
||||||
);
|
);
|
||||||
@@ -155,6 +294,7 @@ router.delete(
|
|||||||
locationEnvironment: "body",
|
locationEnvironment: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
requireBlindIndicesEnabled: true,
|
requireBlindIndicesEnabled: true,
|
||||||
|
requireE2EEOff: false
|
||||||
}),
|
}),
|
||||||
secretsController.deleteSecretByName
|
secretsController.deleteSecretByName
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { validateServiceAccountClientForWorkspace } from './serviceAccount';
|
|||||||
import { validateUserClientForWorkspace } from './user';
|
import { validateUserClientForWorkspace } from './user';
|
||||||
import { validateServiceTokenDataClientForWorkspace } from './serviceTokenData';
|
import { validateServiceTokenDataClientForWorkspace } from './serviceTokenData';
|
||||||
import {
|
import {
|
||||||
|
BadRequestError,
|
||||||
UnauthorizedRequestError,
|
UnauthorizedRequestError,
|
||||||
WorkspaceNotFoundError
|
WorkspaceNotFoundError
|
||||||
} from '../utils/errors';
|
} from '../utils/errors';
|
||||||
@@ -22,6 +23,7 @@ import {
|
|||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
AUTH_MODE_API_KEY
|
AUTH_MODE_API_KEY
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
|
import { BotService } from '../services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for workspace with id [workspaceId] based
|
* Validate authenticated clients for workspace with id [workspaceId] based
|
||||||
@@ -39,7 +41,8 @@ export const validateClientForWorkspace = async ({
|
|||||||
environment,
|
environment,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions,
|
||||||
requireBlindIndicesEnabled
|
requireBlindIndicesEnabled,
|
||||||
|
requireE2EEOff
|
||||||
}: {
|
}: {
|
||||||
authData: {
|
authData: {
|
||||||
authMode: string;
|
authMode: string;
|
||||||
@@ -50,6 +53,7 @@ export const validateClientForWorkspace = async ({
|
|||||||
acceptedRoles: Array<'admin' | 'member'>;
|
acceptedRoles: Array<'admin' | 'member'>;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
requireBlindIndicesEnabled: boolean;
|
requireBlindIndicesEnabled: boolean;
|
||||||
|
requireE2EEOff: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const workspace = await Workspace.findById(workspaceId);
|
const workspace = await Workspace.findById(workspaceId);
|
||||||
|
|
||||||
@@ -70,6 +74,14 @@ export const validateClientForWorkspace = async ({
|
|||||||
message: 'Failed workspace authorization due to blind indices not being enabled'
|
message: 'Failed workspace authorization due to blind indices not being enabled'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requireE2EEOff) {
|
||||||
|
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
|
||||||
|
|
||||||
|
if (isWorkspaceE2EE) throw BadRequestError({
|
||||||
|
message: 'Failed workspace authorization due to end-to-end encryption not being disabled'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
|
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
|
||||||
const membership = await validateUserClientForWorkspace({
|
const membership = await validateUserClientForWorkspace({
|
||||||
|
|||||||
Reference in New Issue
Block a user