Account.Credentials 是 JSONB map,混合存放可编辑的非敏感配置(base_url、 model_mapping、project_id 等)与敏感秘钥(OAuth access/refresh/id token、 API key、AWS secret、Vertex private key 等)。当前所有 admin 账号接口直接 透传该 map,token 经由浏览器 DevTools、抓包、日志等途径泄漏。 - service 包新增 SensitiveCredentialKeys 清单与 MergePreservingSensitiveCreds 作为单一权威定义。 - dto 层 RedactCredentials 在响应里剥离敏感子键,输出 credentials_status (has_<key> 布尔标识)告知前端存在性,不暴露原值。 - AccountFromServiceShallow 接入脱敏,覆盖 list、get、create、update、 refresh、batch、bulk-update、OAuth 创建等 9 个 handler。 - service.UpdateAccount 改为合并语义:incoming 没传敏感键则保留 existing, 让前端"全对象 PUT"流程在脱敏后无感工作;显式提供新 token 仍会覆盖。 - 前端 EditAccountModal 修复脱敏后会崩的两处兜底:apikey 必填检查和 Vertex SA JSON 存在性校验改读 credentials_status.has_*。 - 导出端点 /admin/accounts/data 走独立的 DataAccount 结构,按设计保留 完整 credentials 作为管理员备份路径。 测试:RedactCredentials 单元测试、mapper 端到端 JSON 断言(确认序列化 后无 token 子串)、UpdateAccount 合并语义三种场景(保留 / 覆盖 / 空 map 跳过)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.2 KiB
Go
45 lines
1.2 KiB
Go
// Package dto provides data transfer objects for HTTP handlers.
|
||
package dto
|
||
|
||
import "github.com/Wei-Shaw/sub2api/internal/service"
|
||
|
||
// RedactCredentials 复制一份 in,剥离 service.SensitiveCredentialKeys 列出的所有敏感子键,
|
||
// 并产出一个 has_<key> 状态 map 表示哪些敏感键存在且非零值。
|
||
//
|
||
// 输入 nil 时返回 nil, nil(避免响应里出现空对象)。
|
||
// 不修改入参;调用方拿到的 out 可安全序列化进 JSON 返回前端。
|
||
func RedactCredentials(in map[string]any) (out map[string]any, status map[string]bool) {
|
||
if in == nil {
|
||
return nil, nil
|
||
}
|
||
out = make(map[string]any, len(in))
|
||
for k, v := range in {
|
||
if service.IsSensitiveCredentialKey(k) {
|
||
if isCredentialValuePresent(v) {
|
||
if status == nil {
|
||
status = make(map[string]bool, 4)
|
||
}
|
||
status["has_"+k] = true
|
||
}
|
||
continue
|
||
}
|
||
out[k] = v
|
||
}
|
||
return out, status
|
||
}
|
||
|
||
// isCredentialValuePresent 判断值是否"存在且非零"。空字符串、nil、false 均视为未配置;
|
||
// 其余非零类型(数字、对象、字符串等)视为已配置。
|
||
func isCredentialValuePresent(v any) bool {
|
||
switch x := v.(type) {
|
||
case nil:
|
||
return false
|
||
case string:
|
||
return x != ""
|
||
case bool:
|
||
return x
|
||
default:
|
||
return true
|
||
}
|
||
}
|