fix: 修复 OIDC 用户预配与跨标签页登录态
增加受控 JIT 本地用户投影,并使用 HttpOnly Cookie 在新标签页恢复认证中心登录态。补充错误语义、安全校验、自动化测试与回滚配置。
This commit is contained in:
@@ -57,6 +57,8 @@ func (s *Server) ready(w http.ResponseWriter, r *http.Request) {
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} auth.User
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Router /api/v1/me [get]
|
||||
func (s *Server) me(w http.ResponseWriter, r *http.Request) {
|
||||
user, _ := auth.UserFromContext(r.Context())
|
||||
@@ -630,6 +632,8 @@ func (s *Server) listUserGroups(w http.ResponseWriter, r *http.Request) {
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} UserGroupListResponse
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/workspace/user-groups [get]
|
||||
func (s *Server) listCurrentUserGroups(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -669,6 +673,8 @@ func compactAuthStrings(values ...string) []string {
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} APIKeyListResponse
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/v1/api-keys [get]
|
||||
func (s *Server) listAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -689,8 +695,9 @@ func (s *Server) listAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} PlayableAPIKeyListResponse
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/playground/api-keys [get]
|
||||
func (s *Server) listPlayableAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -698,7 +705,7 @@ func (s *Server) listPlayableAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := s.store.ListPlayableAPIKeys(r.Context(), user)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrLocalUserRequired) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
writeLocalUserRequired(w)
|
||||
return
|
||||
}
|
||||
s.logger.Error("list playable api keys failed", "error", err)
|
||||
@@ -719,6 +726,8 @@ func (s *Server) listPlayableAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
// @Success 201 {object} store.CreatedAPIKey
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/v1/api-keys [post]
|
||||
func (s *Server) createAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -731,7 +740,7 @@ func (s *Server) createAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
created, err := s.store.CreateAPIKey(r.Context(), input, user)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrLocalUserRequired) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
writeLocalUserRequired(w)
|
||||
return
|
||||
}
|
||||
s.logger.Error("create api key failed", "error", err)
|
||||
@@ -768,7 +777,11 @@ func (s *Server) updateAPIKeyScopes(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, item)
|
||||
return
|
||||
}
|
||||
if errors.Is(err, store.ErrLocalUserRequired) || errors.Is(err, store.ErrInvalidAPIKeyScopes) {
|
||||
if errors.Is(err, store.ErrLocalUserRequired) {
|
||||
writeLocalUserRequired(w)
|
||||
return
|
||||
}
|
||||
if errors.Is(err, store.ErrInvalidAPIKeyScopes) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -801,7 +814,7 @@ func (s *Server) disableAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, store.ErrLocalUserRequired) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
writeLocalUserRequired(w)
|
||||
return
|
||||
}
|
||||
if store.IsNotFound(err) {
|
||||
@@ -833,7 +846,7 @@ func (s *Server) deleteAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, store.ErrLocalUserRequired) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
writeLocalUserRequired(w)
|
||||
return
|
||||
}
|
||||
if store.IsNotFound(err) {
|
||||
@@ -1505,6 +1518,8 @@ func matchedRateLimitRule(policy map[string]any, metric string) map[string]any {
|
||||
// @Success 200 {object} TaskListResponse
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 503 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/workspace/tasks [get]
|
||||
// @Router /api/v1/tasks [get]
|
||||
|
||||
Reference in New Issue
Block a user