Admins (with 2FA) can view the site as a non-admin user for up to 30 minutes, with a required reason and a "Viewing as" banner. It is strictly read-only: a guard mounted before every route refuses any write made with an impersonation token, protect/optionalAuth check the session is live, and GET handlers skip the price-refresh writes. Start, end, reason and every page/API call (including blocked attempts) are audited; optional email to the user. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CP62L9JxHomA36aYP1ekCe
56 lines
2.0 KiB
SQL
56 lines
2.0 KiB
SQL
-- AlterEnum
|
|
-- This migration adds more than one value to an enum.
|
|
-- With PostgreSQL versions 11 and earlier, this is not possible
|
|
-- in a single migration. This can be worked around by creating
|
|
-- multiple migrations, each migration adding only one value to
|
|
-- the enum.
|
|
|
|
|
|
ALTER TYPE "AdminAuditAction" ADD VALUE 'impersonation_started';
|
|
ALTER TYPE "AdminAuditAction" ADD VALUE 'impersonation_ended';
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ImpersonationSession" (
|
|
"id" TEXT NOT NULL,
|
|
"adminId" TEXT NOT NULL,
|
|
"targetUserId" TEXT NOT NULL,
|
|
"reason" TEXT NOT NULL,
|
|
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
"endedAt" TIMESTAMP(3),
|
|
"endedReason" TEXT,
|
|
|
|
CONSTRAINT "ImpersonationSession_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ImpersonationRequestLog" (
|
|
"id" TEXT NOT NULL,
|
|
"sessionId" TEXT NOT NULL,
|
|
"method" TEXT NOT NULL,
|
|
"path" TEXT NOT NULL,
|
|
"blocked" BOOLEAN NOT NULL DEFAULT false,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ImpersonationRequestLog_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ImpersonationSession_adminId_idx" ON "ImpersonationSession"("adminId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ImpersonationSession_targetUserId_idx" ON "ImpersonationSession"("targetUserId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ImpersonationRequestLog_sessionId_createdAt_idx" ON "ImpersonationRequestLog"("sessionId", "createdAt");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImpersonationSession" ADD CONSTRAINT "ImpersonationSession_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImpersonationSession" ADD CONSTRAINT "ImpersonationSession_targetUserId_fkey" FOREIGN KEY ("targetUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImpersonationRequestLog" ADD CONSTRAINT "ImpersonationRequestLog_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "ImpersonationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|