81 lines
3.3 KiB
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
|
|
$$;
|