diff --git a/messages/en-US.json b/messages/en-US.json index a3862c0a..236778ca 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1175,7 +1175,7 @@ "saveSecuritySettings": "Save Security Settings", "sendEmailNotification": "Send Email Notification", "linkCopied": "Link Copied", - "linkCopiedDescription": "The reset link has been copied to your clipboard" + "linkCopiedDescription": "The reset link has been copied to your clipboard", "securityKeyManage": "Manage Security Keys", "securityKeyDescription": "Add or remove security keys for passwordless authentication", "securityKeyRegister": "Register New Security Key", diff --git a/package-lock.json b/package-lock.json index e0f2d0dd..cb44266b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,9 +33,9 @@ "@radix-ui/react-toast": "1.2.14", "@react-email/components": "0.3.1", "@react-email/render": "^1.1.2", + "@react-email/tailwind": "1.2.1", "@simplewebauthn/browser": "^13.1.0", "@simplewebauthn/server": "^9.0.3", - "@react-email/tailwind": "1.2.1", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", diff --git a/server/db/sqlite/migrate.ts b/server/db/sqlite/migrate.ts index 55399ea6..15be0891 100644 --- a/server/db/sqlite/migrate.ts +++ b/server/db/sqlite/migrate.ts @@ -1,46 +1,13 @@ import { migrate } from "drizzle-orm/better-sqlite3/migrator"; import db from "./driver"; import path from "path"; -import { location } from "./driver"; -import Database from "better-sqlite3"; -import type { Database as BetterSqlite3Database } from "better-sqlite3"; const migrationsFolder = path.join("server/migrations"); -const dropAllTables = (sqlite: BetterSqlite3Database) => { - console.log("Dropping all existing tables..."); - - // Disable foreign key checks - sqlite.pragma('foreign_keys = OFF'); - - // Get all tables - const tables = sqlite.prepare(` - SELECT name FROM sqlite_master - WHERE type='table' - AND name NOT LIKE 'sqlite_%' - `).all() as { name: string }[]; - - // Drop each table - for (const table of tables) { - console.log(`Dropping table: ${table.name}`); - sqlite.prepare(`DROP TABLE IF EXISTS "${table.name}"`).run(); - } - - // Re-enable foreign key checks - sqlite.pragma('foreign_keys = ON'); -}; - const runMigrations = async () => { console.log("Running migrations..."); try { - // Initialize the database file with a valid SQLite header - const sqlite = new Database(location) as BetterSqlite3Database; - - // Drop all existing tables first - dropAllTables(sqlite); - - // Run the migrations - migrate(db as any, { + await migrate(db as any, { migrationsFolder: migrationsFolder, }); console.log("Migrations completed successfully."); diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 4aca24ae..b2dfaf7d 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -91,6 +91,16 @@ export async function verifyTotp( ); } + // Add type guard to ensure password is defined + if (!password) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Password is required for two-factor authentication" + ) + ); + } + const validPassword = await verifyPassword( password, user.passwordHash! diff --git a/server/routers/user/index.ts b/server/routers/user/index.ts index c6330f4b..2abcde07 100644 --- a/server/routers/user/index.ts +++ b/server/routers/user/index.ts @@ -15,6 +15,4 @@ export { updateUser } from "./updateUser"; export { adminUpdateUser } from "./adminUpdateUser"; export { adminResetUserPassword } from "./adminResetUserPassword"; export type { AdminResetUserPasswordBody, AdminResetUserPasswordResponse } from "./adminResetUserPassword"; -export * from "./updateUser2FA"; -export * from "./adminUpdateUser2FA"; -export * from "./adminGetUser"; +export * from "./adminUpdateUser2FA"; \ No newline at end of file diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index 53c5e545..075c7d1b 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -6,6 +6,7 @@ import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; import path from "path"; import m1 from "./scriptsPg/1.6.0"; import m2 from "./scriptsPg/1.7.0"; +import m3 from "./scriptsPg/1.8.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -13,7 +14,8 @@ import m2 from "./scriptsPg/1.7.0"; // Define the migration list with versions and their corresponding functions const migrations = [ { version: "1.6.0", run: m1 }, - { version: "1.7.0", run: m2 } + { version: "1.7.0", run: m2 }, + { version: "1.8.0", run: m3 } // Add new migrations here as they are created ] as { version: string; diff --git a/server/setup/scriptsPg/1.8.0.ts b/server/setup/scriptsPg/1.8.0.ts new file mode 100644 index 00000000..c372fe6a --- /dev/null +++ b/server/setup/scriptsPg/1.8.0.ts @@ -0,0 +1,31 @@ +import { db } from "@server/db/pg"; + +export default async function migrate() { + try { + console.log("Starting webauthnChallenge table creation..."); + + // Create the table (PostgreSQL already has the correct table name) + await db.execute(` + CREATE TABLE IF NOT EXISTS webauthnChallenge ( + sessionId TEXT PRIMARY KEY, + challenge TEXT NOT NULL, + securityKeyName TEXT, + userId TEXT, + expiresAt INTEGER NOT NULL, + FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE + ); + `); + + // Create the index + await db.execute(` + CREATE INDEX IF NOT EXISTS idx_webauthnChallenge_expiresAt ON webauthnChallenge(expiresAt); + `); + + console.log("Successfully created webauthnChallenge table and index"); + return true; + } catch (error: any) { + console.error("Unable to create webauthnChallenge table:", error); + console.error("Error details:", error.message); + return false; + } +} \ No newline at end of file diff --git a/server/setup/scriptsSqlite/1.7.0.ts b/server/setup/scriptsSqlite/1.7.0.ts index 0790b27c..d597196b 100644 --- a/server/setup/scriptsSqlite/1.7.0.ts +++ b/server/setup/scriptsSqlite/1.7.0.ts @@ -17,12 +17,17 @@ export default async function migration() { db.exec(` ALTER TABLE orgs ADD COLUMN passwordResetTokenExpiryHours INTEGER NOT NULL DEFAULT 1; `); - })(); // <-- executes the transaction immediately + })(); // executes the transaction immediately db.pragma("foreign_keys = ON"); console.log(`Added passwordResetTokenExpiryHours column to orgs table`); } catch (e) { console.log("Error adding passwordResetTokenExpiryHours column to orgs table:"); console.log(e); + } + + try { + db.pragma("foreign_keys = OFF"); + db.transaction(() => { db.exec(` CREATE TABLE IF NOT EXISTS securityKey ( credentialId TEXT PRIMARY KEY, diff --git a/server/setup/scriptsSqlite/1.8.1.ts b/server/setup/scriptsSqlite/1.8.1.ts deleted file mode 100644 index 560b314a..00000000 --- a/server/setup/scriptsSqlite/1.8.1.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { db } from "@server/db"; - -export default async function migrate() { - try { - console.log("Starting table rename migration..."); - - // Rename the table - await db.run(` - ALTER TABLE securityKeyChallenge RENAME TO webauthnChallenge; - `); - console.log("Successfully renamed table"); - - // Rename the index - await db.run(` - DROP INDEX IF EXISTS idx_securityKeyChallenge_expiresAt; - CREATE INDEX IF NOT EXISTS idx_webauthnChallenge_expiresAt ON webauthnChallenge(expiresAt); - `); - console.log("Successfully updated index"); - - console.log(`Renamed securityKeyChallenge table to webauthnChallenge`); - return true; - } catch (error: any) { - console.error("Unable to rename securityKeyChallenge table:", error); - console.error("Error details:", error.message); - return false; - } -} \ No newline at end of file diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index b8a647c2..d4eac4aa 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -186,52 +186,48 @@ export default function UsersTable({ users }: Props) { cell: ({ row }) => { const r = row.original; return ( - <> -