feat: made raw secret endpoints and normal e2ee ones to be same functionality

This commit is contained in:
=
2024-07-30 23:01:12 +05:30
parent 2cbae96c9a
commit 547be80dcf
9 changed files with 314 additions and 70 deletions
+7 -2
View File
@@ -608,7 +608,9 @@ export const RAW_SECRETS = {
skipMultilineEncoding: "Skip multiline encoding for the secret value.", skipMultilineEncoding: "Skip multiline encoding for the secret value.",
type: "The type of the secret to create.", type: "The type of the secret to create.",
workspaceId: "The ID of the project to create the secret in.", workspaceId: "The ID of the project to create the secret in.",
tagIds: "The ID of the tags to be attached to the created secret." tagIds: "The ID of the tags to be attached to the created secret.",
secretReminderRepeatDays: "Interval for secret rotation notifications, measured in days",
secretReminderNote: "Note to be attached in notification email"
}, },
GET: { GET: {
expand: "Whether or not to expand secret references", expand: "Whether or not to expand secret references",
@@ -631,7 +633,10 @@ export const RAW_SECRETS = {
type: "The type of the secret to update.", type: "The type of the secret to update.",
projectSlug: "The slug of the project to update the secret in.", projectSlug: "The slug of the project to update the secret in.",
workspaceId: "The ID of the project to update the secret in.", workspaceId: "The ID of the project to update the secret in.",
tagIds: "The ID of the tags to be attached to the updated secret." tagIds: "The ID of the tags to be attached to the updated secret.",
secretReminderRepeatDays: "Interval for secret rotation notifications, measured in days",
secretReminderNote: "Note to be attached in notification email",
newSecretName: "The new name for the secret"
}, },
DELETE: { DELETE: {
secretName: "The name of the secret to delete.", secretName: "The name of the secret to delete.",
+1
View File
@@ -703,6 +703,7 @@ export const registerRoutes = async (
}); });
const secretImportService = secretImportServiceFactory({ const secretImportService = secretImportServiceFactory({
licenseService, licenseService,
projectBotService,
projectEnvDAL, projectEnvDAL,
folderDAL, folderDAL,
permissionService, permissionService,
@@ -63,7 +63,13 @@ export const secretRawSchema = z.object({
type: z.string(), type: z.string(),
secretKey: z.string(), secretKey: z.string(),
secretValue: z.string(), secretValue: z.string(),
secretComment: z.string().optional() secretComment: z.string().optional(),
secretReminderNote: z.string().nullable().optional(),
secretReminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
metadata: z.unknown().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
}); });
export const ProjectPermissionSchema = z.object({ export const ProjectPermissionSchema = z.object({
@@ -8,6 +8,8 @@ import { readLimit, secretsLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { secretRawSchema } from "../sanitizedSchemas";
export const registerSecretImportRouter = async (server: FastifyZodProvider) => { export const registerSecretImportRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
@@ -353,4 +355,48 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
return { secrets: importedSecrets }; return { secrets: importedSecrets };
} }
}); });
server.route({
url: "/secrets/raw",
method: "GET",
config: {
rateLimit: secretsLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
}),
response: {
200: z.object({
secrets: z
.object({
secretPath: z.string(),
environment: z.string(),
environmentInfo: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
}),
folderId: z.string().optional(),
secrets: secretRawSchema.array()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const importedSecrets = await server.services.secretImport.getRawSecretsFromImports({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
});
return { secrets: importedSecrets };
}
});
}; };
+59 -12
View File
@@ -186,7 +186,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
200: z.object({ 200: z.object({
secrets: secretRawSchema secrets: secretRawSchema
.extend({ .extend({
secretPath: z.string().optional() secretPath: z.string().optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
name: true,
color: true
})
.array()
.optional()
}) })
.array(), .array(),
imports: z imports: z
@@ -194,7 +202,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretPath: z.string(), secretPath: z.string(),
environment: z.string(), environment: z.string(),
folderId: z.string().optional(), folderId: z.string().optional(),
secrets: secretRawSchema.array() secrets: secretRawSchema.omit({ createdAt: true, updatedAt: true }).array()
}) })
.array() .array()
.optional() .optional()
@@ -425,7 +433,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment), secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds), tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding), skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type) type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
secretReminderRepeatDays: z
.number()
.optional()
.nullable()
.describe(RAW_SECRETS.CREATE.secretReminderRepeatDays),
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.CREATE.secretReminderNote)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -448,7 +462,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretValue: req.body.secretValue, secretValue: req.body.secretValue,
skipMultilineEncoding: req.body.skipMultilineEncoding, skipMultilineEncoding: req.body.skipMultilineEncoding,
secretComment: req.body.secretComment, secretComment: req.body.secretComment,
tagIds: req.body.tagIds tagIds: req.body.tagIds,
secretReminderNote: req.body.secretReminderNote,
secretReminderRepeatDays: req.body.secretReminderRepeatDays
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -514,7 +530,16 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
.describe(RAW_SECRETS.UPDATE.secretPath), .describe(RAW_SECRETS.UPDATE.secretPath),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding), skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type), type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds) tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
metadata: z.record(z.string()).optional(),
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
secretReminderRepeatDays: z
.number()
.optional()
.nullable()
.describe(RAW_SECRETS.UPDATE.secretReminderRepeatDays),
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
secretComment: z.string().optional().describe(RAW_SECRETS.UPDATE.secretComment)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -536,7 +561,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
type: req.body.type, type: req.body.type,
secretValue: req.body.secretValue, secretValue: req.body.secretValue,
skipMultilineEncoding: req.body.skipMultilineEncoding, skipMultilineEncoding: req.body.skipMultilineEncoding,
tagIds: req.body.tagIds tagIds: req.body.tagIds,
secretReminderRepeatDays: req.body.secretReminderRepeatDays,
secretReminderNote: req.body.secretReminderNote,
metadata: req.body.metadata,
newSecretName: req.body.newSecretName,
secretComment: req.body.secretComment
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -1760,7 +1790,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
} }
], ],
body: z.object({ body: z.object({
projectSlug: z.string().trim().describe(RAW_SECRETS.CREATE.projectSlug), projectSlug: z.string().trim().optional().describe(RAW_SECRETS.UPDATE.projectSlug),
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.UPDATE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.CREATE.environment), environment: z.string().trim().describe(RAW_SECRETS.CREATE.environment),
secretPath: z secretPath: z
.string() .string()
@@ -1776,7 +1807,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())) .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
.describe(RAW_SECRETS.CREATE.secretValue), .describe(RAW_SECRETS.CREATE.secretValue),
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment), secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding) skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
metadata: z.record(z.string()).optional(),
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
}) })
.array() .array()
.min(1) .min(1)
@@ -1799,6 +1832,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretPath, secretPath,
environment, environment,
projectSlug, projectSlug,
projectId: req.body.workspaceId,
secrets: inputSecrets secrets: inputSecrets
}); });
@@ -1849,7 +1883,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
} }
], ],
body: z.object({ body: z.object({
projectSlug: z.string().trim().describe(RAW_SECRETS.UPDATE.projectSlug), projectSlug: z.string().trim().optional().describe(RAW_SECRETS.DELETE.projectSlug),
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.DELETE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.UPDATE.environment), environment: z.string().trim().describe(RAW_SECRETS.UPDATE.environment),
secretPath: z secretPath: z
.string() .string()
@@ -1865,7 +1900,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())) .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
.describe(RAW_SECRETS.UPDATE.secretValue), .describe(RAW_SECRETS.UPDATE.secretValue),
secretComment: z.string().trim().optional().describe(RAW_SECRETS.UPDATE.secretComment), secretComment: z.string().trim().optional().describe(RAW_SECRETS.UPDATE.secretComment),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding) skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding),
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
secretReminderRepeatDays: z
.number()
.optional()
.nullable()
.describe(RAW_SECRETS.UPDATE.secretReminderRepeatDays)
}) })
.array() .array()
.min(1) .min(1)
@@ -1887,6 +1930,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretPath, secretPath,
environment, environment,
projectSlug, projectSlug,
projectId: req.body.workspaceId,
secrets: inputSecrets secrets: inputSecrets
}); });
@@ -1937,7 +1981,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
} }
], ],
body: z.object({ body: z.object({
projectSlug: z.string().trim().describe(RAW_SECRETS.DELETE.projectSlug), projectSlug: z.string().trim().optional().describe(RAW_SECRETS.DELETE.projectSlug),
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.DELETE.workspaceId),
environment: z.string().trim().describe(RAW_SECRETS.DELETE.environment), environment: z.string().trim().describe(RAW_SECRETS.DELETE.environment),
secretPath: z secretPath: z
.string() .string()
@@ -1947,7 +1992,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
.describe(RAW_SECRETS.DELETE.secretPath), .describe(RAW_SECRETS.DELETE.secretPath),
secrets: z secrets: z
.object({ .object({
secretKey: z.string().trim().describe(RAW_SECRETS.DELETE.secretName) secretKey: z.string().trim().describe(RAW_SECRETS.DELETE.secretName),
type: z.nativeEnum(SecretType).default(SecretType.Shared)
}) })
.array() .array()
.min(1) .min(1)
@@ -1969,6 +2015,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
environment, environment,
projectSlug, projectSlug,
secretPath, secretPath,
projectId: req.body.workspaceId,
secrets: inputSecrets secrets: inputSecrets
}); });
@@ -10,8 +10,10 @@ import { getReplicationFolderName } from "@app/ee/services/secret-replication/se
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretDALFactory } from "../secret/secret-dal";
import { decryptSecretRaw } from "../secret/secret-fns";
import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretQueueFactory } from "../secret/secret-queue";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "./secret-import-dal"; import { TSecretImportDALFactory } from "./secret-import-dal";
@@ -29,6 +31,7 @@ type TSecretImportServiceFactoryDep = {
secretImportDAL: TSecretImportDALFactory; secretImportDAL: TSecretImportDALFactory;
folderDAL: TSecretFolderDALFactory; folderDAL: TSecretFolderDALFactory;
secretDAL: Pick<TSecretDALFactory, "find">; secretDAL: Pick<TSecretDALFactory, "find">;
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">; projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
projectEnvDAL: TProjectEnvDALFactory; projectEnvDAL: TProjectEnvDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -48,7 +51,8 @@ export const secretImportServiceFactory = ({
projectDAL, projectDAL,
secretDAL, secretDAL,
secretQueueService, secretQueueService,
licenseService licenseService,
projectBotService
}: TSecretImportServiceFactoryDep) => { }: TSecretImportServiceFactoryDep) => {
const createImport = async ({ const createImport = async ({
environment, environment,
@@ -449,12 +453,61 @@ export const secretImportServiceFactory = ({
return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL }); return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL });
}; };
const getRawSecretsFromImports = async ({
path: secretPath,
environment,
projectId,
actor,
actorAuthMethod,
actorId,
actorOrgId
}: TGetSecretsFromImportDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
);
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder) return [];
// this will already order by position
// so anything based on this order will also be in right position
const secretImports = await secretImportDAL.find({ folderId: folder.id, isReplication: false });
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {
environment: importEnv.slug,
secretPath: importPath
})
)
);
const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
const importedSecrets = await fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL });
return importedSecrets.map((el) => ({
...el,
secrets: el.secrets.map((encryptedSecret) =>
decryptSecretRaw({ ...encryptedSecret, workspace: projectId, environment, secretPath }, botKey)
)
}));
};
return { return {
createImport, createImport,
updateImport, updateImport,
deleteImport, deleteImport,
getImports, getImports,
getSecretsFromImports, getSecretsFromImports,
getRawSecretsFromImports,
resyncSecretImportReplication, resyncSecretImportReplication,
fnSecretsFromImports fnSecretsFromImports
}; };
+6 -1
View File
@@ -407,7 +407,12 @@ export const decryptSecretRaw = (
id: secret.id, id: secret.id,
user: secret.userId, user: secret.userId,
tags: secret.tags, tags: secret.tags,
skipMultilineEncoding: secret.skipMultilineEncoding skipMultilineEncoding: secret.skipMultilineEncoding,
secretReminderRepeatDays: secret.secretReminderRepeatDays,
secretReminderNote: secret.secretReminderNote,
metadata: secret.metadata,
createdAt: secret.createdAt,
updatedAt: secret.updatedAt
}; };
}; };
+111 -49
View File
@@ -1176,7 +1176,9 @@ export const secretServiceFactory = ({
secretValue, secretValue,
secretComment, secretComment,
skipMultilineEncoding, skipMultilineEncoding,
tagIds tagIds,
secretReminderNote,
secretReminderRepeatDays
}: TCreateSecretRawDTO) => { }: TCreateSecretRawDTO) => {
const botKey = await projectBotService.getBotKey(projectId); const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
@@ -1205,6 +1207,8 @@ export const secretServiceFactory = ({
secretCommentIV: secretCommentEncrypted.iv, secretCommentIV: secretCommentEncrypted.iv,
secretCommentTag: secretCommentEncrypted.tag, secretCommentTag: secretCommentEncrypted.tag,
skipMultilineEncoding, skipMultilineEncoding,
secretReminderRepeatDays,
secretReminderNote,
tags: tagIds tags: tagIds
}); });
@@ -1223,12 +1227,19 @@ export const secretServiceFactory = ({
secretPath, secretPath,
secretValue, secretValue,
skipMultilineEncoding, skipMultilineEncoding,
tagIds tagIds,
secretReminderNote,
secretReminderRepeatDays,
metadata,
secretComment,
newSecretName
}: TUpdateSecretRawDTO) => { }: TUpdateSecretRawDTO) => {
const botKey = await projectBotService.getBotKey(projectId); const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(newSecretName || secretName, botKey);
const secret = await updateSecret({ const secret = await updateSecret({
secretName, secretName,
@@ -1244,7 +1255,17 @@ export const secretServiceFactory = ({
secretValueIV: secretValueEncrypted.iv, secretValueIV: secretValueEncrypted.iv,
secretValueTag: secretValueEncrypted.tag, secretValueTag: secretValueEncrypted.tag,
skipMultilineEncoding, skipMultilineEncoding,
tags: tagIds tags: tagIds,
metadata,
secretReminderRepeatDays,
secretReminderNote,
newSecretName,
secretKeyIV: secretKeyEncrypted.iv,
secretKeyTag: secretKeyEncrypted.tag,
secretKeyCiphertext: secretKeyEncrypted.ciphertext,
secretCommentIV: secretCommentEncrypted.iv,
secretCommentTag: secretCommentEncrypted.tag,
secretCommentCiphertext: secretCommentEncrypted.ciphertext
}); });
await snapshotService.performSnapshot(secret.folderId); await snapshotService.performSnapshot(secret.folderId);
@@ -1283,6 +1304,7 @@ export const secretServiceFactory = ({
const createManySecretsRaw = async ({ const createManySecretsRaw = async ({
actorId, actorId,
projectSlug, projectSlug,
projectId: optionalProjectId,
environment, environment,
actor, actor,
actorOrgId, actorOrgId,
@@ -1290,9 +1312,16 @@ export const secretServiceFactory = ({
secretPath, secretPath,
secrets: inputSecrets = [] secrets: inputSecrets = []
}: TCreateManySecretRawDTO) => { }: TCreateManySecretRawDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!projectSlug && !optionalProjectId)
if (!project) throw new BadRequestError({ message: "Project not found" }); throw new BadRequestError({ message: "Must provide either project slug or projectId" });
const projectId = project.id;
let projectId = optionalProjectId as string;
// pick either project slug or projectid
if (!optionalProjectId && projectSlug) {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
projectId = project.id;
}
const botKey = await projectBotService.getBotKey(projectId); const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
@@ -1305,24 +1334,28 @@ export const secretServiceFactory = ({
actorId, actorId,
actorOrgId, actorOrgId,
actorAuthMethod, actorAuthMethod,
secrets: inputSecrets.map(({ secretComment, secretKey, secretValue, skipMultilineEncoding }) => { secrets: inputSecrets.map(
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretKey, botKey); ({ secretComment, secretKey, metadata, tagIds, secretValue, skipMultilineEncoding }) => {
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretKey, botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
return { const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
secretName: secretKey, return {
skipMultilineEncoding, secretName: secretKey,
secretKeyCiphertext: secretKeyEncrypted.ciphertext, skipMultilineEncoding,
secretKeyIV: secretKeyEncrypted.iv, secretKeyCiphertext: secretKeyEncrypted.ciphertext,
secretKeyTag: secretKeyEncrypted.tag, secretKeyIV: secretKeyEncrypted.iv,
secretValueCiphertext: secretValueEncrypted.ciphertext, secretKeyTag: secretKeyEncrypted.tag,
secretValueIV: secretValueEncrypted.iv, secretValueCiphertext: secretValueEncrypted.ciphertext,
secretValueTag: secretValueEncrypted.tag, secretValueIV: secretValueEncrypted.iv,
secretCommentCiphertext: secretCommentEncrypted.ciphertext, secretValueTag: secretValueEncrypted.tag,
secretCommentIV: secretCommentEncrypted.iv, secretCommentCiphertext: secretCommentEncrypted.ciphertext,
secretCommentTag: secretCommentEncrypted.tag secretCommentIV: secretCommentEncrypted.iv,
}; secretCommentTag: secretCommentEncrypted.tag,
}) tags: tagIds,
metadata
};
}
)
}); });
return secrets.map((secret) => return secrets.map((secret) =>
@@ -1333,6 +1366,7 @@ export const secretServiceFactory = ({
const updateManySecretsRaw = async ({ const updateManySecretsRaw = async ({
actorId, actorId,
projectSlug, projectSlug,
projectId: optionalProjectId,
environment, environment,
actor, actor,
actorOrgId, actorOrgId,
@@ -1340,9 +1374,15 @@ export const secretServiceFactory = ({
secretPath, secretPath,
secrets: inputSecrets = [] secrets: inputSecrets = []
}: TUpdateManySecretRawDTO) => { }: TUpdateManySecretRawDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!projectSlug && !optionalProjectId)
if (!project) throw new BadRequestError({ message: "Project not found" }); throw new BadRequestError({ message: "Must provide either project slug or projectId" });
const projectId = project.id;
let projectId = optionalProjectId as string;
if (!optionalProjectId && projectSlug) {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
projectId = project.id;
}
const botKey = await projectBotService.getBotKey(projectId); const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
@@ -1355,25 +1395,40 @@ export const secretServiceFactory = ({
actorId, actorId,
actorOrgId, actorOrgId,
actorAuthMethod, actorAuthMethod,
secrets: inputSecrets.map(({ secretComment, secretKey, secretValue, skipMultilineEncoding }) => { secrets: inputSecrets.map(
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretKey, botKey); ({
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); secretComment,
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey); secretKey,
return { secretValue,
secretName: secretKey,
type: SecretType.Shared,
skipMultilineEncoding, skipMultilineEncoding,
secretKeyCiphertext: secretKeyEncrypted.ciphertext, tagIds: tags,
secretKeyIV: secretKeyEncrypted.iv, newSecretName,
secretKeyTag: secretKeyEncrypted.tag, secretReminderNote,
secretValueCiphertext: secretValueEncrypted.ciphertext, secretReminderRepeatDays
secretValueIV: secretValueEncrypted.iv, }) => {
secretValueTag: secretValueEncrypted.tag, const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(newSecretName || secretKey, botKey);
secretCommentCiphertext: secretCommentEncrypted.ciphertext, const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
secretCommentIV: secretCommentEncrypted.iv, const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
secretCommentTag: secretCommentEncrypted.tag return {
}; secretName: secretKey,
}) newSecretName,
tags,
secretReminderRepeatDays,
secretReminderNote,
type: SecretType.Shared,
skipMultilineEncoding,
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
};
}
)
}); });
return secrets.map((secret) => return secrets.map((secret) =>
@@ -1384,6 +1439,7 @@ export const secretServiceFactory = ({
const deleteManySecretsRaw = async ({ const deleteManySecretsRaw = async ({
actorId, actorId,
projectSlug, projectSlug,
projectId: optionalProjectId,
environment, environment,
actor, actor,
actorOrgId, actorOrgId,
@@ -1391,9 +1447,15 @@ export const secretServiceFactory = ({
secretPath, secretPath,
secrets: inputSecrets = [] secrets: inputSecrets = []
}: TDeleteManySecretRawDTO) => { }: TDeleteManySecretRawDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!projectSlug && !optionalProjectId)
if (!project) throw new BadRequestError({ message: "Project not found" }); throw new BadRequestError({ message: "Must provide either project slug or projectId" });
const projectId = project.id;
let projectId = optionalProjectId as string;
if (!optionalProjectId && projectSlug) {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
projectId = project.id;
}
const botKey = await projectBotService.getBotKey(projectId); const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
@@ -1406,7 +1468,7 @@ export const secretServiceFactory = ({
actorId, actorId,
actorOrgId, actorOrgId,
actorAuthMethod, actorAuthMethod,
secrets: inputSecrets.map(({ secretKey }) => ({ secretName: secretKey, type: SecretType.Shared })) secrets: inputSecrets.map(({ secretKey, type = SecretType.Shared }) => ({ secretName: secretKey, type }))
}); });
return secrets.map((secret) => return secrets.map((secret) =>
+23 -4
View File
@@ -160,14 +160,16 @@ export type TGetASecretRawDTO = {
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TCreateSecretRawDTO = TProjectPermission & { export type TCreateSecretRawDTO = TProjectPermission & {
secretName: string;
secretPath: string; secretPath: string;
environment: string; environment: string;
secretName: string;
secretValue: string; secretValue: string;
type: SecretType; type: SecretType;
tagIds?: string[]; tagIds?: string[];
secretComment?: string; secretComment?: string;
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
}; };
export type TUpdateSecretRawDTO = TProjectPermission & { export type TUpdateSecretRawDTO = TProjectPermission & {
@@ -175,11 +177,16 @@ export type TUpdateSecretRawDTO = TProjectPermission & {
environment: string; environment: string;
secretName: string; secretName: string;
secretValue?: string; secretValue?: string;
newSecretName?: string;
secretComment?: string;
type: SecretType; type: SecretType;
tagIds?: string[]; tagIds?: string[];
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
secretReminderRepeatDays?: number | null; secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null; secretReminderNote?: string | null;
metadata?: {
source?: string;
};
}; };
export type TDeleteSecretRawDTO = TProjectPermission & { export type TDeleteSecretRawDTO = TProjectPermission & {
@@ -191,34 +198,46 @@ export type TDeleteSecretRawDTO = TProjectPermission & {
export type TCreateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & { export type TCreateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
secretPath: string; secretPath: string;
projectSlug: string; projectId?: string;
projectSlug?: string;
environment: string; environment: string;
secrets: { secrets: {
secretKey: string; secretKey: string;
secretValue: string; secretValue: string;
secretComment?: string; secretComment?: string;
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
tagIds?: string[];
metadata?: {
source?: string;
};
}[]; }[];
}; };
export type TUpdateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & { export type TUpdateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
secretPath: string; secretPath: string;
projectSlug: string; projectId?: string;
projectSlug?: string;
environment: string; environment: string;
secrets: { secrets: {
secretKey: string; secretKey: string;
newSecretName?: string;
secretValue: string; secretValue: string;
secretComment?: string; secretComment?: string;
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
tagIds?: string[];
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
}[]; }[];
}; };
export type TDeleteManySecretRawDTO = Omit<TProjectPermission, "projectId"> & { export type TDeleteManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
secretPath: string; secretPath: string;
projectSlug: string; projectId?: string;
projectSlug?: string;
environment: string; environment: string;
secrets: { secrets: {
secretKey: string; secretKey: string;
type?: SecretType;
}[]; }[];
}; };