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:
+198
-5
@@ -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": {
|
||||
|
||||
+134
-3
@@ -932,10 +932,26 @@ definitions:
|
||||
example: manual recharge
|
||||
type: string
|
||||
type: object
|
||||
identity.PairingCleanupStatus:
|
||||
enum:
|
||||
- none
|
||||
- pending
|
||||
- completed
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- PairingCleanupNone
|
||||
- PairingCleanupPending
|
||||
- PairingCleanupCompleted
|
||||
identity.PairingExchange:
|
||||
properties:
|
||||
authCenterAuditId:
|
||||
type: string
|
||||
cancelledAt:
|
||||
type: string
|
||||
cleanupCompletedAt:
|
||||
type: string
|
||||
cleanupStatus:
|
||||
$ref: '#/definitions/identity.PairingCleanupStatus'
|
||||
createdAt:
|
||||
type: string
|
||||
expiresAt:
|
||||
@@ -983,6 +999,7 @@ definitions:
|
||||
- completed
|
||||
- failed
|
||||
- expired
|
||||
- cancelled
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- PairingMetadataPending
|
||||
@@ -992,6 +1009,7 @@ definitions:
|
||||
- PairingCompleted
|
||||
- PairingFailed
|
||||
- PairingExpired
|
||||
- PairingCancelled
|
||||
identity.Revision:
|
||||
properties:
|
||||
activatedAt:
|
||||
@@ -4616,7 +4634,7 @@ paths:
|
||||
- identity
|
||||
/api/admin/system/identity/disable:
|
||||
post:
|
||||
description: 保留 Superseded Revision 供回滚,清理 BFF Session,并继续允许本地管理登录。
|
||||
description: 将 Revision 转为只读 Superseded 审计历史,清理 BFF Session,并继续允许本地管理登录;重新接入必须使用新的接入码。
|
||||
parameters:
|
||||
- description: 幂等键
|
||||
in: header
|
||||
@@ -4746,6 +4764,118 @@ paths:
|
||||
summary: 查询统一认证配对状态
|
||||
tags:
|
||||
- identity
|
||||
/api/admin/system/identity/pairings/{pairingID}/cancel:
|
||||
post:
|
||||
description: 原子封存未激活 Revision,并异步清理由该 Revision 拥有的临时 Secret 与 SSF 连接。不会撤销已经完成的远端
|
||||
Exchange;下一次凭据交付会轮换机器凭据。
|
||||
parameters:
|
||||
- description: 配对 ID
|
||||
in: path
|
||||
name: pairingID
|
||||
required: true
|
||||
type: string
|
||||
- description: 幂等键
|
||||
in: header
|
||||
name: Idempotency-Key
|
||||
required: true
|
||||
type: string
|
||||
- description: 当前 Pairing ETag
|
||||
in: header
|
||||
name: If-Match
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
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'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 放弃本地统一认证配对
|
||||
tags:
|
||||
- identity
|
||||
/api/admin/system/identity/pairings/{pairingID}/retire-conflicting-security-event:
|
||||
post:
|
||||
description: 仅当指定 Pairing 仍因 owner 冲突停在 credentials_saved 时,退役不属于该 Revision
|
||||
的旧连接;陈旧请求不会退役当前 Pairing 已创建的新连接。
|
||||
parameters:
|
||||
- description: 配对 ID
|
||||
in: path
|
||||
name: pairingID
|
||||
required: true
|
||||
type: string
|
||||
- description: 幂等键
|
||||
in: header
|
||||
name: Idempotency-Key
|
||||
required: true
|
||||
type: string
|
||||
- description: 当前 Pairing ETag
|
||||
in: header
|
||||
name: If-Match
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
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'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 安全退役阻塞配对的旧 SSF 连接
|
||||
tags:
|
||||
- identity
|
||||
/api/admin/system/identity/revisions/{revisionID}:
|
||||
patch:
|
||||
consumes:
|
||||
@@ -4857,6 +4987,7 @@ paths:
|
||||
- identity
|
||||
/api/admin/system/identity/revisions/{revisionID}/rollback:
|
||||
post:
|
||||
description: 当前远端 OAuth/SSF 资源未版本化,接口会拒绝直接回滚并要求禁用后使用新接入码完成交接。
|
||||
parameters:
|
||||
- description: Revision ID
|
||||
in: path
|
||||
@@ -4902,7 +5033,7 @@ paths:
|
||||
$ref: '#/definitions/httpapi.ErrorEnvelope'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 回滚统一认证 Revision
|
||||
summary: 请求恢复历史统一认证 Revision
|
||||
tags:
|
||||
- identity
|
||||
/api/admin/system/identity/revisions/{revisionID}/validate:
|
||||
@@ -5825,7 +5956,7 @@ paths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。
|
||||
description: 使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。非本地身份模式只允许本地应急 Manager 登录。
|
||||
parameters:
|
||||
- description: 登录请求,account 可为用户名或邮箱
|
||||
in: body
|
||||
|
||||
Reference in New Issue
Block a user