From 70fe80414d69cd947f41273efe88fdefb62337d0 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 21 Mar 2024 20:21:58 +0530 Subject: [PATCH] feat(server): added some more validation and feedback for deletion etc --- .../20240318164718_dynamic-secret.ts | 8 +- .../src/db/schemas/dynamic-secret-leases.ts | 3 +- backend/src/db/schemas/dynamic-secrets.ts | 2 +- backend/src/server/routes/sanitizedSchemas.ts | 15 ++- .../routes/v1/dynamic-secret-lease-router.ts | 30 ++++- .../server/routes/v1/dynamic-secret-router.ts | 115 ++++++++++++++---- .../dynamic-secret-lease-queue.ts | 65 ++++++---- .../dynamic-secret-lease-service.ts | 2 +- .../dynamic-secret-lease-types.ts | 4 + .../dynamic-secret/dynamic-secret-service.ts | 59 ++++++++- .../dynamic-secret/dynamic-secret-types.ts | 20 ++- 11 files changed, 255 insertions(+), 68 deletions(-) diff --git a/backend/src/db/migrations/20240318164718_dynamic-secret.ts b/backend/src/db/migrations/20240318164718_dynamic-secret.ts index ea7186da4..78c97610a 100644 --- a/backend/src/db/migrations/20240318164718_dynamic-secret.ts +++ b/backend/src/db/migrations/20240318164718_dynamic-secret.ts @@ -19,9 +19,9 @@ export async function up(knex: Knex): Promise { t.string("algorithm").notNullable().defaultTo(SecretEncryptionAlgo.AES_256_GCM); t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8); t.uuid("folderId").notNullable(); - t.boolean("isDeleting").defaultTo(false); - // used for flag why delete is failing + // for background process communication t.string("status"); + t.string("statusDetails"); t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); t.unique(["slug", "folderId"]); t.timestamps(true, true, true); @@ -36,8 +36,10 @@ export async function up(knex: Knex): Promise { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.integer("version").notNullable(); t.string("externalEntityId").notNullable(); - t.string("maxTTL"); t.datetime("expireAt").notNullable(); + // for background process communication + t.string("status"); + t.string("statusDetails"); t.uuid("dynamicSecretId").notNullable(); t.foreign("dynamicSecretId").references("id").inTable(TableName.DynamicSecret).onDelete("CASCADE"); t.timestamps(true, true, true); diff --git a/backend/src/db/schemas/dynamic-secret-leases.ts b/backend/src/db/schemas/dynamic-secret-leases.ts index 95efdeb2b..8c16bcb55 100644 --- a/backend/src/db/schemas/dynamic-secret-leases.ts +++ b/backend/src/db/schemas/dynamic-secret-leases.ts @@ -11,8 +11,9 @@ export const DynamicSecretLeasesSchema = z.object({ id: z.string().uuid(), version: z.number(), externalEntityId: z.string(), - maxTTL: z.string().nullable().optional(), expireAt: z.date(), + status: z.string().nullable().optional(), + statusDetails: z.string().nullable().optional(), dynamicSecretId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date() diff --git a/backend/src/db/schemas/dynamic-secrets.ts b/backend/src/db/schemas/dynamic-secrets.ts index 81da953b3..f0cf005d6 100644 --- a/backend/src/db/schemas/dynamic-secrets.ts +++ b/backend/src/db/schemas/dynamic-secrets.ts @@ -20,8 +20,8 @@ export const DynamicSecretsSchema = z.object({ algorithm: z.string().default("aes-256-gcm"), keyEncoding: z.string().default("utf8"), folderId: z.string().uuid(), - isDeleting: z.boolean().default(false).nullable().optional(), status: z.string().nullable().optional(), + statusDetails: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index 03e48c247..eaae4149c 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -1,6 +1,11 @@ import { z } from "zod"; -import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema, UsersSchema } from "@app/db/schemas"; +import { + DynamicSecretsSchema, + IntegrationAuthsSchema, + SecretApprovalPoliciesSchema, + UsersSchema +} from "@app/db/schemas"; // sometimes the return data must be santizied to avoid leaking important values // always prefer pick over omit in zod @@ -56,3 +61,11 @@ export const secretRawSchema = z.object({ secretValue: z.string(), secretComment: z.string().optional() }); + +export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({ + inputIV: true, + inputTag: true, + inputCiphertext: true, + keyEncoding: true, + algorithm: true +}); diff --git a/backend/src/server/routes/v1/dynamic-secret-lease-router.ts b/backend/src/server/routes/v1/dynamic-secret-lease-router.ts index cf39bdd78..f9c30210e 100644 --- a/backend/src/server/routes/v1/dynamic-secret-lease-router.ts +++ b/backend/src/server/routes/v1/dynamic-secret-lease-router.ts @@ -2,6 +2,8 @@ import ms from "ms"; import { z } from "zod"; import { DynamicSecretLeasesSchema } from "@app/db/schemas"; +import { daysToMillisecond } from "@app/lib/dates"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -16,8 +18,15 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide ttl: z .string() .optional() - .refine((val) => typeof val === "undefined" || ms(val) > 0, "TTL must be a positive number"), - path: z.string().default("/"), + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { @@ -49,7 +58,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide }), body: z.object({ projectId: z.string(), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { @@ -80,8 +89,19 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide leaseId: z.string() }), body: z.object({ + ttl: z + .string() + .optional() + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), projectId: z.string(), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { @@ -113,7 +133,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide }), querystring: z.object({ projectId: z.string(), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { diff --git a/backend/src/server/routes/v1/dynamic-secret-router.ts b/backend/src/server/routes/v1/dynamic-secret-router.ts index 9c56f8543..5b30b3556 100644 --- a/backend/src/server/routes/v1/dynamic-secret-router.ts +++ b/backend/src/server/routes/v1/dynamic-secret-router.ts @@ -1,11 +1,14 @@ import ms from "ms"; import { z } from "zod"; -import { DynamicSecretsSchema } from "@app/db/schemas"; +import { daysToMillisecond } from "@app/lib/dates"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { DynamicSecretProviderSchema } from "@app/services/dynamic-secret/providers/models"; +import { SanitizedDynamicSecretSchema } from "../sanitizedSchemas"; + export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => { server.route({ url: "/", @@ -14,18 +17,32 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => body: z.object({ projectId: z.string(), provider: DynamicSecretProviderSchema, - defaultTTL: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number"), + defaultTTL: z.string().superRefine((val, ctx) => { + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), maxTTL: z .string() .optional() - .refine((val) => typeof val === "undefined" || ms(val) > 0, "Max TTL must be a positive number"), - path: z.string().default("/"), + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }) + .nullable(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string(), slug: z.string().toLowerCase() }), response: { 200: z.object({ - dynamicSecret: DynamicSecretsSchema + dynamicSecret: SanitizedDynamicSecretSchema }) } }, @@ -51,22 +68,39 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }), body: z.object({ projectId: z.string(), - inputs: z.any().optional(), - defaultTTL: z - .string() - .optional() - .refine((val) => typeof val === "undefined" || ms(val) > 0, "TTL must be a positive number"), - maxTTL: z - .string() - .optional() - .refine((val) => typeof val === "undefined" || ms(val) > 0, "Max TTL must be a positive number"), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string(), - newSlug: z.string() + data: z.object({ + inputs: z.any().optional(), + defaultTTL: z + .string() + .optional() + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), + maxTTL: z + .string() + .optional() + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + if (valMs > daysToMillisecond(1)) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }) + .nullable(), + newSlug: z.string().optional() + }) }), response: { 200: z.object({ - dynamicSecret: DynamicSecretsSchema + dynamicSecret: SanitizedDynamicSecretSchema }) } }, @@ -78,7 +112,10 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, slug: req.params.slug, - ...req.body + path: req.body.path, + projectId: req.body.projectId, + environment: req.body.environment, + ...req.body.data }); return { dynamicSecret: dynamicSecretCfg }; } @@ -93,12 +130,12 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }), body: z.object({ projectId: z.string(), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { 200: z.object({ - dynamicSecret: DynamicSecretsSchema + dynamicSecret: SanitizedDynamicSecretSchema }) } }, @@ -116,18 +153,52 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => } }); + server.route({ + url: "/:slug", + method: "GET", + schema: { + params: z.object({ + slug: z.string() + }), + querystring: z.object({ + projectId: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), + environment: z.string() + }), + response: { + 200: z.object({ + dynamicSecret: SanitizedDynamicSecretSchema.extend({ + inputs: z.unknown() + }) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const dynamicSecretCfg = await server.services.dynamicSecret.getDetails({ + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + slug: req.params.slug, + ...req.query + }); + return { dynamicSecret: dynamicSecretCfg }; + } + }); + server.route({ url: "/", method: "GET", schema: { querystring: z.object({ projectId: z.string(), - path: z.string(), + path: z.string().trim().default("/").transform(removeTrailingSlash), environment: z.string() }), response: { 200: z.object({ - dynamicSecrets: DynamicSecretsSchema.array() + dynamicSecrets: SanitizedDynamicSecretSchema.array() }) } }, diff --git a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index c5bb051fe..fe5d75a83 100644 --- a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -5,13 +5,14 @@ import { logger } from "@app/lib/logger"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal"; +import { DynamicSecretStatus } from "../dynamic-secret/dynamic-secret-types"; import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models"; import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal"; type TDynamicSecretLeaseQueueServiceFactoryDep = { queueService: TQueueServiceFactory; - dynamicSecretLeaseDAL: Pick; - dynamicSecretDAL: Pick; + dynamicSecretLeaseDAL: Pick; + dynamicSecretDAL: Pick; dynamicSecretProviders: Record; }; @@ -89,44 +90,60 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ logger.info("Dynamic secret pruning started: ", dynamicSecretCfgId, job.id); const dynamicSecretCfg = await dynamicSecretDAL.findById(dynamicSecretCfgId); if (!dynamicSecretCfg) throw new DisableRotationErrors({ message: "Dynamic secret not found" }); - if (!dynamicSecretCfg.isDeleting) throw new DisableRotationErrors({ message: "Document not deleted" }); + if ((dynamicSecretCfg.status as DynamicSecretStatus) !== DynamicSecretStatus.Deleting) + throw new DisableRotationErrors({ message: "Document not deleted" }); const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfgId }); - if (dynamicSecretLeases.length) throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); + if (dynamicSecretLeases.length) { + const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders]; + const decryptedStoredInput = JSON.parse( + infisicalSymmetricDecrypt({ + keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding, + ciphertext: dynamicSecretCfg.inputCiphertext, + tag: dynamicSecretCfg.inputTag, + iv: dynamicSecretCfg.inputIV + }) + ) as object; - const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders]; - const decryptedStoredInput = JSON.parse( - infisicalSymmetricDecrypt({ - keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding, - ciphertext: dynamicSecretCfg.inputCiphertext, - tag: dynamicSecretCfg.inputTag, - iv: dynamicSecretCfg.inputIV - }) - ) as object; - - await Promise.allSettled(dynamicSecretLeases.map(({ id }) => unsetLeaseRevocation(id))); - await Promise.allSettled( - dynamicSecretLeases.map(({ externalEntityId }) => - selectedProvider.revoke(decryptedStoredInput, externalEntityId) - ) - ); + await Promise.allSettled(dynamicSecretLeases.map(({ id }) => unsetLeaseRevocation(id))); + await Promise.allSettled( + dynamicSecretLeases.map(({ externalEntityId }) => + selectedProvider.revoke(decryptedStoredInput, externalEntityId) + ) + ); + } await dynamicSecretDAL.deleteById(dynamicSecretCfgId); } logger.info("Finished dynamic secret job", job.id); } catch (error) { + logger.error(error); + + if (job?.name === QueueJobs.DynamicSecretPruning) { + const { dynamicSecretCfgId } = job.data as { dynamicSecretCfgId: string }; + await dynamicSecretDAL.updateById(dynamicSecretCfgId, { + status: DynamicSecretStatus.FailedDeletion, + statusDetails: (error as Error)?.message?.slice(0, 255) + }); + } + + if (job?.name === QueueJobs.DynamicSecretRevocation) { + const { leaseId } = job.data as { leaseId: string }; + await dynamicSecretLeaseDAL.updateById(leaseId, { + status: DynamicSecretStatus.FailedDeletion, + statusDetails: (error as Error)?.message?.slice(0, 255) + }); + } if (error instanceof DisableRotationErrors) { if (job.id) { await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, job.id); } } + // propogate to next part + throw error; } }); - queueService.listen(QueueName.AuditLogPrune, "failed", (err) => { - logger.error(err, `${QueueName.AuditLogPrune}: log pruning failed`); - }); - return { pruneDynamicSecret, setLeaseRevocation, diff --git a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index 1aace52c1..fe9957fdd 100644 --- a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -156,7 +156,7 @@ export const dynamicSecretLeaseServiceFactory = ({ ); await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id); - await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, Number(new Date()) - Number(expireAt)); + await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, Number(expireAt) - Number(new Date())); const updatedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, { expireAt, externalEntityId: entityId diff --git a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-types.ts b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-types.ts index decc7e3d1..4326e0a76 100644 --- a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-types.ts +++ b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-types.ts @@ -1,5 +1,9 @@ import { TProjectPermission } from "@app/lib/types"; +export enum DynamicSecretLeaseStatus { + FailedDeletion = "Failed to delete" +} + export type TCreateDynamicSecretLeaseDTO = { slug: string; path: string; diff --git a/backend/src/services/dynamic-secret/dynamic-secret-service.ts b/backend/src/services/dynamic-secret/dynamic-secret-service.ts index c9ce0428e..38ac4c0a7 100644 --- a/backend/src/services/dynamic-secret/dynamic-secret-service.ts +++ b/backend/src/services/dynamic-secret/dynamic-secret-service.ts @@ -11,9 +11,11 @@ import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/ import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TDynamicSecretDALFactory } from "./dynamic-secret-dal"; import { + DynamicSecretStatus, TCreateDynamicSecretDTO, - TDeleteDyanmicSecretDTO, - TListDyanmicSecretsDTO, + TDeleteDynamicSecretDTO, + TDetailsDynamicSecretDTO, + TListDynamicSecretsDTO, TUpdateDynamicSecretDTO } from "./dynamic-secret-types"; import { DynamicSecretProviders, TDynamicProviderFns } from "./providers/models"; @@ -146,7 +148,9 @@ export const dynamicSecretServiceFactory = ({ keyEncoding: encryptedInput.encoding, maxTTL, defaultTTL, - slug: newSlug ?? slug + slug: newSlug ?? slug, + status: null, + statusDetails: null }); return updatedDynamicCfg; @@ -161,7 +165,7 @@ export const dynamicSecretServiceFactory = ({ slug, path, environment - }: TDeleteDyanmicSecretDTO) => { + }: TDeleteDynamicSecretDTO) => { const { permission } = await permissionService.getProjectPermission( actor, actorId, @@ -184,7 +188,9 @@ export const dynamicSecretServiceFactory = ({ // if leases exist we should flag it as deleting and then remove leases in background // then delete the main one if (leases.length) { - const updatedDynamicSecretCfg = await dynamicSecretDAL.updateById(dynamicSecretCfg.id, { isDeleting: true }); + const updatedDynamicSecretCfg = await dynamicSecretDAL.updateById(dynamicSecretCfg.id, { + status: DynamicSecretStatus.Deleting + }); await dynamicSecretQueueService.pruneDynamicSecret(updatedDynamicSecretCfg.id); return updatedDynamicSecretCfg; } @@ -193,6 +199,46 @@ export const dynamicSecretServiceFactory = ({ return deletedDynamicSecretCfg; }; + const getDetails = async ({ + slug, + projectId, + path, + environment, + actorAuthMethod, + actorOrgId, + actorId, + actor + }: TDetailsDynamicSecretDTO) => { + const { permission } = await permissionService.getProjectPermission( + actor, + actorId, + projectId, + actorAuthMethod, + actorOrgId + ); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Edit, + subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + ); + + const folder = await folderDAL.findBySecretPath(projectId, environment, path); + if (!folder) throw new BadRequestError({ message: "Folder not found" }); + + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); + const decryptedStoredInput = JSON.parse( + infisicalSymmetricDecrypt({ + keyEncoding: dynamicSecretCfg.keyEncoding as SecretKeyEncoding, + ciphertext: dynamicSecretCfg.inputCiphertext, + tag: dynamicSecretCfg.inputTag, + iv: dynamicSecretCfg.inputIV + }) + ) as object; + const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders]; + const providerInputs = (await selectedProvider.validateProviderInputs(decryptedStoredInput)) as object; + return { ...dynamicSecretCfg, inputs: providerInputs }; + }; + const list = async ({ actorAuthMethod, actorOrgId, @@ -201,7 +247,7 @@ export const dynamicSecretServiceFactory = ({ projectId, path, environment - }: TListDyanmicSecretsDTO) => { + }: TListDynamicSecretsDTO) => { const { permission } = await permissionService.getProjectPermission( actor, actorId, @@ -225,6 +271,7 @@ export const dynamicSecretServiceFactory = ({ create, updateBySlug, deleteBySlug, + getDetails, list }; }; diff --git a/backend/src/services/dynamic-secret/dynamic-secret-types.ts b/backend/src/services/dynamic-secret/dynamic-secret-types.ts index 7ce31684a..1af9d293e 100644 --- a/backend/src/services/dynamic-secret/dynamic-secret-types.ts +++ b/backend/src/services/dynamic-secret/dynamic-secret-types.ts @@ -4,11 +4,17 @@ import { TProjectPermission } from "@app/lib/types"; import { DynamicSecretProviderSchema } from "./providers/models"; +// various status for dynamic secret that happens in background +export enum DynamicSecretStatus { + Deleting = "Revocation in process", + FailedDeletion = "Failed to delete" +} + type TProvider = z.infer; export type TCreateDynamicSecretDTO = { provider: TProvider; defaultTTL: string; - maxTTL?: string; + maxTTL?: string | null; path: string; environment: string; slug: string; @@ -18,19 +24,25 @@ export type TUpdateDynamicSecretDTO = { slug: string; newSlug?: string; defaultTTL?: string; - maxTTL?: string; + maxTTL?: string | null; path: string; environment: string; inputs?: TProvider["inputs"]; } & TProjectPermission; -export type TDeleteDyanmicSecretDTO = { +export type TDeleteDynamicSecretDTO = { slug: string; path: string; environment: string; } & TProjectPermission; -export type TListDyanmicSecretsDTO = { +export type TDetailsDynamicSecretDTO = { + slug: string; + path: string; + environment: string; +} & TProjectPermission; + +export type TListDynamicSecretsDTO = { path: string; environment: string; } & TProjectPermission;