feat(server): updated dynamic secret names from feedback, added describe and fixed login not working

This commit is contained in:
Akhil Mohan
2024-03-26 13:18:31 +05:30
parent b592f4cb6d
commit a7d2ec80c6
11 changed files with 259 additions and 139 deletions
@@ -8,7 +8,7 @@ export async function up(knex: Knex): Promise<void> {
if (!doesTableExist) { if (!doesTableExist) {
await knex.schema.createTable(TableName.DynamicSecret, (t) => { await knex.schema.createTable(TableName.DynamicSecret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("slug").notNullable(); t.string("name").notNullable();
t.integer("version").notNullable(); t.integer("version").notNullable();
t.string("type").notNullable(); t.string("type").notNullable();
t.string("defaultTTL").notNullable(); t.string("defaultTTL").notNullable();
@@ -23,7 +23,7 @@ export async function up(knex: Knex): Promise<void> {
t.string("status"); t.string("status");
t.string("statusDetails"); t.string("statusDetails");
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.unique(["slug", "folderId"]); t.unique(["name", "folderId"]);
t.timestamps(true, true, true); t.timestamps(true, true, true);
}); });
} }
+1 -1
View File
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
export const DynamicSecretsSchema = z.object({ export const DynamicSecretsSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
slug: z.string(), name: z.string(),
version: z.number(), version: z.number(),
type: z.string(), type: z.string(),
defaultTTL: z.string(), defaultTTL: z.string(),
+78
View File
@@ -285,3 +285,81 @@ export const AUDIT_LOGS = {
actor: "The actor to filter the audit logs by." actor: "The actor to filter the audit logs by."
} }
} as const; } 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;
@@ -2,6 +2,7 @@ import ms from "ms";
import { z } from "zod"; import { z } from "zod";
import { DynamicSecretLeasesSchema } from "@app/db/schemas"; import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates"; import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -15,11 +16,12 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
method: "POST", method: "POST",
schema: { schema: {
body: z.object({ body: z.object({
slug: z.string().min(1), dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(),
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.projectSlug),
ttl: z ttl: z
.string() .string()
.optional() .optional()
.describe(DYNAMIC_SECRET_LEASES.CREATE.ttl)
.superRefine((val, ctx) => { .superRefine((val, ctx) => {
if (!val) return; if (!val) return;
const valMs = ms(val); const valMs = ms(val);
@@ -28,8 +30,8 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
if (valMs > daysToMillisecond(1)) if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}), }),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRET_LEASES.CREATE.path),
environment: z.string().min(1) environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.path)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -46,6 +48,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
name: req.body.dynamicSecretName,
...req.body ...req.body
}); });
return { lease, data, dynamicSecret }; return { lease, data, dynamicSecret };
@@ -57,13 +60,19 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
method: "DELETE", method: "DELETE",
schema: { schema: {
params: z.object({ params: z.object({
leaseId: z.string() leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId)
}), }),
body: z.object({ body: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.projectSlug),
path: z.string().min(1).trim().default("/").transform(removeTrailingSlash), path: z
environment: z.string().min(1), .string()
isForced: z.boolean().default(false) .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: { response: {
200: z.object({ 200: z.object({
@@ -90,11 +99,12 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
method: "POST", method: "POST",
schema: { schema: {
params: z.object({ params: z.object({
leaseId: z.string() leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId)
}), }),
body: z.object({ body: z.object({
ttl: z ttl: z
.string() .string()
.describe(DYNAMIC_SECRET_LEASES.RENEW.ttl)
.optional() .optional()
.superRefine((val, ctx) => { .superRefine((val, ctx) => {
if (!val) return; if (!val) return;
@@ -104,9 +114,15 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
if (valMs > daysToMillisecond(1)) if (valMs > daysToMillisecond(1))
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}), }),
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.projectSlug),
path: z.string().min(1).trim().default("/").transform(removeTrailingSlash), path: z
environment: z.string().min(1) .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: { response: {
200: z.object({ 200: z.object({
@@ -133,12 +149,17 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
method: "GET", method: "GET",
schema: { schema: {
params: z.object({ params: z.object({
leaseId: z.string() leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId)
}), }),
querystring: z.object({ querystring: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z
environment: z.string().min(1) .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: { response: {
200: z.object({ 200: z.object({
@@ -1,7 +1,9 @@
import slugify from "@sindresorhus/slugify";
import ms from "ms"; import ms from "ms";
import { z } from "zod"; import { z } from "zod";
import { DynamicSecretLeasesSchema } from "@app/db/schemas"; import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates"; import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -16,17 +18,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
method: "POST", method: "POST",
schema: { schema: {
body: z.object({ body: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug),
provider: DynamicSecretProviderSchema, provider: DynamicSecretProviderSchema.describe(DYNAMIC_SECRETS.CREATE.provider),
defaultTTL: z.string().superRefine((val, ctx) => { defaultTTL: z
const valMs = ms(val); .string()
if (valMs < 60 * 1000) .describe(DYNAMIC_SECRETS.CREATE.defaultTTL)
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); .superRefine((val, ctx) => {
if (valMs > daysToMillisecond(1)) const valMs = ms(val);
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); 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 maxTTL: z
.string() .string()
.describe(DYNAMIC_SECRETS.CREATE.maxTTL)
.optional() .optional()
.superRefine((val, ctx) => { .superRefine((val, ctx) => {
if (!val) return; 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" }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}) })
.nullable(), .nullable(),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
environment: z.string().min(1), environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
slug: z.string().min(1).toLowerCase() 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: { response: {
200: z.object({ 200: z.object({
@@ -61,20 +75,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}); });
server.route({ server.route({
url: "/:slug", url: "/:name",
method: "PATCH", method: "PATCH",
schema: { schema: {
params: z.object({ params: z.object({
slug: z.string() name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name)
}), }),
body: z.object({ body: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.UPDATE.path),
environment: z.string().min(1), environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.UPDATE.environmentSlug),
data: z.object({ data: z.object({
inputs: z.any().optional(), inputs: z.any().optional().describe(DYNAMIC_SECRETS.UPDATE.inputs),
defaultTTL: z defaultTTL: z
.string() .string()
.describe(DYNAMIC_SECRETS.UPDATE.defaultTTL)
.optional() .optional()
.superRefine((val, ctx) => { .superRefine((val, ctx) => {
if (!val) return; if (!val) return;
@@ -86,6 +101,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}), }),
maxTTL: z maxTTL: z
.string() .string()
.describe(DYNAMIC_SECRETS.UPDATE.maxTTL)
.optional() .optional()
.superRefine((val, ctx) => { .superRefine((val, ctx) => {
if (!val) return; 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" }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
}) })
.nullable(), .nullable(),
newSlug: z.string().optional() newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional()
}) })
}), }),
response: { response: {
@@ -107,15 +123,15 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.updateBySlug({ const dynamicSecretCfg = await server.services.dynamicSecret.updateByName({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
slug: req.params.slug, name: req.params.name,
path: req.body.path, path: req.body.path,
projectSlug: req.body.projectSlug, projectSlug: req.body.projectSlug,
environment: req.body.environment, environmentSlug: req.body.environmentSlug,
...req.body.data ...req.body.data
}); });
return { dynamicSecret: dynamicSecretCfg }; return { dynamicSecret: dynamicSecretCfg };
@@ -123,17 +139,17 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}); });
server.route({ server.route({
url: "/:slug", url: "/:name",
method: "DELETE", method: "DELETE",
schema: { schema: {
params: z.object({ params: z.object({
slug: z.string() name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name)
}), }),
body: z.object({ body: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.DELETE.path),
environment: z.string().min(1), environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.DELETE.environmentSlug),
isForced: z.boolean().default(false) isForced: z.boolean().default(false).describe(DYNAMIC_SECRETS.DELETE.isForced)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -143,12 +159,12 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const dynamicSecretCfg = await server.services.dynamicSecret.deleteBySlug({ const dynamicSecretCfg = await server.services.dynamicSecret.deleteByName({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
slug: req.params.slug, name: req.params.name,
...req.body ...req.body
}); });
return { dynamicSecret: dynamicSecretCfg }; return { dynamicSecret: dynamicSecretCfg };
@@ -156,16 +172,16 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}); });
server.route({ server.route({
url: "/:slug", url: "/:name",
method: "GET", method: "GET",
schema: { schema: {
params: z.object({ params: z.object({
slug: z.string() name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name)
}), }),
querystring: z.object({ querystring: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.GET_BY_NAME.path),
environment: z.string().min(1) environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.environmentSlug)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -182,7 +198,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
slug: req.params.slug, name: req.params.name,
...req.query ...req.query
}); });
return { dynamicSecret: dynamicSecretCfg }; return { dynamicSecret: dynamicSecretCfg };
@@ -194,9 +210,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
method: "GET", method: "GET",
schema: { schema: {
querystring: z.object({ querystring: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(DYNAMIC_SECRETS.LIST.path),
environment: z.string().min(1) environmentSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.environmentSlug)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -218,16 +234,21 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}); });
server.route({ server.route({
url: "/:slug/leases", url: "/:name/leases",
method: "GET", method: "GET",
schema: { schema: {
params: z.object({ params: z.object({
slug: z.string() name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name)
}), }),
querystring: z.object({ querystring: z.object({
projectSlug: z.string().min(1), projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.projectSlug),
path: z.string().trim().default("/").transform(removeTrailingSlash), path: z
environment: z.string().min(1) .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: { response: {
200: z.object({ 200: z.object({
@@ -242,7 +263,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
slug: req.params.slug, name: req.params.name,
...req.query ...req.query
}); });
return { leases }; return { leases };
@@ -32,7 +32,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
.select(selectAllTableCols(TableName.DynamicSecretLease)) .select(selectAllTableCols(TableName.DynamicSecretLease))
.select( .select(
db.ref("id").withSchema(TableName.DynamicSecret).as("dynId"), 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("version").withSchema(TableName.DynamicSecret).as("dynVersion"),
db.ref("type").withSchema(TableName.DynamicSecret).as("dynType"), db.ref("type").withSchema(TableName.DynamicSecret).as("dynType"),
db.ref("defaultTTL").withSchema(TableName.DynamicSecret).as("dynDefaultTTL"), db.ref("defaultTTL").withSchema(TableName.DynamicSecret).as("dynDefaultTTL"),
@@ -54,7 +54,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
...DynamicSecretLeasesSchema.parse(doc), ...DynamicSecretLeasesSchema.parse(doc),
dynamicSecret: { dynamicSecret: {
id: doc.dynId, id: doc.dynId,
slug: doc.dynSlug, name: doc.dynName,
version: doc.dynVersion, version: doc.dynVersion,
type: doc.dynType, type: doc.dynType,
defaultTTL: doc.dynDefaultTTL, defaultTTL: doc.dynDefaultTTL,
@@ -45,9 +45,9 @@ export const dynamicSecretLeaseServiceFactory = ({
projectDAL projectDAL
}: TDynamicSecretLeaseServiceFactoryDep) => { }: TDynamicSecretLeaseServiceFactoryDep) => {
const create = async ({ const create = async ({
environment, environmentSlug,
path, path,
slug, name,
projectSlug, projectSlug,
actor, actor,
actorId, actorId,
@@ -69,13 +69,13 @@ export const dynamicSecretLeaseServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, 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" }); 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 (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const totalLeasesTaken = await dynamicSecretLeaseDAL.countLeasesForDynamicSecret(dynamicSecretCfg.id); const totalLeasesTaken = await dynamicSecretLeaseDAL.countLeasesForDynamicSecret(dynamicSecretCfg.id);
@@ -119,7 +119,7 @@ export const dynamicSecretLeaseServiceFactory = ({
actor, actor,
projectSlug, projectSlug,
path, path,
environment, environmentSlug,
leaseId leaseId
}: TRenewDynamicSecretLeaseDTO) => { }: TRenewDynamicSecretLeaseDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
@@ -135,10 +135,10 @@ export const dynamicSecretLeaseServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit, 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" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
@@ -180,7 +180,7 @@ export const dynamicSecretLeaseServiceFactory = ({
const revokeLease = async ({ const revokeLease = async ({
leaseId, leaseId,
environment, environmentSlug,
path, path,
projectSlug, projectSlug,
actor, actor,
@@ -202,10 +202,10 @@ export const dynamicSecretLeaseServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete, 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" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
@@ -245,12 +245,12 @@ export const dynamicSecretLeaseServiceFactory = ({
const listLeases = async ({ const listLeases = async ({
path, path,
slug, name,
actor, actor,
actorId, actorId,
projectSlug, projectSlug,
actorOrgId, actorOrgId,
environment, environmentSlug,
actorAuthMethod actorAuthMethod
}: TListDynamicSecretLeasesDTO) => { }: TListDynamicSecretLeasesDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
@@ -266,13 +266,13 @@ export const dynamicSecretLeaseServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read, 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" }); 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 (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id }); const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
@@ -283,7 +283,7 @@ export const dynamicSecretLeaseServiceFactory = ({
projectSlug, projectSlug,
actorOrgId, actorOrgId,
path, path,
environment, environmentSlug,
actor, actor,
actorId, actorId,
leaseId, leaseId,
@@ -302,10 +302,10 @@ export const dynamicSecretLeaseServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read, 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" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
@@ -5,9 +5,9 @@ export enum DynamicSecretLeaseStatus {
} }
export type TCreateDynamicSecretLeaseDTO = { export type TCreateDynamicSecretLeaseDTO = {
slug: string; name: string;
path: string; path: string;
environment: string; environmentSlug: string;
ttl?: string; ttl?: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@@ -15,21 +15,21 @@ export type TCreateDynamicSecretLeaseDTO = {
export type TDetailsDynamicSecretLeaseDTO = { export type TDetailsDynamicSecretLeaseDTO = {
leaseId: string; leaseId: string;
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TListDynamicSecretLeasesDTO = { export type TListDynamicSecretLeasesDTO = {
slug: string; name: string;
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretLeaseDTO = { export type TDeleteDynamicSecretLeaseDTO = {
leaseId: string; leaseId: string;
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
isForced?: boolean; isForced?: boolean;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@@ -37,7 +37,7 @@ export type TDeleteDynamicSecretLeaseDTO = {
export type TRenewDynamicSecretLeaseDTO = { export type TRenewDynamicSecretLeaseDTO = {
leaseId: string; leaseId: string;
path: string; path: string;
environment: string; environmentSlug: string;
ttl?: string; ttl?: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@@ -48,11 +48,11 @@ export const dynamicSecretServiceFactory = ({
const create = async ({ const create = async ({
path, path,
actor, actor,
slug, name,
actorId, actorId,
maxTTL, maxTTL,
provider, provider,
environment, environmentSlug,
projectSlug, projectSlug,
actorOrgId, actorOrgId,
defaultTTL, defaultTTL,
@@ -71,13 +71,13 @@ export const dynamicSecretServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, 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" }); 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) if (existingDynamicSecret)
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" }); throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
@@ -99,22 +99,22 @@ export const dynamicSecretServiceFactory = ({
maxTTL, maxTTL,
defaultTTL, defaultTTL,
folderId: folder.id, folderId: folder.id,
slug name
}); });
return dynamicSecretCfg; return dynamicSecretCfg;
}; };
const updateBySlug = async ({ const updateByName = async ({
slug, name,
maxTTL, maxTTL,
defaultTTL, defaultTTL,
inputs, inputs,
environment, environmentSlug,
projectSlug, projectSlug,
path, path,
actor, actor,
actorId, actorId,
newSlug, newName,
actorOrgId, actorOrgId,
actorAuthMethod actorAuthMethod
}: TUpdateDynamicSecretDTO) => { }: TUpdateDynamicSecretDTO) => {
@@ -132,17 +132,17 @@ export const dynamicSecretServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit, 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" }); 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 (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
if (newSlug) { if (newName) {
const existingDynamicSecret = await dynamicSecretDAL.findOne({ slug: newSlug, folderId: folder.id }); const existingDynamicSecret = await dynamicSecretDAL.findOne({ name: newName, folderId: folder.id });
if (existingDynamicSecret) if (existingDynamicSecret)
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" }); throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
} }
@@ -171,7 +171,7 @@ export const dynamicSecretServiceFactory = ({
keyEncoding: encryptedInput.encoding, keyEncoding: encryptedInput.encoding,
maxTTL, maxTTL,
defaultTTL, defaultTTL,
slug: newSlug ?? slug, name: newName ?? name,
status: null, status: null,
statusDetails: null statusDetails: null
}); });
@@ -179,15 +179,15 @@ export const dynamicSecretServiceFactory = ({
return updatedDynamicCfg; return updatedDynamicCfg;
}; };
const deleteBySlug = async ({ const deleteByName = async ({
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
actorId, actorId,
actor, actor,
projectSlug, projectSlug,
slug, name,
path, path,
environment, environmentSlug,
isForced isForced
}: TDeleteDynamicSecretDTO) => { }: TDeleteDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
@@ -204,13 +204,13 @@ export const dynamicSecretServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit, 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" }); 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 (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const leases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id }); const leases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
@@ -239,10 +239,10 @@ export const dynamicSecretServiceFactory = ({
}; };
const getDetails = async ({ const getDetails = async ({
slug, name,
projectSlug, projectSlug,
path, path,
environment, environmentSlug,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
actorId, actorId,
@@ -261,13 +261,13 @@ export const dynamicSecretServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit, 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" }); 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 (!dynamicSecretCfg) throw new BadRequestError({ message: "Dynamic secret not found" });
const decryptedStoredInput = JSON.parse( const decryptedStoredInput = JSON.parse(
infisicalSymmetricDecrypt({ infisicalSymmetricDecrypt({
@@ -289,7 +289,7 @@ export const dynamicSecretServiceFactory = ({
actor, actor,
projectSlug, projectSlug,
path, path,
environment environmentSlug
}: TListDynamicSecretsDTO) => { }: TListDynamicSecretsDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" }); if (!project) throw new BadRequestError({ message: "Project not found" });
@@ -304,10 +304,10 @@ export const dynamicSecretServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit, 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" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
const dynamicSecretCfg = await dynamicSecretDAL.find({ folderId: folder.id }); const dynamicSecretCfg = await dynamicSecretDAL.find({ folderId: folder.id });
@@ -316,8 +316,8 @@ export const dynamicSecretServiceFactory = ({
return { return {
create, create,
updateBySlug, updateByName,
deleteBySlug, deleteByName,
getDetails, getDetails,
list list
}; };
@@ -16,39 +16,39 @@ export type TCreateDynamicSecretDTO = {
defaultTTL: string; defaultTTL: string;
maxTTL?: string | null; maxTTL?: string | null;
path: string; path: string;
environment: string; environmentSlug: string;
slug: string; name: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TUpdateDynamicSecretDTO = { export type TUpdateDynamicSecretDTO = {
slug: string; name: string;
newSlug?: string; newName?: string;
defaultTTL?: string; defaultTTL?: string;
maxTTL?: string | null; maxTTL?: string | null;
path: string; path: string;
environment: string; environmentSlug: string;
inputs?: TProvider["inputs"]; inputs?: TProvider["inputs"];
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretDTO = { export type TDeleteDynamicSecretDTO = {
slug: string; name: string;
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
isForced?: boolean; isForced?: boolean;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDetailsDynamicSecretDTO = { export type TDetailsDynamicSecretDTO = {
slug: string; name: string;
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TListDynamicSecretsDTO = { export type TListDynamicSecretsDTO = {
path: string; path: string;
environment: string; environmentSlug: string;
projectSlug: string; projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@@ -13,8 +13,8 @@ import { DynamicSecretSqlDBSchema, TDynamicProviderFns } from "./models";
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000; const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
const generatePassword = (size?: number) => { const generatePassword = (size?: number) => {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*'$#"; const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#";
return customAlphabet(charset, 32)(size); return customAlphabet(charset, 48)(size);
}; };
export const SqlDatabaseProvider = (): TDynamicProviderFns => { export const SqlDatabaseProvider = (): TDynamicProviderFns => {
@@ -61,13 +61,13 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const db = await getClient(providerInputs); const db = await getClient(providerInputs);
const username = alphaNumericNanoId(21); const username = alphaNumericNanoId(32);
const password = generatePassword(); const password = generatePassword();
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
username, username,
password: "infisical", password,
expiration expiration
}); });