feat(gateway): add accountable admin controls
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE TYPE "AdminAuditOutcome" AS ENUM ('STARTED', 'SUCCEEDED', 'FAILED');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE "app_user"
|
||||
ADD COLUMN IF NOT EXISTS "kakao_grace_until" TIMESTAMP(3);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "admin_audit_event" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"correlation_id" TEXT NOT NULL,
|
||||
"actor_user_id" TEXT NOT NULL,
|
||||
"actor_username" TEXT NOT NULL,
|
||||
"credential_kind" TEXT NOT NULL DEFAULT 'SESSION',
|
||||
"capability" TEXT,
|
||||
"scope" TEXT,
|
||||
"action" TEXT NOT NULL,
|
||||
"target_type" TEXT,
|
||||
"target_id" TEXT,
|
||||
"profile_name" TEXT,
|
||||
"reason" TEXT,
|
||||
"outcome" "AdminAuditOutcome" NOT NULL,
|
||||
"summary" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"error_code" TEXT,
|
||||
"error_message" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "admin_audit_event_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "admin_audit_event_correlation_id_created_at_idx"
|
||||
ON "admin_audit_event"("correlation_id", "created_at");
|
||||
CREATE INDEX IF NOT EXISTS "admin_audit_event_actor_user_id_created_at_idx"
|
||||
ON "admin_audit_event"("actor_user_id", "created_at");
|
||||
CREATE INDEX IF NOT EXISTS "admin_audit_event_target_type_target_id_created_at_idx"
|
||||
ON "admin_audit_event"("target_type", "target_id", "created_at");
|
||||
CREATE INDEX IF NOT EXISTS "admin_audit_event_profile_name_created_at_idx"
|
||||
ON "admin_audit_event"("profile_name", "created_at");
|
||||
CREATE INDEX IF NOT EXISTS "admin_audit_event_action_created_at_idx"
|
||||
ON "admin_audit_event"("action", "created_at");
|
||||
|
||||
COMMENT ON TABLE "admin_audit_event" IS
|
||||
'Append-only administrator action ledger. Application code must never update or delete rows.';
|
||||
|
||||
CREATE OR REPLACE FUNCTION reject_admin_audit_event_mutation()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'admin_audit_event is append-only';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS "admin_audit_event_append_only" ON "admin_audit_event";
|
||||
CREATE TRIGGER "admin_audit_event_append_only"
|
||||
BEFORE UPDATE OR DELETE ON "admin_audit_event"
|
||||
FOR EACH ROW EXECUTE FUNCTION reject_admin_audit_event_mutation();
|
||||
@@ -59,6 +59,12 @@ enum GatewayRuntimeActionStatus {
|
||||
IGNORED
|
||||
}
|
||||
|
||||
enum AdminAuditOutcome {
|
||||
STARTED
|
||||
SUCCEEDED
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum GatewaySourceMode {
|
||||
BRANCH
|
||||
COMMIT
|
||||
@@ -87,6 +93,7 @@ model AppUser {
|
||||
privacyAcceptedAt DateTime? @map("privacy_accepted_at")
|
||||
kakaoVerifiedAt DateTime? @map("kakao_verified_at")
|
||||
kakaoGraceStartedAt DateTime @default(now()) @map("kakao_grace_started_at")
|
||||
kakaoGraceUntil DateTime? @map("kakao_grace_until")
|
||||
deleteAfter DateTime? @map("delete_after")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
@@ -97,6 +104,33 @@ model AppUser {
|
||||
@@map("app_user")
|
||||
}
|
||||
|
||||
model AdminAuditEvent {
|
||||
id String @id @default(uuid())
|
||||
correlationId String @map("correlation_id")
|
||||
actorUserId String @map("actor_user_id")
|
||||
actorUsername String @map("actor_username")
|
||||
credentialKind String @default("SESSION") @map("credential_kind")
|
||||
capability String?
|
||||
scope String?
|
||||
action String
|
||||
targetType String? @map("target_type")
|
||||
targetId String? @map("target_id")
|
||||
profileName String? @map("profile_name")
|
||||
reason String?
|
||||
outcome AdminAuditOutcome
|
||||
summary Json @default(dbgenerated("'{}'::jsonb"))
|
||||
errorCode String? @map("error_code")
|
||||
errorMessage String? @map("error_message")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([correlationId, createdAt])
|
||||
@@index([actorUserId, createdAt])
|
||||
@@index([targetType, targetId, createdAt])
|
||||
@@index([profileName, createdAt])
|
||||
@@index([action, createdAt])
|
||||
@@map("admin_audit_event")
|
||||
}
|
||||
|
||||
model UserIcon {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
|
||||
Reference in New Issue
Block a user