Files
OpenFlare/backend/pkg/ginutil/auth.go
T
Ryan 53ae3007d0 fix(cordis): fail-closed auth guards for user/message_gateway/admin (#1)
* autoresearch iter 23: fail-closed auth guarding for user/message_gateway

Both plugins resolve contracts.AuthService in Apply to build their route
middleware, but declared only DBService in Inject(). The kernel gates a
plugin's Apply solely on declared deps, and cmd/app.go registers user
before auth, so user mounted first, core.Inject failed, and loginMW
silently degraded to a pass-through closure — leaving /api/v1/user
change-password, profile and access-tokens unguarded. message_gateway
was saved only by its later list position.

Declare AuthService in Inject() for both, and pin the property with a
reconcile-level test that mirrors production registration order and
asserts the real auth middleware reaches the route table.

* autoresearch iter 24: make auth middleware fallbacks fail closed

user, message_gateway and admin each fell back to a c.Next() closure when
contracts.AuthService could not be resolved, so a route would be served as
if authenticated. For admin this is reachable at runtime: OnDispose calls
service.ResetServices(), which nils the global the per-request guard reads,
so requests still in flight during dispose bypass authorization entirely.

Add ginutil.AuthUnavailable() and bind every fallback to it, with a test
that drives each plugin's registered guard without an auth service present
and asserts the request is aborted rather than passed through.

* chore(autoresearch): log iter 23 (fail-open auth ordering, proven)

* autoresearch iter 24 follow-up: let staticcheck infer the auth guard type

* docs(autoresearch): log iters 24-25 and lessons 9-11 (declared-dep bug class, gate discipline)
2026-08-29 11:21:04 +08:00

25 lines
775 B
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package ginutil
import (
"Wavelet/pkg/response"
"github.com/gin-gonic/gin"
)
// errAuthUnavailable is reported when a route's authentication guard cannot be
// resolved, so the request is rejected instead of reaching the handler.
const errAuthUnavailable = "authServiceUnavailable"
// AuthUnavailable returns a middleware that denies the request. Plugins use it
// as the fallback when contracts.AuthService cannot be resolved or its middleware
// has an unexpected shape: the alternative is a pass-through closure that serves
// the request as if it were authenticated.
func AuthUnavailable() gin.HandlerFunc {
return func(c *gin.Context) {
response.AbortUnauthorized(c, errAuthUnavailable)
}
}