fix(identity): 完善统一认证配对恢复与安全退役

修复 credentials_saved 状态无法恢复、配对与激活并发冲突,以及 SSF 和身份 Secret 生命周期不完整的问题。新增持久化协调器、取消与清理状态机、事务级并发门禁、受控 SSF 凭据交接、禁用后的延迟 Secret 清理,并对生产环境统一认证及 Discovery 端点强制 HTTPS。

验证:go test ./...;go test -race ./internal/auth ./internal/identity ./internal/identityruntime ./internal/securityevents ./internal/httpapi ./internal/store -count=1;go vet ./...;真实 PostgreSQL 并发及清理成功/冲突回滚测试;pnpm openapi。
This commit is contained in:
2026-07-17 18:31:12 +08:00
parent cdfca61304
commit a312ad880d
55 changed files with 9225 additions and 419 deletions
+198 -5
View File
@@ -2918,7 +2918,7 @@
"BearerAuth": []
}
],
"description": "保留 Superseded Revision 供回滚,清理 BFF Session,并继续允许本地管理登录。",
"description": "将 Revision 转为只读 Superseded 审计历史,清理 BFF Session,并继续允许本地管理登录;重新接入必须使用新的接入码。",
"produces": [
"application/json"
],
@@ -3116,6 +3116,174 @@
}
}
},
"/api/admin/system/identity/pairings/{pairingID}/cancel": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "原子封存未激活 Revision,并异步清理由该 Revision 拥有的临时 Secret 与 SSF 连接。不会撤销已经完成的远端 Exchange;下一次凭据交付会轮换机器凭据。",
"produces": [
"application/json"
],
"tags": [
"identity"
],
"summary": "放弃本地统一认证配对",
"parameters": [
{
"type": "string",
"description": "配对 ID",
"name": "pairingID",
"in": "path",
"required": true
},
{
"type": "string",
"description": "幂等键",
"name": "Idempotency-Key",
"in": "header",
"required": true
},
{
"type": "string",
"description": "当前 Pairing ETag",
"name": "If-Match",
"in": "header",
"required": true
}
],
"responses": {
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/identity.PairingExchange"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"412": {
"description": "Precondition Failed",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"428": {
"description": "Precondition Required",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
}
}
}
},
"/api/admin/system/identity/pairings/{pairingID}/retire-conflicting-security-event": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "仅当指定 Pairing 仍因 owner 冲突停在 credentials_saved 时,退役不属于该 Revision 的旧连接;陈旧请求不会退役当前 Pairing 已创建的新连接。",
"produces": [
"application/json"
],
"tags": [
"identity"
],
"summary": "安全退役阻塞配对的旧 SSF 连接",
"parameters": [
{
"type": "string",
"description": "配对 ID",
"name": "pairingID",
"in": "path",
"required": true
},
{
"type": "string",
"description": "幂等键",
"name": "Idempotency-Key",
"in": "header",
"required": true
},
{
"type": "string",
"description": "当前 Pairing ETag",
"name": "If-Match",
"in": "header",
"required": true
}
],
"responses": {
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/identity.PairingExchange"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"412": {
"description": "Precondition Failed",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
},
"428": {
"description": "Precondition Required",
"schema": {
"$ref": "#/definitions/httpapi.ErrorEnvelope"
}
}
}
}
},
"/api/admin/system/identity/revisions/{revisionID}": {
"patch": {
"security": [
@@ -3290,13 +3458,14 @@
"BearerAuth": []
}
],
"description": "当前远端 OAuth/SSF 资源未版本化,接口会拒绝直接回滚并要求禁用后使用新接入码完成交接。",
"produces": [
"application/json"
],
"tags": [
"identity"
],
"summary": "回滚统一认证 Revision",
"summary": "请求恢复历史统一认证 Revision",
"parameters": [
{
"type": "string",
@@ -4792,7 +4961,7 @@
},
"/api/v1/auth/login": {
"post": {
"description": "使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。",
"description": "使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。非本地身份模式只允许本地应急 Manager 登录。",
"consumes": [
"application/json"
],
@@ -11492,12 +11661,34 @@
}
}
},
"identity.PairingCleanupStatus": {
"type": "string",
"enum": [
"none",
"pending",
"completed"
],
"x-enum-varnames": [
"PairingCleanupNone",
"PairingCleanupPending",
"PairingCleanupCompleted"
]
},
"identity.PairingExchange": {
"type": "object",
"properties": {
"authCenterAuditId": {
"type": "string"
},
"cancelledAt": {
"type": "string"
},
"cleanupCompletedAt": {
"type": "string"
},
"cleanupStatus": {
"$ref": "#/definitions/identity.PairingCleanupStatus"
},
"createdAt": {
"type": "string"
},
@@ -11565,7 +11756,8 @@
"credentials_saved",
"completed",
"failed",
"expired"
"expired",
"cancelled"
],
"x-enum-varnames": [
"PairingMetadataPending",
@@ -11574,7 +11766,8 @@
"PairingCredentialsSaved",
"PairingCompleted",
"PairingFailed",
"PairingExpired"
"PairingExpired",
"PairingCancelled"
]
},
"identity.Revision": {