bibimbap/docs/rbac-ddl.sql

81 lines
3.3 KiB
SQL

-- W1 거버넌스/RBAC. 멱등. db/apply-local-ddl.sh 로 실행 DB 비파괴 적용.
-- 기존 데이터(전원 role='USER') 호환 — 추가만, 파괴 없음.
-- 1) users.role 값집합 확장 (CHECK 멱등 추가)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_role_check') THEN
ALTER TABLE "users"
ADD CONSTRAINT "users_role_check"
CHECK ("role" IN ('ADMIN', 'SUBADMIN', 'USER'));
END IF;
END
$$;
-- 2) users.permissions_epoch (세션 권한 전파 버전 스탬프)
ALTER TABLE "users"
ADD COLUMN IF NOT EXISTS "permissions_epoch" bigint DEFAULT 0 NOT NULL;
COMMENT ON COLUMN "users"."permissions_epoch" IS
'RBAC 권한 변경 버전. 변경 시 +1 → 세션 캐시 epoch 와 mismatch 시 권한 재로딩(회수 즉시성, 결정4)';
-- 3) permissions (권한 카탈로그)
CREATE SEQUENCE IF NOT EXISTS "permissions_id_seq";
CREATE TABLE IF NOT EXISTS "permissions" (
"id" bigint DEFAULT nextval('permissions_id_seq'::regclass) NOT NULL,
"permission_key" character varying(50) NOT NULL,
"display_name" character varying(100) NOT NULL,
"is_active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
PRIMARY KEY ("id")
);
ALTER SEQUENCE "permissions_id_seq" OWNED BY "permissions"."id";
CREATE UNIQUE INDEX IF NOT EXISTS "ux_permissions_key"
ON "permissions" ("permission_key");
-- 4) user_permissions (SUBADMIN 개별 권한 join)
CREATE SEQUENCE IF NOT EXISTS "user_permissions_id_seq";
CREATE TABLE IF NOT EXISTS "user_permissions" (
"id" bigint DEFAULT nextval('user_permissions_id_seq'::regclass) NOT NULL,
"user_id" bigint NOT NULL,
"permission_key" character varying(50) NOT NULL,
"granted_by" bigint,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
PRIMARY KEY ("id")
);
ALTER SEQUENCE "user_permissions_id_seq" OWNED BY "user_permissions"."id";
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'user_permissions_user_id_fkey') THEN
ALTER TABLE "user_permissions"
ADD CONSTRAINT "user_permissions_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users" ("id");
END IF;
END
$$;
CREATE UNIQUE INDEX IF NOT EXISTS "ux_user_permissions_user_key"
ON "user_permissions" ("user_id", "permission_key");
CREATE INDEX IF NOT EXISTS "idx_user_permissions_user"
ON "user_permissions" ("user_id");
-- 5) rbac_audit_log (감사로그 — 임명/강등/토글 대칭 기록)
CREATE SEQUENCE IF NOT EXISTS "rbac_audit_log_id_seq";
CREATE TABLE IF NOT EXISTS "rbac_audit_log" (
"id" bigint DEFAULT nextval('rbac_audit_log_id_seq'::regclass) NOT NULL,
"actor_id" bigint NOT NULL,
"target_id" bigint NOT NULL,
"action" character varying(30) NOT NULL,
"permission_key" character varying(50),
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
PRIMARY KEY ("id")
);
ALTER SEQUENCE "rbac_audit_log_id_seq" OWNED BY "rbac_audit_log"."id";
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'rbac_audit_log_action_check') THEN
ALTER TABLE "rbac_audit_log"
ADD CONSTRAINT "rbac_audit_log_action_check"
CHECK ("action" IN ('APPOINT', 'DEMOTE', 'GRANT', 'REVOKE'));
END IF;
END
$$;