diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index 0fc8c574..7ae21836 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -16,6 +16,7 @@ import m8 from "./scriptsPg/1.11.1"; import m9 from "./scriptsPg/1.12.0"; import m10 from "./scriptsPg/1.13.0"; import m11 from "./scriptsPg/1.14.0"; +import m12 from "./scriptsPg/1.15.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -32,7 +33,8 @@ const migrations = [ { version: "1.11.1", run: m8 }, { version: "1.12.0", run: m9 }, { version: "1.13.0", run: m10 }, - { version: "1.14.0", run: m11 } + { version: "1.14.0", run: m11 }, + { version: "1.15.0", run: m12 } // Add new migrations here as they are created ] as { version: string; diff --git a/server/setup/migrationsSqlite.ts b/server/setup/migrationsSqlite.ts index 640836e0..0bbc86ee 100644 --- a/server/setup/migrationsSqlite.ts +++ b/server/setup/migrationsSqlite.ts @@ -34,6 +34,7 @@ import m29 from "./scriptsSqlite/1.11.1"; import m30 from "./scriptsSqlite/1.12.0"; import m31 from "./scriptsSqlite/1.13.0"; import m32 from "./scriptsSqlite/1.14.0"; +import m33 from "./scriptsSqlite/1.15.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -66,7 +67,8 @@ const migrations = [ { version: "1.11.1", run: m29 }, { version: "1.12.0", run: m30 }, { version: "1.13.0", run: m31 }, - { version: "1.14.0", run: m32 } + { version: "1.14.0", run: m32 }, + { version: "1.15.0", run: m33 } // Add new migrations here as they are created ] as const; diff --git a/server/setup/scriptsPg/1.15.0.ts b/server/setup/scriptsPg/1.15.0.ts new file mode 100644 index 00000000..1ccf001b --- /dev/null +++ b/server/setup/scriptsPg/1.15.0.ts @@ -0,0 +1,136 @@ +import { db } from "@server/db/pg/driver"; +import { sql } from "drizzle-orm"; +import { __DIRNAME } from "@server/lib/consts"; + +const version = "1.15.0"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + try { + await db.execute(sql`BEGIN`); + + await db.execute(sql` + CREATE TABLE "approvals" ( + "approvalId" serial PRIMARY KEY NOT NULL, + "timestamp" integer NOT NULL, + "orgId" varchar NOT NULL, + "clientId" integer, + "userId" varchar NOT NULL, + "decision" varchar DEFAULT 'pending' NOT NULL, + "type" varchar NOT NULL + ); + `); + await db.execute(sql` + CREATE TABLE "clientPostureSnapshots" ( + "snapshotId" serial PRIMARY KEY NOT NULL, + "clientId" integer, + "collectedAt" integer NOT NULL + ); + `); + await db.execute(sql` + CREATE TABLE "currentFingerprint" ( + "id" serial PRIMARY KEY NOT NULL, + "olmId" text NOT NULL, + "firstSeen" integer NOT NULL, + "lastSeen" integer NOT NULL, + "lastCollectedAt" integer NOT NULL, + "username" text, + "hostname" text, + "platform" text, + "osVersion" text, + "kernelVersion" text, + "arch" text, + "deviceModel" text, + "serialNumber" text, + "platformFingerprint" varchar, + "biometricsEnabled" boolean DEFAULT false NOT NULL, + "diskEncrypted" boolean DEFAULT false NOT NULL, + "firewallEnabled" boolean DEFAULT false NOT NULL, + "autoUpdatesEnabled" boolean DEFAULT false NOT NULL, + "tpmAvailable" boolean DEFAULT false NOT NULL, + "windowsDefenderEnabled" boolean DEFAULT false NOT NULL, + "macosSipEnabled" boolean DEFAULT false NOT NULL, + "macosGatekeeperEnabled" boolean DEFAULT false NOT NULL, + "macosFirewallStealthMode" boolean DEFAULT false NOT NULL, + "linuxAppArmorEnabled" boolean DEFAULT false NOT NULL, + "linuxSELinuxEnabled" boolean DEFAULT false NOT NULL + ); + `); + await db.execute(sql` + CREATE TABLE "fingerprintSnapshots" ( + "id" serial PRIMARY KEY NOT NULL, + "fingerprintId" integer, + "username" text, + "hostname" text, + "platform" text, + "osVersion" text, + "kernelVersion" text, + "arch" text, + "deviceModel" text, + "serialNumber" text, + "platformFingerprint" varchar, + "biometricsEnabled" boolean DEFAULT false NOT NULL, + "diskEncrypted" boolean DEFAULT false NOT NULL, + "firewallEnabled" boolean DEFAULT false NOT NULL, + "autoUpdatesEnabled" boolean DEFAULT false NOT NULL, + "tpmAvailable" boolean DEFAULT false NOT NULL, + "windowsDefenderEnabled" boolean DEFAULT false NOT NULL, + "macosSipEnabled" boolean DEFAULT false NOT NULL, + "macosGatekeeperEnabled" boolean DEFAULT false NOT NULL, + "macosFirewallStealthMode" boolean DEFAULT false NOT NULL, + "linuxAppArmorEnabled" boolean DEFAULT false NOT NULL, + "linuxSELinuxEnabled" boolean DEFAULT false NOT NULL, + "hash" text NOT NULL, + "collectedAt" integer NOT NULL + ); + `); + await db.execute( + sql`ALTER TABLE "loginPageBranding" ALTER COLUMN "logoUrl" DROP NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "clients" ADD COLUMN "archived" boolean DEFAULT false NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "clients" ADD COLUMN "blocked" boolean DEFAULT false NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "clients" ADD COLUMN "approvalState" varchar;` + ); + await db.execute(sql`ALTER TABLE "idp" ADD COLUMN "tags" text;`); + await db.execute( + sql`ALTER TABLE "olms" ADD COLUMN "archived" boolean DEFAULT false NOT NULL;` + ); + await db.execute( + sql`ALTER TABLE "roles" ADD COLUMN "requireDeviceApproval" boolean DEFAULT false;` + ); + await db.execute( + sql`ALTER TABLE "approvals" ADD CONSTRAINT "approvals_orgId_orgs_orgId_fk" FOREIGN KEY ("orgId") REFERENCES "public"."orgs"("orgId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "approvals" ADD CONSTRAINT "approvals_clientId_clients_clientId_fk" FOREIGN KEY ("clientId") REFERENCES "public"."clients"("clientId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "approvals" ADD CONSTRAINT "approvals_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "clientPostureSnapshots" ADD CONSTRAINT "clientPostureSnapshots_clientId_clients_clientId_fk" FOREIGN KEY ("clientId") REFERENCES "public"."clients"("clientId") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "currentFingerprint" ADD CONSTRAINT "currentFingerprint_olmId_olms_id_fk" FOREIGN KEY ("olmId") REFERENCES "public"."olms"("id") ON DELETE cascade ON UPDATE no action;` + ); + await db.execute( + sql`ALTER TABLE "fingerprintSnapshots" ADD CONSTRAINT "fingerprintSnapshots_fingerprintId_currentFingerprint_id_fk" FOREIGN KEY ("fingerprintId") REFERENCES "public"."currentFingerprint"("id") ON DELETE set null ON UPDATE no action;` + ); + + await db.execute(sql`COMMIT`); + console.log("Migrated database"); + } catch (e) { + await db.execute(sql`ROLLBACK`); + console.log("Unable to migrate database"); + console.log(e); + throw e; + } + + console.log(`${version} migration complete`); +} diff --git a/server/setup/scriptsSqlite/1.15.0.ts b/server/setup/scriptsSqlite/1.15.0.ts new file mode 100644 index 00000000..c8a3a221 --- /dev/null +++ b/server/setup/scriptsSqlite/1.15.0.ts @@ -0,0 +1,155 @@ +import { __DIRNAME, APP_PATH } from "@server/lib/consts"; +import Database from "better-sqlite3"; +import path from "path"; + +const version = "1.15.0"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + const location = path.join(APP_PATH, "db", "db.sqlite"); + const db = new Database(location); + + try { + db.pragma("foreign_keys = OFF"); + + db.transaction(() => { + db.prepare( + ` +CREATE TABLE 'approvals' ( + 'approvalId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, + 'timestamp' integer NOT NULL, + 'orgId' text NOT NULL, + 'clientId' integer, + 'userId' text, + 'decision' text DEFAULT 'pending' NOT NULL, + 'type' text NOT NULL, + FOREIGN KEY ('orgId') REFERENCES 'orgs'('orgId') ON UPDATE no action ON DELETE cascade, + FOREIGN KEY ('clientId') REFERENCES 'clients'('clientId') ON UPDATE no action ON DELETE cascade, + FOREIGN KEY ('userId') REFERENCES 'user'('id') ON UPDATE no action ON DELETE cascade +); + ` + ).run(); + + db.prepare( + ` +CREATE TABLE 'currentFingerprint' ( + 'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL, + 'olmId' text NOT NULL, + 'firstSeen' integer NOT NULL, + 'lastSeen' integer NOT NULL, + 'lastCollectedAt' integer NOT NULL, + 'username' text, + 'hostname' text, + 'platform' text, + 'osVersion' text, + 'kernelVersion' text, + 'arch' text, + 'deviceModel' text, + 'serialNumber' text, + 'platformFingerprint' text, + 'biometricsEnabled' integer DEFAULT false NOT NULL, + 'diskEncrypted' integer DEFAULT false NOT NULL, + 'firewallEnabled' integer DEFAULT false NOT NULL, + 'autoUpdatesEnabled' integer DEFAULT false NOT NULL, + 'tpmAvailable' integer DEFAULT false NOT NULL, + 'windowsDefenderEnabled' integer DEFAULT false NOT NULL, + 'macosSipEnabled' integer DEFAULT false NOT NULL, + 'macosGatekeeperEnabled' integer DEFAULT false NOT NULL, + 'macosFirewallStealthMode' integer DEFAULT false NOT NULL, + 'linuxAppArmorEnabled' integer DEFAULT false NOT NULL, + 'linuxSELinuxEnabled' integer DEFAULT false NOT NULL, + FOREIGN KEY ('olmId') REFERENCES 'olms'('id') ON UPDATE no action ON DELETE cascade +); + ` + ).run(); + + db.prepare( + ` +CREATE TABLE 'fingerprintSnapshots' ( + 'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL, + 'fingerprintId' integer, + 'username' text, + 'hostname' text, + 'platform' text, + 'osVersion' text, + 'kernelVersion' text, + 'arch' text, + 'deviceModel' text, + 'serialNumber' text, + 'platformFingerprint' text, + 'biometricsEnabled' integer DEFAULT false NOT NULL, + 'diskEncrypted' integer DEFAULT false NOT NULL, + 'firewallEnabled' integer DEFAULT false NOT NULL, + 'autoUpdatesEnabled' integer DEFAULT false NOT NULL, + 'tpmAvailable' integer DEFAULT false NOT NULL, + 'windowsDefenderEnabled' integer DEFAULT false NOT NULL, + 'macosSipEnabled' integer DEFAULT false NOT NULL, + 'macosGatekeeperEnabled' integer DEFAULT false NOT NULL, + 'macosFirewallStealthMode' integer DEFAULT false NOT NULL, + 'linuxAppArmorEnabled' integer DEFAULT false NOT NULL, + 'linuxSELinuxEnabled' integer DEFAULT false NOT NULL, + 'hash' text NOT NULL, + 'collectedAt' integer NOT NULL, + FOREIGN KEY ('fingerprintId') REFERENCES 'currentFingerprint'('id') ON UPDATE no action ON DELETE set null +); + ` + ).run(); + + db.prepare( + ` +CREATE TABLE '__new_loginPageBranding' ( + 'loginPageBrandingId' integer PRIMARY KEY AUTOINCREMENT NOT NULL, + 'logoUrl' text, + 'logoWidth' integer NOT NULL, + 'logoHeight' integer NOT NULL, + 'primaryColor' text, + 'resourceTitle' text NOT NULL, + 'resourceSubtitle' text, + 'orgTitle' text, + 'orgSubtitle' text +); + ` + ).run(); + + db.prepare( + `INSERT INTO '__new_loginPageBranding'("loginPageBrandingId", "logoUrl", "logoWidth", "logoHeight", "primaryColor", "resourceTitle", "resourceSubtitle", "orgTitle", "orgSubtitle") SELECT "loginPageBrandingId", "logoUrl", "logoWidth", "logoHeight", "primaryColor", "resourceTitle", "resourceSubtitle", "orgTitle", "orgSubtitle" FROM 'loginPageBranding';` + ).run(); + + db.prepare(`DROP TABLE 'loginPageBranding';`).run(); + + db.prepare( + `ALTER TABLE '__new_loginPageBranding' RENAME TO 'loginPageBranding';` + ).run(); + + db.prepare( + `ALTER TABLE 'clients' ADD 'archived' integer DEFAULT false NOT NULL;` + ).run(); + + db.prepare( + `ALTER TABLE 'clients' ADD 'blocked' integer DEFAULT false NOT NULL;` + ).run(); + + db.prepare(`ALTER TABLE 'clients' ADD 'approvalState' text;`).run(); + + db.prepare(`ALTER TABLE 'idp' ADD 'tags' text;`).run(); + + db.prepare( + `ALTER TABLE 'olms' ADD 'archived' integer DEFAULT false NOT NULL;` + ).run(); + + db.prepare( + `ALTER TABLE 'roles' ADD 'requireDeviceApproval' integer DEFAULT false;` + ).run(); + })(); + + db.pragma("foreign_keys = ON"); + + console.log(`Migrated database`); + } catch (e) { + console.log("Failed to migrate db:", e); + throw e; + } + + console.log(`${version} migration complete`); +}