diff --git a/_config/script/redact-data-report.md b/_config/script/redact-data-report.md index a37f07165..d8b711009 100644 --- a/_config/script/redact-data-report.md +++ b/_config/script/redact-data-report.md @@ -86,8 +86,10 @@ | 表 | 保留行 | 标识 | |----|--------|------| | `iam_user_info` | 1 行 | `id=1`(username=`bootx`,name=`超级管理员`,client_code=`admin`)—— 系统内置超级管理员 | +| `iam_user_expand_info` | 1 行 | `id=1`——内置超管的扩展信息。**必须与 `iam_user_info` 同 id 保留**:登录后 `getLoginAfterUserInfo` 按 id 查此表,缺行则抛 `UserInfoNotExistsException`,token 签发成功也无法进入系统 | > 该账号在 `iam_user_role` 中无角色绑定记录,推断为代码层面识别的内置超管(拥有全部权限),故 `iam_user_role` 可整表清除。 +> `iam_user_password_security` 虽也属超管关联表,但 `getPasswordStatus` 用 `.orElse(null)` 优雅降级(返回 `initialPassword=true`),缺行不卡登录,故**不**纳入行级保留,保持整表清除。 ### 5.2 清除清单(约 63 张表,整表跳过) @@ -101,7 +103,7 @@ `mch_credential`、`pay_easy_pay_credential`。 #### D. 用户认证与隐私 -`iam_user_info`(仅留 id=1)、`iam_user_password_history`、`iam_user_two_factor`、`iam_user_social`、`iam_user_role`、`iam_user_expand_info`、`iam_user_password_security`、`iam_user_dashboard_preference`、`mch_user`。 +`iam_user_info`(仅留 id=1)、`iam_user_password_history`、`iam_user_two_factor`、`iam_user_social`、`iam_user_role`、`iam_user_password_security`、`iam_user_dashboard_preference`、`mch_user`。(注:`iam_user_expand_info` 已改为行级保留 id=1,见 §5.1) #### E. 平台配置 `system_platform_config`(含明文 OSS 密钥,整表清)、`system_platform_encrypt_config`(加密平台认证配置)。 @@ -143,12 +145,13 @@ const KEEP_TABLES = new Set([ 'system_sensitive_word', ]) -// iam_user_info 行级保留:id=1 (bootx 超管) -const BOOTX_ADMIN_RE = /^INSERT INTO public\.iam_user_info\s+VALUES\s*\(\s*1\s*,/ +// 内置超管关联表行级保留:id=1 (bootx) —— iam_user_info + iam_user_expand_info +const BOOTX_ADMIN_TABLES = new Set(['iam_user_info', 'iam_user_expand_info']) +const BOOTX_ADMIN_RE = /^INSERT INTO public\.(?:iam_user_info|iam_user_expand_info)\s+VALUES\s*\(\s*1\s*,/ function handleInsert(table, lines) { - if (KEEP_TABLES.has(table)) return output(lines) // ① 种子表保留 - if (BOOTX_ADMIN_RE.test(lines[0])) return output(lines) // ② bootx 行保留 + if (KEEP_TABLES.has(table)) return output(lines) // ① 种子表保留 + if (BOOTX_ADMIN_TABLES.has(table) && BOOTX_ADMIN_RE.test(lines[0])) return output(lines) // ② bootx 行保留 // ③ 其余清除 writeRedactedMark(table) } diff --git a/_config/script/redact-data.mjs b/_config/script/redact-data.mjs index fe77763c7..e2c956b2f 100644 --- a/_config/script/redact-data.mjs +++ b/_config/script/redact-data.mjs @@ -6,8 +6,10 @@ * 策略:保留 18 张系统种子表 + bootx 内置超管(id=1),其余整表清除。 * * 三原则决策: - * ① 表 ∈ KEEP_TABLES(系统种子) → 整表原样保留 - * ② iam_user_info 且 id=1 (bootx 超管) → 该行保留,同表其余行清除 + * ① 表 ∈ KEEP_TABLES(系统种子) → 整表原样保留 + * ② 内置超管关联表且 id=1 (bootx) → 该行保留,同表其余行清除 + * (iam_user_info 账号主表 + iam_user_expand_info 扩展信息, + * 后者登录后拉用户信息必需,缺则抛 UserInfoNotExistsException) * ③ 其余 → 整表跳过(写审计注释) * * 格式假设:pg_dump --inserts(INSERT INTO public.表名 VALUES (...),无列名)。 @@ -55,9 +57,11 @@ const KEEP_TABLES = new Set([ 'system_sensitive_word', ]) -// ---------- 行级保留:iam_user_info 的 bootx 超管(id=1) ---------- -const BOOTX_ADMIN_TABLE = 'iam_user_info' -const BOOTX_ADMIN_RE = /^INSERT INTO (?:public\.)?iam_user_info\s+VALUES\s*\(\s*1\s*,/ +// ---------- 行级保留:内置超管 bootx(id=1) 的关联表 ---------- +// iam_user_info: 账号主表 +// iam_user_expand_info: 用户扩展信息(登录后拉用户信息必需,缺则 getLoginAfterUserInfo 抛 UserInfoNotExistsException) +const BOOTX_ADMIN_TABLES = new Set(['iam_user_info', 'iam_user_expand_info']) +const BOOTX_ADMIN_RE = /^INSERT INTO (?:public\.)?(?:iam_user_info|iam_user_expand_info)\s+VALUES\s*\(\s*1\s*,/ // ---------- 解析规则 ---------- const INSERT_RE = /^INSERT INTO (?:public\.)?(\w+)\s+VALUES\b/i @@ -102,8 +106,8 @@ function handleInsert(table, lines) { for (const ln of lines) output.write(ln + '\n') return } - // ② iam_user_info 行级保留:bootx 超管 (id=1) - if (table === BOOTX_ADMIN_TABLE && BOOTX_ADMIN_RE.test(lines[0])) { + // ② 内置超管关联表行级保留:bootx (id=1) + if (BOOTX_ADMIN_TABLES.has(table) && BOOTX_ADMIN_RE.test(lines[0])) { bump(table, true) for (const ln of lines) output.write(ln + '\n') return @@ -149,11 +153,11 @@ rl.on('close', () => { function printReport() { const keptSeedTables = [] - const partialTables = [] // 行级保留表(iam_user_info) + const partialTables = [] // 行级保留表(内置超管关联表) const clearedTables = [] for (const [t, s] of stats) { if (KEEP_TABLES.has(t)) keptSeedTables.push([t, s]) - else if (t === BOOTX_ADMIN_TABLE) partialTables.push([t, s]) + else if (BOOTX_ADMIN_TABLES.has(t)) partialTables.push([t, s]) else clearedTables.push([t, s]) } @@ -165,7 +169,8 @@ function printReport() { lines.push('') lines.push('========== 脱敏统计(白名单模式) ==========') lines.push(`[保留·种子表] ${keptSeedTables.length} 张表,INSERT ${sumKept} 条(全保留)`) - lines.push(`[保留·行级] iam_user_info:INSERT ${partialTables[0]?.[1].total ?? 0} → 保留 ${partialTables[0]?.[1].kept ?? 0} (bootx id=1),清除 ${sumPartialCleared}`) + const partialDetail = partialTables.map(([t, s]) => `${t}(${s.total}→${s.kept})`).join(', ') || '无' + lines.push(`[保留·行级] 内置超管(id=1)关联表:${partialDetail},清除 ${sumPartialCleared}`) lines.push(`[清除] ${clearedTables.length} 张表,丢弃 INSERT ${sumCleared} 条`) lines.push('---- 清除明细 ----') for (const [t, s] of clearedTables.sort((a, b) => a[0].localeCompare(b[0]))) { diff --git a/_config/sql/data.sql b/_config/sql/data.sql index 3060547a9..fe257e016 100644 --- a/_config/sql/data.sql +++ b/_config/sql/data.sql @@ -3705,6 +3705,8 @@ INSERT INTO iam_perm_code VALUES (2072377871723388928, 'trade:order:view', 'trad INSERT INTO iam_perm_code VALUES (2072990657130180608, 'trade:refund:manage', 'trade:refund', true, '', 1, 1, 1, false, '2026-07-03 10:27:41.620782+00', '2026-07-15 03:59:23.715999+00', 'perm.trade:refund:manage'); INSERT INTO iam_perm_code VALUES (2072990657130180609, 'trade:refund:view', 'trade:refund', true, '', 1, 1, 1, false, '2026-07-03 10:27:41.620782+00', '2026-07-15 03:59:23.717505+00', 'perm.trade:refund:view'); INSERT INTO iam_perm_code VALUES (2079866295577419776, 'trade:callback-record:view', 'trade:callback-record', true, '', 1, 1, 0, false, '2026-07-22 09:49:01.56951+00', '2026-07-22 09:49:01.575017+00', 'perm.trade:callback-record:view'); +INSERT INTO iam_perm_code VALUES (2083000000000000001, 'trade:transfer:view', 'trade:transfer', true, '', 1, 1, 0, false, '2026-08-05 16:00:00+00', '2026-08-05 16:00:00+00', 'perm.trade:transfer:view'); +INSERT INTO iam_perm_code VALUES (2083000000000000002, 'trade:transfer:manage', 'trade:transfer', true, '', 1, 1, 0, false, '2026-08-05 16:00:00+00', '2026-08-05 16:00:00+00', 'perm.trade:transfer:manage'); INSERT INTO iam_perm_code VALUES (2079866295615168512, 'trade:mch-notice:manage', 'trade:mch-notice', true, '', 1, 1, 0, false, '2026-07-22 09:49:01.578524+00', '2026-07-22 09:49:01.578524+00', 'perm.trade:mch-notice:manage'); INSERT INTO iam_perm_code VALUES (2079866295619362816, 'trade:mch-notice:view', 'trade:mch-notice', true, '', 1, 1, 0, false, '2026-07-22 09:49:01.579524+00', '2026-07-22 09:49:01.579524+00', 'perm.trade:mch-notice:view'); INSERT INTO iam_perm_code VALUES (2082811057154473984, 'merchant:gateway-pay-config:manage', 'merchant:gateway-pay-config', true, NULL, 1, 1, 0, false, '2026-07-30 12:50:27.440009+00', '2026-07-30 12:50:27.44601+00', 'perm.merchant:gateway-pay-config:manage'); @@ -4165,6 +4167,7 @@ INSERT INTO iam_role_menu VALUES (2083360959857102849, 2, NULL, 91112); -- -- REDACTED: iam_user_expand_info (整表清除) +INSERT INTO iam_user_expand_info VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, false, NULL, NULL, NULL, '2026-03-28 14:26:08+00', NULL); --