From ee29577e6d36d86d9c0112682c7a681aecf068b6 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Wed, 20 Mar 2024 14:55:11 +0530 Subject: [PATCH] feat(server): added dynamic secret database schema --- backend/src/@types/knex.d.ts | 12 ++++ .../20240318164718_dynamic-secret.ts | 56 +++++++++++++++++++ .../src/db/schemas/dynamic-secret-leases.ts | 23 ++++++++ backend/src/db/schemas/dynamic-secrets.ts | 31 ++++++++++ backend/src/db/schemas/index.ts | 2 + backend/src/db/schemas/models.ts | 2 + 6 files changed, 126 insertions(+) create mode 100644 backend/src/db/migrations/20240318164718_dynamic-secret.ts create mode 100644 backend/src/db/schemas/dynamic-secret-leases.ts create mode 100644 backend/src/db/schemas/dynamic-secrets.ts diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index 31b80e0e3..29d526e52 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -17,6 +17,12 @@ import { TBackupPrivateKey, TBackupPrivateKeyInsert, TBackupPrivateKeyUpdate, + TDynamicSecretLeases, + TDynamicSecretLeasesInsert, + TDynamicSecretLeasesUpdate, + TDynamicSecrets, + TDynamicSecretsInsert, + TDynamicSecretsUpdate, TGitAppInstallSessions, TGitAppInstallSessionsInsert, TGitAppInstallSessionsUpdate, @@ -340,6 +346,12 @@ declare module "knex/types/tables" { TSecretSnapshotFoldersInsert, TSecretSnapshotFoldersUpdate >; + [TableName.DynamicSecret]: Knex.CompositeTableType; + [TableName.DynamicSecretLease]: Knex.CompositeTableType< + TDynamicSecretLeases, + TDynamicSecretLeasesInsert, + TDynamicSecretLeasesUpdate + >; [TableName.SamlConfig]: Knex.CompositeTableType; [TableName.LdapConfig]: Knex.CompositeTableType; [TableName.OrgBot]: Knex.CompositeTableType; diff --git a/backend/src/db/migrations/20240318164718_dynamic-secret.ts b/backend/src/db/migrations/20240318164718_dynamic-secret.ts new file mode 100644 index 000000000..ea7186da4 --- /dev/null +++ b/backend/src/db/migrations/20240318164718_dynamic-secret.ts @@ -0,0 +1,56 @@ +import { Knex } from "knex"; + +import { SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas"; +import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; + +export async function up(knex: Knex): Promise { + const doesTableExist = await knex.schema.hasTable(TableName.DynamicSecret); + if (!doesTableExist) { + await knex.schema.createTable(TableName.DynamicSecret, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("slug").notNullable(); + t.integer("version").notNullable(); + t.string("type").notNullable(); + t.string("defaultTTL").notNullable(); + t.string("maxTTL"); + t.string("inputIV").notNullable(); + t.text("inputCiphertext").notNullable(); + t.string("inputTag").notNullable(); + 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 + t.string("status"); + t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); + t.unique(["slug", "folderId"]); + t.timestamps(true, true, true); + }); + } + + await createOnUpdateTrigger(knex, TableName.DynamicSecret); + + const doesTableDynamicSecretLease = await knex.schema.hasTable(TableName.DynamicSecretLease); + if (!doesTableDynamicSecretLease) { + await knex.schema.createTable(TableName.DynamicSecretLease, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.integer("version").notNullable(); + t.string("externalEntityId").notNullable(); + t.string("maxTTL"); + t.datetime("expireAt").notNullable(); + t.uuid("dynamicSecretId").notNullable(); + t.foreign("dynamicSecretId").references("id").inTable(TableName.DynamicSecret).onDelete("CASCADE"); + t.timestamps(true, true, true); + }); + } + + await createOnUpdateTrigger(knex, TableName.DynamicSecretLease); +} + +export async function down(knex: Knex): Promise { + await dropOnUpdateTrigger(knex, TableName.DynamicSecretLease); + await knex.schema.dropTableIfExists(TableName.DynamicSecretLease); + + await dropOnUpdateTrigger(knex, TableName.DynamicSecret); + await knex.schema.dropTableIfExists(TableName.DynamicSecret); +} diff --git a/backend/src/db/schemas/dynamic-secret-leases.ts b/backend/src/db/schemas/dynamic-secret-leases.ts new file mode 100644 index 000000000..95efdeb2b --- /dev/null +++ b/backend/src/db/schemas/dynamic-secret-leases.ts @@ -0,0 +1,23 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const DynamicSecretLeasesSchema = z.object({ + id: z.string().uuid(), + version: z.number(), + externalEntityId: z.string(), + maxTTL: z.string().nullable().optional(), + expireAt: z.date(), + dynamicSecretId: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date() +}); + +export type TDynamicSecretLeases = z.infer; +export type TDynamicSecretLeasesInsert = Omit, TImmutableDBKeys>; +export type TDynamicSecretLeasesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/dynamic-secrets.ts b/backend/src/db/schemas/dynamic-secrets.ts new file mode 100644 index 000000000..81da953b3 --- /dev/null +++ b/backend/src/db/schemas/dynamic-secrets.ts @@ -0,0 +1,31 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const DynamicSecretsSchema = z.object({ + id: z.string().uuid(), + slug: z.string(), + version: z.number(), + type: z.string(), + defaultTTL: z.string(), + maxTTL: z.string().nullable().optional(), + inputIV: z.string(), + inputCiphertext: z.string(), + inputTag: z.string(), + 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(), + createdAt: z.date(), + updatedAt: z.date() +}); + +export type TDynamicSecrets = z.infer; +export type TDynamicSecretsInsert = Omit, TImmutableDBKeys>; +export type TDynamicSecretsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/index.ts b/backend/src/db/schemas/index.ts index 001fdbf18..efa80cfac 100644 --- a/backend/src/db/schemas/index.ts +++ b/backend/src/db/schemas/index.ts @@ -3,6 +3,8 @@ export * from "./audit-logs"; export * from "./auth-token-sessions"; export * from "./auth-tokens"; export * from "./backup-private-key"; +export * from "./dynamic-secret-leases"; +export * from "./dynamic-secrets"; export * from "./git-app-install-sessions"; export * from "./git-app-org"; export * from "./identities"; diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index f85feff9c..bda66682e 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -59,6 +59,8 @@ export enum TableName { GitAppOrg = "git_app_org", SecretScanningGitRisk = "secret_scanning_git_risks", TrustedIps = "trusted_ips", + DynamicSecret = "dynamic_secrets", + DynamicSecretLease = "dynamic_secret_leases", // junction tables with tags JnSecretTag = "secret_tag_junction", SecretVersionTag = "secret_version_tag_junction"