mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 22:28:15 +00:00
Add non-e2ee option for getSecret, getSecrets, start createSecret
This commit is contained in:
@@ -6,8 +6,6 @@ import {
|
|||||||
EventService
|
EventService
|
||||||
} from '../../services';
|
} from '../../services';
|
||||||
import { eventPushSecrets } from '../../events';
|
import { eventPushSecrets } from '../../events';
|
||||||
import { getAuthDataPayloadIdObj } from '../../utils/auth';
|
|
||||||
import { BadRequestError } from '../../utils/errors';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secrets for workspace with id [workspaceId] and environment
|
* Get secrets for workspace with id [workspaceId] and environment
|
||||||
@@ -68,9 +66,11 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
|
secretValue,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
|
secretComment,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag
|
||||||
@@ -85,14 +85,14 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
|
secretValue,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
...((secretCommentCiphertext && secretCommentIV && secretCommentTag) ? {
|
secretComment,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag
|
||||||
} : {})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
|
|||||||
@@ -86,6 +86,18 @@ export const createBot = async ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether or not workspace with id [workspaceId] is end-to-end encrypted
|
||||||
|
* @param {Types.ObjectId} workspaceId - id of workspace to check
|
||||||
|
*/
|
||||||
|
export const getIsWorkspaceE2EEHelper = async (workspaceId: Types.ObjectId) => {
|
||||||
|
const botKey = await BotKey.exists({
|
||||||
|
workspace: workspaceId
|
||||||
|
});
|
||||||
|
|
||||||
|
return botKey ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return decrypted secrets for workspace with id [workspaceId]
|
* Return decrypted secrets for workspace with id [workspaceId]
|
||||||
* and [environment] using bot
|
* and [environment] using bot
|
||||||
@@ -101,7 +113,7 @@ export const getSecretsBotHelper = async ({
|
|||||||
environment: string;
|
environment: string;
|
||||||
}) => {
|
}) => {
|
||||||
const content = {} as any;
|
const content = {} as any;
|
||||||
const key = await getKey({ workspaceId: workspaceId.toString() });
|
const key = await getKey({ workspaceId: workspaceId });
|
||||||
const secrets = await Secret.find({
|
const secrets = await Secret.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
@@ -136,7 +148,7 @@ export const getSecretsBotHelper = async ({
|
|||||||
* @param {String} obj.workspaceId - id of workspace
|
* @param {String} obj.workspaceId - id of workspace
|
||||||
* @returns {String} key - decrypted workspace key
|
* @returns {String} key - decrypted workspace key
|
||||||
*/
|
*/
|
||||||
export const getKey = async ({ workspaceId }: { workspaceId: string }) => {
|
export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => {
|
||||||
const encryptionKey = await getEncryptionKey();
|
const encryptionKey = await getEncryptionKey();
|
||||||
const rootEncryptionKey = await getRootEncryptionKey();
|
const rootEncryptionKey = await getRootEncryptionKey();
|
||||||
|
|
||||||
@@ -201,7 +213,7 @@ export const encryptSymmetricHelper = async ({
|
|||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
plaintext: string;
|
plaintext: string;
|
||||||
}) => {
|
}) => {
|
||||||
const key = await getKey({ workspaceId: workspaceId.toString() });
|
const key = await getKey({ workspaceId: workspaceId });
|
||||||
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
|
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext,
|
plaintext,
|
||||||
key,
|
key,
|
||||||
@@ -233,7 +245,7 @@ export const decryptSymmetricHelper = async ({
|
|||||||
iv: string;
|
iv: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
}) => {
|
}) => {
|
||||||
const key = await getKey({ workspaceId: workspaceId.toString() });
|
const key = await getKey({ workspaceId: workspaceId });
|
||||||
const plaintext = decryptSymmetric128BitHexKeyUTF8({
|
const plaintext = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext,
|
ciphertext,
|
||||||
iv,
|
iv,
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import {
|
|||||||
UpdateSecretParams,
|
UpdateSecretParams,
|
||||||
DeleteSecretParams,
|
DeleteSecretParams,
|
||||||
} from '../interfaces/services/SecretService';
|
} from '../interfaces/services/SecretService';
|
||||||
import { Secret, ISecret, SecretBlindIndexData } from '../models';
|
import {
|
||||||
|
ISecret,
|
||||||
|
Secret,
|
||||||
|
SecretBlindIndexData,
|
||||||
|
BotKey
|
||||||
|
} from '../models';
|
||||||
import { SecretVersion } from '../ee/models';
|
import { SecretVersion } from '../ee/models';
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
@@ -32,7 +37,7 @@ import {
|
|||||||
decryptSymmetric128BitHexKeyUTF8,
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
} from '../utils/crypto';
|
} from '../utils/crypto';
|
||||||
import { getEncryptionKey, client, getRootEncryptionKey } from '../config';
|
import { getEncryptionKey, client, getRootEncryptionKey } from '../config';
|
||||||
import { TelemetryService } from '../services';
|
import { BotService, TelemetryService } from '../services';
|
||||||
import { EESecretService, EELogService } from '../ee/services';
|
import { EESecretService, EELogService } from '../ee/services';
|
||||||
import {
|
import {
|
||||||
getAuthDataPayloadIdObj,
|
getAuthDataPayloadIdObj,
|
||||||
@@ -237,6 +242,23 @@ 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
|
||||||
@@ -256,14 +278,17 @@ export const createSecretHelper = async ({
|
|||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
|
secretValue,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
|
secretComment,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
folderId,
|
folderId,
|
||||||
}: CreateSecretParams) => {
|
}: CreateSecretParams) => {
|
||||||
|
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
@@ -298,6 +323,52 @@ 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,
|
||||||
@@ -410,7 +481,7 @@ export const getSecretsHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type: SECRET_PERSONAL,
|
type: SECRET_PERSONAL,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData),
|
||||||
});
|
}).lean();
|
||||||
|
|
||||||
// concat with shared secrets
|
// concat with shared secrets
|
||||||
secrets = secrets.concat(
|
secrets = secrets.concat(
|
||||||
@@ -421,7 +492,7 @@ export const getSecretsHelper = async ({
|
|||||||
secretBlindIndex: {
|
secretBlindIndex: {
|
||||||
$nin: secrets.map((secret) => secret.secretBlindIndex),
|
$nin: secrets.map((secret) => secret.secretBlindIndex),
|
||||||
},
|
},
|
||||||
})
|
}).lean()
|
||||||
);
|
);
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -458,8 +529,45 @@ 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
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -492,7 +600,7 @@ export const getSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type: type ?? SECRET_PERSONAL,
|
type: type ?? SECRET_PERSONAL,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
||||||
});
|
}).lean();
|
||||||
|
|
||||||
if (!secret) {
|
if (!secret) {
|
||||||
// case: failed to find personal secret matching criteria
|
// case: failed to find personal secret matching criteria
|
||||||
@@ -502,7 +610,7 @@ export const getSecretHelper = async ({
|
|||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED,
|
||||||
});
|
}).lean();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret) throw SecretNotFoundError();
|
if (!secret) throw SecretNotFoundError();
|
||||||
@@ -541,8 +649,36 @@ export const getSecretHelper = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
|
||||||
|
|
||||||
|
if (!isWorkspaceE2EE) {
|
||||||
|
// if workspace is not end-to-end encrypted, then decrypt
|
||||||
|
// secret and return it in plaintext
|
||||||
|
|
||||||
return secret;
|
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
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ export interface CreateSecretParams {
|
|||||||
folderId?: string;
|
folderId?: string;
|
||||||
type: "shared" | "personal";
|
type: "shared" | "personal";
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
secretKeyCiphertext: string;
|
secretKeyCiphertext?: string;
|
||||||
secretKeyIV: string;
|
secretKeyIV?: string;
|
||||||
secretKeyTag: string;
|
secretKeyTag?: string;
|
||||||
secretValueCiphertext: string;
|
secretValue?: string;
|
||||||
secretValueIV: string;
|
secretValueCiphertext?: string;
|
||||||
secretValueTag: string;
|
secretValueIV?: string;
|
||||||
|
secretValueTag?: string;
|
||||||
|
secretComment?: string;
|
||||||
secretCommentCiphertext?: string;
|
secretCommentCiphertext?: string;
|
||||||
secretCommentIV?: string;
|
secretCommentIV?: string;
|
||||||
secretCommentTag?: string;
|
secretCommentTag?: string;
|
||||||
|
|||||||
@@ -48,12 +48,14 @@ router.post(
|
|||||||
body('workspaceId').exists().isString().trim(),
|
body('workspaceId').exists().isString().trim(),
|
||||||
body('environment').exists().isString().trim(),
|
body('environment').exists().isString().trim(),
|
||||||
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
body('secretKeyCiphertext').exists().isString().trim(),
|
body('secretKeyCiphertext').optional().isString().trim(),
|
||||||
body('secretKeyIV').exists().isString().trim(),
|
body('secretKeyIV').optional().isString().trim(),
|
||||||
body('secretKeyTag').exists().isString().trim(),
|
body('secretKeyTag').optional().isString().trim(),
|
||||||
body('secretValueCiphertext').exists().isString().trim(),
|
body('secretValue').optional().isString().trim(),
|
||||||
body('secretValueIV').exists().isString().trim(),
|
body('secretValueCiphertext').optional().isString().trim(),
|
||||||
body('secretValueTag').exists().isString().trim(),
|
body('secretValueIV').optional().isString().trim(),
|
||||||
|
body('secretValueTag').optional().isString().trim(),
|
||||||
|
body('secretComment').optional().isString().trim(),
|
||||||
body('secretCommentCiphertext').optional().isString().trim(),
|
body('secretCommentCiphertext').optional().isString().trim(),
|
||||||
body('secretCommentIV').optional().isString().trim(),
|
body('secretCommentIV').optional().isString().trim(),
|
||||||
body('secretCommentTag').optional().isString().trim(),
|
body('secretCommentTag').optional().isString().trim(),
|
||||||
|
|||||||
@@ -2,13 +2,31 @@ import { Types } from 'mongoose';
|
|||||||
import {
|
import {
|
||||||
getSecretsBotHelper,
|
getSecretsBotHelper,
|
||||||
encryptSymmetricHelper,
|
encryptSymmetricHelper,
|
||||||
decryptSymmetricHelper
|
decryptSymmetricHelper,
|
||||||
|
getKey,
|
||||||
|
getIsWorkspaceE2EEHelper
|
||||||
} from '../helpers/bot';
|
} from '../helpers/bot';
|
||||||
|
|
||||||
|
// rename the functions here
|
||||||
|
// refactor the interface situation here
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle bot actions
|
* Class to handle bot actions
|
||||||
*/
|
*/
|
||||||
class BotService {
|
class BotService {
|
||||||
|
static async getIsWorkspaceE2EE(workspaceId: Types.ObjectId) {
|
||||||
|
return await getIsWorkspaceE2EEHelper(workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getWorkspaceKeyWithBot({
|
||||||
|
workspaceId
|
||||||
|
}: {
|
||||||
|
workspaceId: Types.ObjectId;
|
||||||
|
}) {
|
||||||
|
return await getKey({
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return decrypted secrets for workspace with id [workspaceId] and
|
* Return decrypted secrets for workspace with id [workspaceId] and
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
import {
|
|
||||||
ISecret
|
|
||||||
} from '../models';
|
|
||||||
import {
|
import {
|
||||||
CreateSecretParams,
|
CreateSecretParams,
|
||||||
GetSecretsParams,
|
GetSecretsParams,
|
||||||
|
|||||||
Reference in New Issue
Block a user