refactor(access): 统一分层白名单权限语义
取消跨主体专属占用,按租户、用户组、用户、当前 API Key 和 scope 分层求交,并在任务落库前统一校验候选。\n\n增加旧 allow 规则归档清理迁移、脱敏审计工具和回滚运行手册,补齐主体隔离、deny 优先及列表与运行时一致性测试。
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
CREATE TABLE IF NOT EXISTS gateway_access_rule_allow_archive (
|
||||
migration_batch text NOT NULL,
|
||||
rule_id uuid NOT NULL,
|
||||
subject_type text NOT NULL,
|
||||
subject_id uuid NOT NULL,
|
||||
resource_type text NOT NULL,
|
||||
resource_id uuid NOT NULL,
|
||||
effect text NOT NULL,
|
||||
priority integer NOT NULL,
|
||||
min_permission_level integer NOT NULL,
|
||||
conditions jsonb NOT NULL,
|
||||
metadata jsonb NOT NULL,
|
||||
status text NOT NULL,
|
||||
rule_created_at timestamptz NOT NULL,
|
||||
rule_updated_at timestamptz NOT NULL,
|
||||
row_sha256 text NOT NULL CHECK (row_sha256 ~ '^[0-9a-f]{64}$'),
|
||||
archived_at timestamptz NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (migration_batch, rule_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_gateway_access_rule_allow_archive_subject
|
||||
ON gateway_access_rule_allow_archive(migration_batch, subject_type, subject_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gateway_access_rule_migration_batches (
|
||||
migration_batch text PRIMARY KEY,
|
||||
allow_count bigint NOT NULL,
|
||||
allow_sha256 text NOT NULL CHECK (allow_sha256 ~ '^[0-9a-f]{64}$'),
|
||||
deny_count bigint NOT NULL,
|
||||
deny_sha256 text NOT NULL CHECK (deny_sha256 ~ '^[0-9a-f]{64}$'),
|
||||
archived_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Reads stay available while access-rule writes are paused for the atomic
|
||||
-- snapshot, archive, and cleanup sequence.
|
||||
LOCK TABLE gateway_access_rules IN SHARE ROW EXCLUSIVE MODE;
|
||||
|
||||
INSERT INTO gateway_access_rule_migration_batches (
|
||||
migration_batch, allow_count, allow_sha256, deny_count, deny_sha256
|
||||
)
|
||||
SELECT
|
||||
'0102_access_rule_allow_whitelist_semantics',
|
||||
COUNT(*) FILTER (WHERE effect = 'allow'),
|
||||
encode(digest(COALESCE(string_agg(
|
||||
encode(digest(concat_ws(chr(31),
|
||||
id::text, subject_type, subject_id::text, resource_type,
|
||||
resource_id::text, effect, priority::text,
|
||||
min_permission_level::text, conditions::text, metadata::text,
|
||||
status, extract(epoch FROM created_at)::text,
|
||||
extract(epoch FROM updated_at)::text
|
||||
), 'sha256'), 'hex'), chr(10) ORDER BY id
|
||||
) FILTER (WHERE effect = 'allow'), ''), 'sha256'), 'hex'),
|
||||
COUNT(*) FILTER (WHERE effect = 'deny'),
|
||||
encode(digest(COALESCE(string_agg(
|
||||
encode(digest(concat_ws(chr(31),
|
||||
id::text, subject_type, subject_id::text, resource_type,
|
||||
resource_id::text, effect, priority::text,
|
||||
min_permission_level::text, conditions::text, metadata::text,
|
||||
status, extract(epoch FROM created_at)::text,
|
||||
extract(epoch FROM updated_at)::text
|
||||
), 'sha256'), 'hex'), chr(10) ORDER BY id
|
||||
) FILTER (WHERE effect = 'deny'), ''), 'sha256'), 'hex')
|
||||
FROM gateway_access_rules
|
||||
ON CONFLICT (migration_batch) DO NOTHING;
|
||||
|
||||
INSERT INTO gateway_access_rule_allow_archive (
|
||||
migration_batch, rule_id, subject_type, subject_id, resource_type,
|
||||
resource_id, effect, priority, min_permission_level, conditions,
|
||||
metadata, status, rule_created_at, rule_updated_at, row_sha256
|
||||
)
|
||||
SELECT
|
||||
'0102_access_rule_allow_whitelist_semantics', id, subject_type, subject_id,
|
||||
resource_type, resource_id, effect, priority, min_permission_level,
|
||||
conditions, metadata, status, created_at, updated_at,
|
||||
encode(digest(concat_ws(chr(31),
|
||||
id::text, subject_type, subject_id::text, resource_type,
|
||||
resource_id::text, effect, priority::text,
|
||||
min_permission_level::text, conditions::text, metadata::text,
|
||||
status, extract(epoch FROM created_at)::text,
|
||||
extract(epoch FROM updated_at)::text
|
||||
), 'sha256'), 'hex')
|
||||
FROM gateway_access_rules
|
||||
WHERE effect = 'allow'
|
||||
ON CONFLICT (migration_batch, rule_id) DO NOTHING;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
expected_count bigint;
|
||||
expected_sha256 text;
|
||||
archived_count bigint;
|
||||
archived_sha256 text;
|
||||
BEGIN
|
||||
SELECT allow_count, allow_sha256
|
||||
INTO expected_count, expected_sha256
|
||||
FROM gateway_access_rule_migration_batches
|
||||
WHERE migration_batch = '0102_access_rule_allow_whitelist_semantics';
|
||||
|
||||
SELECT COUNT(*), encode(digest(COALESCE(string_agg(
|
||||
row_sha256, chr(10) ORDER BY rule_id
|
||||
), ''), 'sha256'), 'hex')
|
||||
INTO archived_count, archived_sha256
|
||||
FROM gateway_access_rule_allow_archive
|
||||
WHERE migration_batch = '0102_access_rule_allow_whitelist_semantics';
|
||||
|
||||
IF archived_count <> expected_count OR archived_sha256 <> expected_sha256 THEN
|
||||
RAISE EXCEPTION 'access-rule allow archive verification failed';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
DELETE FROM gateway_access_rules
|
||||
WHERE effect = 'allow';
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
expected_deny_count bigint;
|
||||
expected_deny_sha256 text;
|
||||
actual_allow_count bigint;
|
||||
actual_deny_count bigint;
|
||||
actual_deny_sha256 text;
|
||||
BEGIN
|
||||
SELECT deny_count, deny_sha256
|
||||
INTO expected_deny_count, expected_deny_sha256
|
||||
FROM gateway_access_rule_migration_batches
|
||||
WHERE migration_batch = '0102_access_rule_allow_whitelist_semantics';
|
||||
|
||||
SELECT
|
||||
COUNT(*) FILTER (WHERE effect = 'allow'),
|
||||
COUNT(*) FILTER (WHERE effect = 'deny'),
|
||||
encode(digest(COALESCE(string_agg(
|
||||
encode(digest(concat_ws(chr(31),
|
||||
id::text, subject_type, subject_id::text, resource_type,
|
||||
resource_id::text, effect, priority::text,
|
||||
min_permission_level::text, conditions::text, metadata::text,
|
||||
status, extract(epoch FROM created_at)::text,
|
||||
extract(epoch FROM updated_at)::text
|
||||
), 'sha256'), 'hex'), chr(10) ORDER BY id
|
||||
) FILTER (WHERE effect = 'deny'), ''), 'sha256'), 'hex')
|
||||
INTO actual_allow_count, actual_deny_count, actual_deny_sha256
|
||||
FROM gateway_access_rules;
|
||||
|
||||
IF actual_allow_count <> 0 OR
|
||||
actual_deny_count <> expected_deny_count OR
|
||||
actual_deny_sha256 <> expected_deny_sha256 THEN
|
||||
RAISE EXCEPTION 'access-rule whitelist migration verification failed';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
Reference in New Issue
Block a user