mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 21:28:18 +00:00
Draft refactor core secrets fn into reusable factories
This commit is contained in:
@@ -32,7 +32,10 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
|
|||||||
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||||
|
|
||||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||||
import { createManySecretsRawHelper, updateManySecretsRawHelper } from "../secret/secret-fns";
|
import {
|
||||||
|
createManySecretsRawHelper
|
||||||
|
// updateManySecretsRawHelper
|
||||||
|
} from "../secret/secret-fns";
|
||||||
import { Integrations, IntegrationSyncBehavior, IntegrationUrls } from "./integration-list";
|
import { Integrations, IntegrationSyncBehavior, IntegrationUrls } from "./integration-list";
|
||||||
|
|
||||||
const getSecretKeyValuePair = (secrets: Record<string, { value: string | null; comment?: string } | null>) =>
|
const getSecretKeyValuePair = (secrets: Record<string, { value: string | null; comment?: string } | null>) =>
|
||||||
@@ -589,7 +592,6 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to Heroku app named [integration.app]
|
* Sync/push [secrets] to Heroku app named [integration.app]
|
||||||
* server.services...
|
|
||||||
*/
|
*/
|
||||||
const syncSecretsHeroku = async ({
|
const syncSecretsHeroku = async ({
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -609,7 +611,7 @@ const syncSecretsHeroku = async ({
|
|||||||
accessToken
|
accessToken
|
||||||
}: {
|
}: {
|
||||||
projectDAL: TProjectDALFactory;
|
projectDAL: TProjectDALFactory;
|
||||||
integrationDAL: TIntegrationDALFactory;
|
integrationDAL: Pick<TIntegrationDALFactory, "updateById">;
|
||||||
secretDAL: TSecretDALFactory;
|
secretDAL: TSecretDALFactory;
|
||||||
secretVersionDAL: TSecretVersionDALFactory;
|
secretVersionDAL: TSecretVersionDALFactory;
|
||||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||||
@@ -696,27 +698,27 @@ const syncSecretsHeroku = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(secretsToUpdate).length) {
|
// if (Object.keys(secretsToUpdate).length) {
|
||||||
await updateManySecretsRawHelper({
|
// await updateManySecretsRawHelper({
|
||||||
projectId,
|
// projectId,
|
||||||
environment,
|
// environment,
|
||||||
path: secretPath,
|
// path: secretPath,
|
||||||
secrets: Object.keys(secretsToUpdate).map((key) => ({
|
// secrets: Object.keys(secretsToUpdate).map((key) => ({
|
||||||
secretName: key,
|
// secretName: key,
|
||||||
secretValue: secretsToUpdate[key],
|
// secretValue: secretsToUpdate[key],
|
||||||
type: SecretType.Shared,
|
// type: SecretType.Shared,
|
||||||
secretComment: ""
|
// secretComment: ""
|
||||||
})),
|
// })),
|
||||||
botKey, // TODO: consider getting botKey inside this fn
|
// botKey, // TODO: consider getting botKey inside this fn
|
||||||
projectDAL,
|
// projectDAL,
|
||||||
secretDAL,
|
// secretDAL,
|
||||||
secretVersionDAL,
|
// secretVersionDAL,
|
||||||
secretBlindIndexDAL,
|
// secretBlindIndexDAL,
|
||||||
secretTagDAL,
|
// secretTagDAL,
|
||||||
secretVersionTagDAL,
|
// secretVersionTagDAL,
|
||||||
folderDAL
|
// folderDAL
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
await request.patch(
|
await request.patch(
|
||||||
`${IntegrationUrls.HEROKU_API_URL}/apps/${integration.app}/config-vars`,
|
`${IntegrationUrls.HEROKU_API_URL}/apps/${integration.app}/config-vars`,
|
||||||
@@ -3071,7 +3073,7 @@ export const syncIntegrationSecrets = async ({
|
|||||||
appendices
|
appendices
|
||||||
}: {
|
}: {
|
||||||
projectDAL: TProjectDALFactory;
|
projectDAL: TProjectDALFactory;
|
||||||
integrationDAL: TIntegrationDALFactory;
|
integrationDAL: Pick<TIntegrationDALFactory, "updateById">;
|
||||||
secretDAL: TSecretDALFactory;
|
secretDAL: TSecretDALFactory;
|
||||||
secretVersionDAL: TSecretVersionDALFactory;
|
secretVersionDAL: TSecretVersionDALFactory;
|
||||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import {
|
|||||||
TFnSecretBlindIndexCheck,
|
TFnSecretBlindIndexCheck,
|
||||||
TFnSecretBulkInsert,
|
TFnSecretBulkInsert,
|
||||||
TFnSecretBulkUpdate,
|
TFnSecretBulkUpdate,
|
||||||
TUpdateManySecretsRawHelper
|
TUpdateManySecretsRawFn,
|
||||||
|
TUpdateManySecretsRawFnFactory
|
||||||
} from "./secret-types";
|
} from "./secret-types";
|
||||||
|
|
||||||
export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => {
|
export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => {
|
||||||
@@ -500,13 +501,15 @@ export const createManySecretsRawHelper = async ({
|
|||||||
return newSecrets;
|
return newSecrets;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateManySecretsRawHelper = async ({
|
// TOOD: potentially convert raw stuff
|
||||||
projectId,
|
|
||||||
environment,
|
// updateManySecretsRawFnFactory
|
||||||
path: secretPath,
|
|
||||||
secrets,
|
// updateManySecretsRawHelper
|
||||||
userId,
|
|
||||||
botKey, // TODO: consider getting botKey inside this fn
|
export const updateManySecretsRawFnFactory = async ({
|
||||||
|
// TODO: refactor
|
||||||
|
botKey,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
@@ -514,114 +517,127 @@ export const updateManySecretsRawHelper = async ({
|
|||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL
|
folderDAL
|
||||||
}: TUpdateManySecretsRawHelper) => {
|
}: TUpdateManySecretsRawFnFactory) => {
|
||||||
await projectDAL.checkProjectUpgradeStatus(projectId);
|
const updateManySecretsRawFn = async ({
|
||||||
|
projectId,
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
environment,
|
||||||
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Update secret" });
|
path: secretPath,
|
||||||
const folderId = folder.id;
|
secrets, // accept instead ciphertext secrets
|
||||||
|
|
||||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
|
||||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
|
||||||
|
|
||||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
|
||||||
inputSecrets: secrets,
|
|
||||||
folderId,
|
|
||||||
isNew: false,
|
|
||||||
blindIndexCfg,
|
|
||||||
secretDAL,
|
|
||||||
userId
|
userId
|
||||||
});
|
}: TUpdateManySecretsRawFn) => {
|
||||||
|
// TODO: fetch botKey from here
|
||||||
|
|
||||||
const inputSecrets = await Promise.all(
|
await projectDAL.checkProjectUpgradeStatus(projectId);
|
||||||
secrets.map(async (secret) => {
|
|
||||||
if (secret.newSecretName === "") {
|
|
||||||
throw new BadRequestError({ message: "New secret name cannot be empty" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretName, botKey);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretValue || "", botKey);
|
if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Update secret" });
|
||||||
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretComment || "", botKey);
|
const folderId = folder.id;
|
||||||
|
|
||||||
if (secret.type === SecretType.Personal) {
|
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||||
if (!userId) throw new BadRequestError({ message: "Missing user id for personal secret" });
|
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||||
|
|
||||||
const sharedExist = await secretDAL.findOne({
|
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||||
secretBlindIndex: keyName2BlindIndex[secret.secretName],
|
inputSecrets: secrets,
|
||||||
folderId,
|
folderId,
|
||||||
type: SecretType.Shared
|
isNew: false,
|
||||||
});
|
blindIndexCfg,
|
||||||
|
secretDAL,
|
||||||
|
userId
|
||||||
|
});
|
||||||
|
|
||||||
if (!sharedExist)
|
const inputSecrets = await Promise.all(
|
||||||
throw new BadRequestError({
|
secrets.map(async (secret) => {
|
||||||
message: "Failed to update personal secret override for no corresponding shared secret"
|
if (secret.newSecretName === "") {
|
||||||
|
throw new BadRequestError({ message: "New secret name cannot be empty" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretName, botKey);
|
||||||
|
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretValue || "", botKey);
|
||||||
|
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretComment || "", botKey);
|
||||||
|
|
||||||
|
if (secret.type === SecretType.Personal) {
|
||||||
|
if (!userId) throw new BadRequestError({ message: "Missing user id for personal secret" });
|
||||||
|
|
||||||
|
const sharedExist = await secretDAL.findOne({
|
||||||
|
secretBlindIndex: keyName2BlindIndex[secret.secretName],
|
||||||
|
folderId,
|
||||||
|
type: SecretType.Shared
|
||||||
});
|
});
|
||||||
|
|
||||||
if (secret.newSecretName) throw new BadRequestError({ message: "Personal secret cannot change the key name" });
|
if (!sharedExist)
|
||||||
}
|
throw new BadRequestError({
|
||||||
|
message: "Failed to update personal secret override for no corresponding shared secret"
|
||||||
|
});
|
||||||
|
|
||||||
const tags = secret.tags ? await secretTagDAL.findManyTagsById(projectId, secret.tags) : [];
|
if (secret.newSecretName)
|
||||||
if ((secret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
throw new BadRequestError({ message: "Personal secret cannot change the key name" });
|
||||||
|
|
||||||
return {
|
|
||||||
type: secret.type,
|
|
||||||
userId: secret.type === SecretType.Personal ? userId : null,
|
|
||||||
secretName: secret.secretName,
|
|
||||||
newSecretName: secret.newSecretName,
|
|
||||||
secretKeyCiphertext: secretKeyEncrypted.ciphertext,
|
|
||||||
secretKeyIV: secretKeyEncrypted.iv,
|
|
||||||
secretKeyTag: secretKeyEncrypted.tag,
|
|
||||||
secretValueCiphertext: secretValueEncrypted.ciphertext,
|
|
||||||
secretValueIV: secretValueEncrypted.iv,
|
|
||||||
secretValueTag: secretValueEncrypted.tag,
|
|
||||||
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
|
||||||
secretCommentIV: secretCommentEncrypted.iv,
|
|
||||||
secretCommentTag: secretCommentEncrypted.tag,
|
|
||||||
skipMultilineEncoding: secret.skipMultilineEncoding,
|
|
||||||
tags: secret.tags
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const tagIds = inputSecrets.flatMap(({ tags = [] }) => tags);
|
|
||||||
const tags = tagIds.length ? await secretTagDAL.findManyTagsById(projectId, tagIds) : [];
|
|
||||||
if (tagIds.length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
|
||||||
|
|
||||||
// now find any secret that needs to update its name
|
|
||||||
// same process as above
|
|
||||||
const nameUpdatedSecrets = inputSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
|
||||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
|
||||||
inputSecrets: nameUpdatedSecrets,
|
|
||||||
folderId,
|
|
||||||
isNew: true,
|
|
||||||
blindIndexCfg,
|
|
||||||
secretDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
|
||||||
fnSecretBulkUpdate({
|
|
||||||
folderId,
|
|
||||||
projectId,
|
|
||||||
tx,
|
|
||||||
inputSecrets: inputSecrets.map(({ secretName, newSecretName, ...el }) => ({
|
|
||||||
filter: { secretBlindIndex: keyName2BlindIndex[secretName], type: SecretType.Shared },
|
|
||||||
data: {
|
|
||||||
...el,
|
|
||||||
folderId,
|
|
||||||
secretBlindIndex:
|
|
||||||
newSecretName && newKeyName2BlindIndex[newSecretName]
|
|
||||||
? newKeyName2BlindIndex[newSecretName]
|
|
||||||
: keyName2BlindIndex[secretName],
|
|
||||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
|
||||||
keyEncoding: SecretKeyEncoding.UTF8
|
|
||||||
}
|
}
|
||||||
})),
|
|
||||||
secretDAL,
|
|
||||||
secretVersionDAL,
|
|
||||||
secretTagDAL,
|
|
||||||
secretVersionTagDAL
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return updatedSecrets;
|
const tags = secret.tags ? await secretTagDAL.findManyTagsById(projectId, secret.tags) : [];
|
||||||
|
if ((secret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: secret.type,
|
||||||
|
userId: secret.type === SecretType.Personal ? userId : null,
|
||||||
|
secretName: secret.secretName,
|
||||||
|
newSecretName: secret.newSecretName,
|
||||||
|
secretKeyCiphertext: secretKeyEncrypted.ciphertext,
|
||||||
|
secretKeyIV: secretKeyEncrypted.iv,
|
||||||
|
secretKeyTag: secretKeyEncrypted.tag,
|
||||||
|
secretValueCiphertext: secretValueEncrypted.ciphertext,
|
||||||
|
secretValueIV: secretValueEncrypted.iv,
|
||||||
|
secretValueTag: secretValueEncrypted.tag,
|
||||||
|
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
||||||
|
secretCommentIV: secretCommentEncrypted.iv,
|
||||||
|
secretCommentTag: secretCommentEncrypted.tag,
|
||||||
|
skipMultilineEncoding: secret.skipMultilineEncoding,
|
||||||
|
tags: secret.tags
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagIds = inputSecrets.flatMap(({ tags = [] }) => tags);
|
||||||
|
const tags = tagIds.length ? await secretTagDAL.findManyTagsById(projectId, tagIds) : [];
|
||||||
|
if (tagIds.length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
||||||
|
|
||||||
|
// now find any secret that needs to update its name
|
||||||
|
// same process as above
|
||||||
|
const nameUpdatedSecrets = inputSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
||||||
|
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||||
|
inputSecrets: nameUpdatedSecrets,
|
||||||
|
folderId,
|
||||||
|
isNew: true,
|
||||||
|
blindIndexCfg,
|
||||||
|
secretDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
||||||
|
fnSecretBulkUpdate({
|
||||||
|
folderId,
|
||||||
|
projectId,
|
||||||
|
tx,
|
||||||
|
inputSecrets: inputSecrets.map(({ secretName, newSecretName, ...el }) => ({
|
||||||
|
filter: { secretBlindIndex: keyName2BlindIndex[secretName], type: SecretType.Shared },
|
||||||
|
data: {
|
||||||
|
...el,
|
||||||
|
folderId,
|
||||||
|
secretBlindIndex:
|
||||||
|
newSecretName && newKeyName2BlindIndex[newSecretName]
|
||||||
|
? newKeyName2BlindIndex[newSecretName]
|
||||||
|
: keyName2BlindIndex[secretName],
|
||||||
|
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||||
|
keyEncoding: SecretKeyEncoding.UTF8
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
secretDAL,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedSecrets;
|
||||||
|
};
|
||||||
|
|
||||||
|
return updateManySecretsRawFn;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import { TSecretDALFactory } from "./secret-dal";
|
|||||||
import { interpolateSecrets } from "./secret-fns";
|
import { interpolateSecrets } from "./secret-fns";
|
||||||
import { TCreateSecretReminderDTO, THandleReminderDTO, TRemoveSecretReminderDTO } from "./secret-types";
|
import { TCreateSecretReminderDTO, THandleReminderDTO, TRemoveSecretReminderDTO } from "./secret-types";
|
||||||
|
|
||||||
|
// import { updateManySecretsRawFnFactory } from "@app/services/secret/secret-fns";
|
||||||
|
|
||||||
export type TSecretQueueFactory = ReturnType<typeof secretQueueFactory>;
|
export type TSecretQueueFactory = ReturnType<typeof secretQueueFactory>;
|
||||||
|
|
||||||
type TSecretQueueFactoryDep = {
|
type TSecretQueueFactoryDep = {
|
||||||
@@ -318,6 +320,17 @@ export const secretQueueFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const updateManySecretsRawFn = updateManySecretsRawFnFactory({
|
||||||
|
// botKey, // can move this out
|
||||||
|
// projectDAL,
|
||||||
|
// secretDAL,
|
||||||
|
// secretVersionDAL,
|
||||||
|
// secretBlindIndexDAL,
|
||||||
|
// secretTagDAL,
|
||||||
|
// secretVersionTagDAL,
|
||||||
|
// folderDAL
|
||||||
|
// });
|
||||||
|
|
||||||
await syncIntegrationSecrets({
|
await syncIntegrationSecrets({
|
||||||
projectDAL,
|
projectDAL,
|
||||||
integrationDAL,
|
integrationDAL,
|
||||||
@@ -328,7 +341,7 @@ export const secretQueueFactory = ({
|
|||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
botKey,
|
botKey,
|
||||||
projectId,
|
projectId, // service
|
||||||
environment,
|
environment,
|
||||||
secretPath,
|
secretPath,
|
||||||
integration,
|
integration,
|
||||||
|
|||||||
@@ -274,7 +274,18 @@ export type TCreateManySecretsRawHelper = {
|
|||||||
folderDAL: TSecretFolderDALFactory;
|
folderDAL: TSecretFolderDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateManySecretsRawHelper = {
|
export type TUpdateManySecretsRawFnFactory = {
|
||||||
|
botKey: string;
|
||||||
|
projectDAL: TProjectDALFactory;
|
||||||
|
secretDAL: TSecretDALFactory;
|
||||||
|
secretVersionDAL: TSecretVersionDALFactory;
|
||||||
|
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||||
|
secretTagDAL: TSecretTagDALFactory;
|
||||||
|
secretVersionTagDAL: TSecretVersionTagDALFactory;
|
||||||
|
folderDAL: TSecretFolderDALFactory;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TUpdateManySecretsRawFn = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
path: string;
|
path: string;
|
||||||
@@ -292,13 +303,5 @@ export type TUpdateManySecretsRawHelper = {
|
|||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
}[];
|
}[];
|
||||||
userId?: string; // only relevant for personal secret(s)
|
userId?: string;
|
||||||
botKey: string;
|
|
||||||
projectDAL: TProjectDALFactory;
|
|
||||||
secretDAL: TSecretDALFactory;
|
|
||||||
secretVersionDAL: TSecretVersionDALFactory;
|
|
||||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
|
||||||
secretTagDAL: TSecretTagDALFactory;
|
|
||||||
secretVersionTagDAL: TSecretVersionTagDALFactory;
|
|
||||||
folderDAL: TSecretFolderDALFactory;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user