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
+19 -6
View File
@@ -79,7 +79,7 @@ func (s *Server) me(w http.ResponseWriter, r *http.Request) {
// @Failure 500 {object} ErrorEnvelope
// @Router /api/v1/auth/register [post]
func (s *Server) register(w http.ResponseWriter, r *http.Request) {
if !s.localIdentityEnabled() {
if !s.localIdentityEnabled() || !s.ordinaryLocalJWTEnabled() {
writeError(w, http.StatusForbidden, "local registration is disabled")
return
}
@@ -111,7 +111,7 @@ func (s *Server) register(w http.ResponseWriter, r *http.Request) {
// login godoc
// @Summary 本地登录
// @Description 使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。
// @Description 使用用户名或邮箱登录本地账号,并返回 24 小时 JWT。非本地身份模式只允许本地应急 Manager 登录。
// @Tags auth
// @Accept json
// @Produce json
@@ -123,10 +123,6 @@ func (s *Server) register(w http.ResponseWriter, r *http.Request) {
// @Failure 500 {object} ErrorEnvelope
// @Router /api/v1/auth/login [post]
func (s *Server) login(w http.ResponseWriter, r *http.Request) {
if !s.localIdentityEnabled() {
writeError(w, http.StatusForbidden, "local login is disabled")
return
}
var input store.LocalLoginInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
@@ -142,6 +138,10 @@ func (s *Server) login(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusInternalServerError, "login failed")
return
}
if !s.localLoginAllowed(user) {
writeError(w, http.StatusForbidden, "local login is disabled except for break-glass managers")
return
}
s.writeAuthResponse(w, http.StatusOK, user)
}
@@ -150,6 +150,19 @@ func (s *Server) localIdentityEnabled() bool {
return mode == "" || mode == "standalone" || mode == "hybrid"
}
func (s *Server) localLoginAllowed(user store.GatewayUser) bool {
for _, role := range user.Roles {
if role == "manager" || role == "admin" {
return true
}
}
return s.localIdentityEnabled() && s.ordinaryLocalJWTEnabled()
}
func (s *Server) ordinaryLocalJWTEnabled() bool {
return s.identityRuntime == nil || s.identityRuntime.LegacyJWTEnabled()
}
func (s *Server) writeAuthResponse(w http.ResponseWriter, status int, user store.GatewayUser) {
authUser := authUserFromGatewayUser(user)
const ttl = 24 * time.Hour