From a7d2ec80c649f0c39fae050995038393948d88d1 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Tue, 26 Mar 2024 13:18:31 +0530 Subject: [PATCH] feat(server): updated dynamic secret names from feedback, added describe and fixed login not working --- .../20240318164718_dynamic-secret.ts | 4 +- backend/src/db/schemas/dynamic-secrets.ts | 2 +- backend/src/lib/api-docs/constants.ts | 78 ++++++++++++ .../routes/v1/dynamic-secret-lease-router.ts | 55 ++++++--- .../server/routes/v1/dynamic-secret-router.ts | 111 +++++++++++------- .../dynamic-secret-lease-dal.ts | 4 +- .../dynamic-secret-lease-service.ts | 38 +++--- .../dynamic-secret-lease-types.ts | 14 +-- .../dynamic-secret/dynamic-secret-service.ts | 64 +++++----- .../dynamic-secret/dynamic-secret-types.ts | 20 ++-- .../dynamic-secret/providers/sql-database.ts | 8 +- 11 files changed, 259 insertions(+), 139 deletions(-) diff --git a/backend/src/db/migrations/20240318164718_dynamic-secret.ts b/backend/src/db/migrations/20240318164718_dynamic-secret.ts index 78c97610a..743744a03 100644 --- a/backend/src/db/migrations/20240318164718_dynamic-secret.ts +++ b/backend/src/db/migrations/20240318164718_dynamic-secret.ts @@ -8,7 +8,7 @@ export async function up(knex: Knex): Promise { if (!doesTableExist) { await knex.schema.createTable(TableName.DynamicSecret, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); - t.string("slug").notNullable(); + t.string("name").notNullable(); t.integer("version").notNullable(); t.string("type").notNullable(); t.string("defaultTTL").notNullable(); @@ -23,7 +23,7 @@ export async function up(knex: Knex): Promise { t.string("status"); t.string("statusDetails"); t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); - t.unique(["slug", "folderId"]); + t.unique(["name", "folderId"]); t.timestamps(true, true, true); }); } diff --git a/backend/src/db/schemas/dynamic-secrets.ts b/backend/src/db/schemas/dynamic-secrets.ts index f0cf005d6..b27da396c 100644 --- a/backend/src/db/schemas/dynamic-secrets.ts +++ b/backend/src/db/schemas/dynamic-secrets.ts @@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models"; export const DynamicSecretsSchema = z.object({ id: z.string().uuid(), - slug: z.string(), + name: z.string(), version: z.number(), type: z.string(), defaultTTL: z.string(), diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 2e42f3388..0c115f55e 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -285,3 +285,81 @@ export const AUDIT_LOGS = { actor: "The actor to filter the audit logs by." } } as const; + +export const DYNAMIC_SECRETS = { + LIST: { + projectSlug: "The slug of the project to create dynamic secret in.", + environmentSlug: "The slug of the environment to list folders from.", + path: "The path to list folders from." + }, + LIST_LEAES_BY_NAME: { + projectSlug: "The slug of the project to create dynamic secret in.", + environmentSlug: "The slug of the environment to list folders from.", + path: "The path to list folders from.", + name: "The name of the dynamic secret." + }, + GET_BY_NAME: { + projectSlug: "The slug of the project to create dynamic secret in.", + environmentSlug: "The slug of the environment to list folders from.", + path: "The path to list folders from.", + name: "The name of the dynamic secret." + }, + CREATE: { + projectSlug: "The slug of the project to create dynamic secret in.", + environmentSlug: "The slug of the environment to create the dynamic secret in.", + path: "The path to create the dynamic secret in.", + name: "The name of the dynamic secret.", + provider: "The type of dynamic secret.", + defaultTTL: "The default TTL that will be applied for all the leases.", + maxTTL: "The maximum limit a TTL can be leases or renewed." + }, + UPDATE: { + projectSlug: "The slug of the project to update dynamic secret in.", + environmentSlug: "The slug of the environment to update the dynamic secret in.", + path: "The path to update the dynamic secret in.", + name: "The name of the dynamic secret.", + inputs: "The new partial values for the configurated provider of the dynamic secret", + defaultTTL: "The default TTL that will be applied for all the leases.", + maxTTL: "The maximum limit a TTL can be leases or renewed.", + newName: "The new name for the dynamic secret." + }, + DELETE: { + projectSlug: "The slug of the project to delete dynamic secret in.", + environmentSlug: "The slug of the environment to delete the dynamic secret in.", + path: "The path to delete the dynamic secret in.", + name: "The name of the dynamic secret.", + isForced: + "A boolean flag to delete the the dynamic secret from infisical without trying to remove it from external provider. Used when the dynamic secret got modified externally." + } +} as const; + +export const DYNAMIC_SECRET_LEASES = { + GET_BY_LEASEID: { + projectSlug: "The slug of the project to create dynamic secret in.", + environmentSlug: "The slug of the environment to list folders from.", + path: "The path to list folders from.", + leaseId: "The ID of the dynamic secret lease." + }, + CREATE: { + projectSlug: "The slug of the project of the dynamic secret in.", + environmentSlug: "The slug of the environment of the dynamic secret in.", + path: "The path of the dynamic secret in.", + dynamicSecretName: "The name of the dynamic secret.", + ttl: "The lease lifetime ttl. If not provided the default TTL of dynamic secret will be used." + }, + RENEW: { + projectSlug: "The slug of the project of the dynamic secret in.", + environmentSlug: "The slug of the environment of the dynamic secret in.", + path: "The path of the dynamic secret in.", + leaseId: "The ID of the dynamic secret lease.", + ttl: "The renew TTL that gets added with current expiry (ensure it's below max TTL) for a total less than creation time + max TTL." + }, + DELETE: { + projectSlug: "The slug of the project of the dynamic secret in.", + environmentSlug: "The slug of the environment of the dynamic secret in.", + path: "The path of the dynamic secret in.", + leaseId: "The ID of the dynamic secret lease.", + isForced: + "A boolean flag to delete the the dynamic secret from infisical without trying to remove it from external provider. Used when the dynamic secret got modified externally." + } +} as const; 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 530990b6f..b4f88e2ba 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,7 @@ import ms from "ms"; import { z } from "zod"; import { DynamicSecretLeasesSchema } from "@app/db/schemas"; +import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs"; import { daysToMillisecond } from "@app/lib/dates"; import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -15,11 +16,12 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide method: "POST", schema: { body: z.object({ - slug: z.string().min(1), - projectSlug: z.string().min(1), + dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(), + projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.projectSlug), ttl: z .string() .optional() + .describe(DYNAMIC_SECRET_LEASES.CREATE.ttl) .superRefine((val, ctx) => { if (!val) return; const valMs = ms(val); @@ -28,8 +30,8 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide 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().min(1) + path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRET_LEASES.CREATE.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.path) }), response: { 200: z.object({ @@ -46,6 +48,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, + name: req.body.dynamicSecretName, ...req.body }); return { lease, data, dynamicSecret }; @@ -57,13 +60,19 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide method: "DELETE", schema: { params: z.object({ - leaseId: z.string() + leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId) }), body: z.object({ - projectSlug: z.string().min(1), - path: z.string().min(1).trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1), - isForced: z.boolean().default(false) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.projectSlug), + path: z + .string() + .min(1) + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(DYNAMIC_SECRET_LEASES.DELETE.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.environmentSlug), + isForced: z.boolean().default(false).describe(DYNAMIC_SECRET_LEASES.DELETE.isForced) }), response: { 200: z.object({ @@ -90,11 +99,12 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide method: "POST", schema: { params: z.object({ - leaseId: z.string() + leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId) }), body: z.object({ ttl: z .string() + .describe(DYNAMIC_SECRET_LEASES.RENEW.ttl) .optional() .superRefine((val, ctx) => { if (!val) return; @@ -104,9 +114,15 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide if (valMs > daysToMillisecond(1)) ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); }), - projectSlug: z.string().min(1), - path: z.string().min(1).trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.projectSlug), + path: z + .string() + .min(1) + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(DYNAMIC_SECRET_LEASES.RENEW.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.ttl) }), response: { 200: z.object({ @@ -133,12 +149,17 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide method: "GET", schema: { params: z.object({ - leaseId: z.string() + leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId) }), querystring: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.projectSlug), + path: z + .string() + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.environmentSlug) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/dynamic-secret-router.ts b/backend/src/server/routes/v1/dynamic-secret-router.ts index c1a9564c6..85d8e8596 100644 --- a/backend/src/server/routes/v1/dynamic-secret-router.ts +++ b/backend/src/server/routes/v1/dynamic-secret-router.ts @@ -1,7 +1,9 @@ +import slugify from "@sindresorhus/slugify"; import ms from "ms"; import { z } from "zod"; import { DynamicSecretLeasesSchema } from "@app/db/schemas"; +import { DYNAMIC_SECRETS } from "@app/lib/api-docs"; import { daysToMillisecond } from "@app/lib/dates"; import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -16,17 +18,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => method: "POST", schema: { body: z.object({ - projectSlug: z.string().min(1), - provider: DynamicSecretProviderSchema, - 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" }); - }), + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug), + provider: DynamicSecretProviderSchema.describe(DYNAMIC_SECRETS.CREATE.provider), + defaultTTL: z + .string() + .describe(DYNAMIC_SECRETS.CREATE.defaultTTL) + .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() + .describe(DYNAMIC_SECRETS.CREATE.maxTTL) .optional() .superRefine((val, ctx) => { if (!val) return; @@ -37,9 +43,17 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => 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().min(1), - slug: z.string().min(1).toLowerCase() + path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash), + environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1), + name: z + .string() + .describe(DYNAMIC_SECRETS.CREATE.name) + .min(1) + .toLowerCase() + .max(64) + .refine((v) => slugify(v) === v, { + message: "Slug must be a valid" + }) }), response: { 200: z.object({ @@ -61,20 +75,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }); server.route({ - url: "/:slug", + url: "/:name", method: "PATCH", schema: { params: z.object({ - slug: z.string() + name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name) }), body: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1), + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.projectSlug), + path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.UPDATE.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.environmentSlug), data: z.object({ - inputs: z.any().optional(), + inputs: z.any().optional().describe(DYNAMIC_SECRETS.UPDATE.inputs), defaultTTL: z .string() + .describe(DYNAMIC_SECRETS.UPDATE.defaultTTL) .optional() .superRefine((val, ctx) => { if (!val) return; @@ -86,6 +101,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }), maxTTL: z .string() + .describe(DYNAMIC_SECRETS.UPDATE.maxTTL) .optional() .superRefine((val, ctx) => { if (!val) return; @@ -96,7 +112,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); }) .nullable(), - newSlug: z.string().optional() + newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional() }) }), response: { @@ -107,15 +123,15 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { - const dynamicSecretCfg = await server.services.dynamicSecret.updateBySlug({ + const dynamicSecretCfg = await server.services.dynamicSecret.updateByName({ actor: req.permission.type, actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - slug: req.params.slug, + name: req.params.name, path: req.body.path, projectSlug: req.body.projectSlug, - environment: req.body.environment, + environmentSlug: req.body.environmentSlug, ...req.body.data }); return { dynamicSecret: dynamicSecretCfg }; @@ -123,17 +139,17 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }); server.route({ - url: "/:slug", + url: "/:name", method: "DELETE", schema: { params: z.object({ - slug: z.string() + name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name) }), body: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1), - isForced: z.boolean().default(false) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.projectSlug), + path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.DELETE.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.environmentSlug), + isForced: z.boolean().default(false).describe(DYNAMIC_SECRETS.DELETE.isForced) }), response: { 200: z.object({ @@ -143,12 +159,12 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { - const dynamicSecretCfg = await server.services.dynamicSecret.deleteBySlug({ + const dynamicSecretCfg = await server.services.dynamicSecret.deleteByName({ actor: req.permission.type, actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - slug: req.params.slug, + name: req.params.name, ...req.body }); return { dynamicSecret: dynamicSecretCfg }; @@ -156,16 +172,16 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }); server.route({ - url: "/:slug", + url: "/:name", method: "GET", schema: { params: z.object({ - slug: z.string() + name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name) }), querystring: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.projectSlug), + path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.GET_BY_NAME.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.environmentSlug) }), response: { 200: z.object({ @@ -182,7 +198,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - slug: req.params.slug, + name: req.params.name, ...req.query }); return { dynamicSecret: dynamicSecretCfg }; @@ -194,9 +210,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => method: "GET", schema: { querystring: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug), + path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.LIST.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.environmentSlug) }), response: { 200: z.object({ @@ -218,16 +234,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => }); server.route({ - url: "/:slug/leases", + url: "/:name/leases", method: "GET", schema: { params: z.object({ - slug: z.string() + name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name) }), querystring: z.object({ - projectSlug: z.string().min(1), - path: z.string().trim().default("/").transform(removeTrailingSlash), - environment: z.string().min(1) + projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.projectSlug), + path: z + .string() + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.path), + environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.environmentSlug) }), response: { 200: z.object({ @@ -242,7 +263,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - slug: req.params.slug, + name: req.params.name, ...req.query }); return { leases }; diff --git a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-dal.ts b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-dal.ts index 70a6de5da..810628030 100644 --- a/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-dal.ts +++ b/backend/src/services/dynamic-secret-lease/dynamic-secret-lease-dal.ts @@ -32,7 +32,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => { .select(selectAllTableCols(TableName.DynamicSecretLease)) .select( db.ref("id").withSchema(TableName.DynamicSecret).as("dynId"), - db.ref("slug").withSchema(TableName.DynamicSecret).as("dynSlug"), + db.ref("name").withSchema(TableName.DynamicSecret).as("dynName"), db.ref("version").withSchema(TableName.DynamicSecret).as("dynVersion"), db.ref("type").withSchema(TableName.DynamicSecret).as("dynType"), db.ref("defaultTTL").withSchema(TableName.DynamicSecret).as("dynDefaultTTL"), @@ -54,7 +54,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => { ...DynamicSecretLeasesSchema.parse(doc), dynamicSecret: { id: doc.dynId, - slug: doc.dynSlug, + name: doc.dynName, version: doc.dynVersion, type: doc.dynType, defaultTTL: doc.dynDefaultTTL, 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 fa599cfc7..0d7e3c0f8 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 @@ -45,9 +45,9 @@ export const dynamicSecretLeaseServiceFactory = ({ projectDAL }: TDynamicSecretLeaseServiceFactoryDep) => { const create = async ({ - environment, + environmentSlug, path, - slug, + name, projectSlug, actor, actorId, @@ -69,13 +69,13 @@ export const dynamicSecretLeaseServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); const totalLeasesTaken = await dynamicSecretLeaseDAL.countLeasesForDynamicSecret(dynamicSecretCfg.id); @@ -119,7 +119,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actor, projectSlug, path, - environment, + environmentSlug, leaseId }: TRenewDynamicSecretLeaseDTO) => { const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); @@ -135,10 +135,10 @@ export const dynamicSecretLeaseServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); @@ -180,7 +180,7 @@ export const dynamicSecretLeaseServiceFactory = ({ const revokeLease = async ({ leaseId, - environment, + environmentSlug, path, projectSlug, actor, @@ -202,10 +202,10 @@ export const dynamicSecretLeaseServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); @@ -245,12 +245,12 @@ export const dynamicSecretLeaseServiceFactory = ({ const listLeases = async ({ path, - slug, + name, actor, actorId, projectSlug, actorOrgId, - environment, + environmentSlug, actorAuthMethod }: TListDynamicSecretLeasesDTO) => { const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); @@ -266,13 +266,13 @@ export const dynamicSecretLeaseServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id }); @@ -283,7 +283,7 @@ export const dynamicSecretLeaseServiceFactory = ({ projectSlug, actorOrgId, path, - environment, + environmentSlug, actor, actorId, leaseId, @@ -302,10 +302,10 @@ export const dynamicSecretLeaseServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); 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 81e4ed056..bf182b349 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 @@ -5,9 +5,9 @@ export enum DynamicSecretLeaseStatus { } export type TCreateDynamicSecretLeaseDTO = { - slug: string; + name: string; path: string; - environment: string; + environmentSlug: string; ttl?: string; projectSlug: string; } & Omit; @@ -15,21 +15,21 @@ export type TCreateDynamicSecretLeaseDTO = { export type TDetailsDynamicSecretLeaseDTO = { leaseId: string; path: string; - environment: string; + environmentSlug: string; projectSlug: string; } & Omit; export type TListDynamicSecretLeasesDTO = { - slug: string; + name: string; path: string; - environment: string; + environmentSlug: string; projectSlug: string; } & Omit; export type TDeleteDynamicSecretLeaseDTO = { leaseId: string; path: string; - environment: string; + environmentSlug: string; projectSlug: string; isForced?: boolean; } & Omit; @@ -37,7 +37,7 @@ export type TDeleteDynamicSecretLeaseDTO = { export type TRenewDynamicSecretLeaseDTO = { leaseId: string; path: string; - environment: string; + environmentSlug: string; ttl?: string; projectSlug: string; } & Omit; diff --git a/backend/src/services/dynamic-secret/dynamic-secret-service.ts b/backend/src/services/dynamic-secret/dynamic-secret-service.ts index 4b053bf3b..459512754 100644 --- a/backend/src/services/dynamic-secret/dynamic-secret-service.ts +++ b/backend/src/services/dynamic-secret/dynamic-secret-service.ts @@ -48,11 +48,11 @@ export const dynamicSecretServiceFactory = ({ const create = async ({ path, actor, - slug, + name, actorId, maxTTL, provider, - environment, + environmentSlug, projectSlug, actorOrgId, defaultTTL, @@ -71,13 +71,13 @@ export const dynamicSecretServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const existingDynamicSecret = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const existingDynamicSecret = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (existingDynamicSecret) throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" }); @@ -99,22 +99,22 @@ export const dynamicSecretServiceFactory = ({ maxTTL, defaultTTL, folderId: folder.id, - slug + name }); return dynamicSecretCfg; }; - const updateBySlug = async ({ - slug, + const updateByName = async ({ + name, maxTTL, defaultTTL, inputs, - environment, + environmentSlug, projectSlug, path, actor, actorId, - newSlug, + newName, actorOrgId, actorAuthMethod }: TUpdateDynamicSecretDTO) => { @@ -132,17 +132,17 @@ export const dynamicSecretServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); - if (newSlug) { - const existingDynamicSecret = await dynamicSecretDAL.findOne({ slug: newSlug, folderId: folder.id }); + if (newName) { + const existingDynamicSecret = await dynamicSecretDAL.findOne({ name: newName, folderId: folder.id }); if (existingDynamicSecret) throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" }); } @@ -171,7 +171,7 @@ export const dynamicSecretServiceFactory = ({ keyEncoding: encryptedInput.encoding, maxTTL, defaultTTL, - slug: newSlug ?? slug, + name: newName ?? name, status: null, statusDetails: null }); @@ -179,15 +179,15 @@ export const dynamicSecretServiceFactory = ({ return updatedDynamicCfg; }; - const deleteBySlug = async ({ + const deleteByName = async ({ actorAuthMethod, actorOrgId, actorId, actor, projectSlug, - slug, + name, path, - environment, + environmentSlug, isForced }: TDeleteDynamicSecretDTO) => { const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); @@ -204,13 +204,13 @@ export const dynamicSecretServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); const leases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id }); @@ -239,10 +239,10 @@ export const dynamicSecretServiceFactory = ({ }; const getDetails = async ({ - slug, + name, projectSlug, path, - environment, + environmentSlug, actorAuthMethod, actorOrgId, actorId, @@ -261,13 +261,13 @@ export const dynamicSecretServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const dynamicSecretCfg = await dynamicSecretDAL.findOne({ slug, folderId: folder.id }); + const dynamicSecretCfg = await dynamicSecretDAL.findOne({ name, folderId: folder.id }); if (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" }); const decryptedStoredInput = JSON.parse( infisicalSymmetricDecrypt({ @@ -289,7 +289,7 @@ export const dynamicSecretServiceFactory = ({ actor, projectSlug, path, - environment + environmentSlug }: TListDynamicSecretsDTO) => { const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!project) throw new BadRequestError({ message: "Project not found" }); @@ -304,10 +304,10 @@ export const dynamicSecretServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) + subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) ); - const folder = await folderDAL.findBySecretPath(projectId, environment, path); + const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); const dynamicSecretCfg = await dynamicSecretDAL.find({ folderId: folder.id }); @@ -316,8 +316,8 @@ export const dynamicSecretServiceFactory = ({ return { create, - updateBySlug, - deleteBySlug, + updateByName, + deleteByName, 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 5d5159974..02f2cbb86 100644 --- a/backend/src/services/dynamic-secret/dynamic-secret-types.ts +++ b/backend/src/services/dynamic-secret/dynamic-secret-types.ts @@ -16,39 +16,39 @@ export type TCreateDynamicSecretDTO = { defaultTTL: string; maxTTL?: string | null; path: string; - environment: string; - slug: string; + environmentSlug: string; + name: string; projectSlug: string; } & Omit; export type TUpdateDynamicSecretDTO = { - slug: string; - newSlug?: string; + name: string; + newName?: string; defaultTTL?: string; maxTTL?: string | null; path: string; - environment: string; + environmentSlug: string; inputs?: TProvider["inputs"]; projectSlug: string; } & Omit; export type TDeleteDynamicSecretDTO = { - slug: string; + name: string; path: string; - environment: string; + environmentSlug: string; projectSlug: string; isForced?: boolean; } & Omit; export type TDetailsDynamicSecretDTO = { - slug: string; + name: string; path: string; - environment: string; + environmentSlug: string; projectSlug: string; } & Omit; export type TListDynamicSecretsDTO = { path: string; - environment: string; + environmentSlug: string; projectSlug: string; } & Omit; diff --git a/backend/src/services/dynamic-secret/providers/sql-database.ts b/backend/src/services/dynamic-secret/providers/sql-database.ts index 076f00a9a..c0744031e 100644 --- a/backend/src/services/dynamic-secret/providers/sql-database.ts +++ b/backend/src/services/dynamic-secret/providers/sql-database.ts @@ -13,8 +13,8 @@ import { DynamicSecretSqlDBSchema, TDynamicProviderFns } from "./models"; const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000; const generatePassword = (size?: number) => { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*'$#"; - return customAlphabet(charset, 32)(size); + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#"; + return customAlphabet(charset, 48)(size); }; export const SqlDatabaseProvider = (): TDynamicProviderFns => { @@ -61,13 +61,13 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => { const providerInputs = await validateProviderInputs(inputs); const db = await getClient(providerInputs); - const username = alphaNumericNanoId(21); + const username = alphaNumericNanoId(32); const password = generatePassword(); const expiration = new Date(expireAt).toISOString(); const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ username, - password: "infisical", + password, expiration });