mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
A GetIdentity error (e.g. provision: ensureOrg/Grant failure) was swallowed — next.ServeHTTP without claims → downstream generic 'unauthenticated' 401 with NO diagnostic. Log ::IDENTITY-GET-FAILED:: so the real cause is visible. Co-authored-by: Hanzo Dev <[email protected]>
82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/hanzoai/o11y/pkg/errors"
|
|
"github.com/hanzoai/o11y/pkg/identn"
|
|
"github.com/hanzoai/o11y/pkg/sharder"
|
|
"github.com/hanzoai/o11y/pkg/types"
|
|
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
|
"github.com/hanzoai/o11y/pkg/types/ctxtypes"
|
|
"github.com/hanzoai/o11y/pkg/valuer"
|
|
)
|
|
|
|
const (
|
|
identityCrossOrgMessage string = "::IDENTITY-CROSS-ORG::"
|
|
)
|
|
|
|
type IdentN struct {
|
|
resolver identn.IdentNResolver
|
|
sharder sharder.Sharder
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewIdentN(resolver identn.IdentNResolver, sharder sharder.Sharder, logger *slog.Logger) *IdentN {
|
|
return &IdentN{
|
|
resolver: resolver,
|
|
sharder: sharder,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (m *IdentN) Wrap(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
idn := m.resolver.GetIdentN(r)
|
|
if idn == nil {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
if pre, ok := idn.(identn.IdentNWithPreHook); ok {
|
|
r = pre.Pre(r)
|
|
}
|
|
|
|
identity, err := idn.GetIdentity(r)
|
|
if err != nil {
|
|
// Surface the identity-resolution failure instead of silently dropping to a
|
|
// downstream generic 401 — a swallowed provision/auth error is undebuggable.
|
|
m.logger.ErrorContext(r.Context(), "::IDENTITY-GET-FAILED::", errors.Attr(err))
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
claims := identity.ToClaims()
|
|
if err := m.sharder.IsMyOwnedKey(ctx, types.NewOrganizationKey(valuer.MustNewUUID(claims.OrgID))); err != nil {
|
|
m.logger.ErrorContext(ctx, identityCrossOrgMessage, slog.Any("claims", claims), errors.Attr(err))
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
ctx = authtypes.NewContextWithClaims(ctx, claims)
|
|
|
|
comment := ctxtypes.CommentFromContext(ctx)
|
|
comment.Set("identn_provider", claims.IdentNProvider.StringValue())
|
|
comment.Set("user_id", claims.UserID)
|
|
comment.Set("service_account_id", claims.ServiceAccountID)
|
|
comment.Set("principal", claims.Principal.StringValue())
|
|
comment.Set("org_id", claims.OrgID)
|
|
ctx = ctxtypes.NewContextWithComment(ctx, comment)
|
|
|
|
r = r.WithContext(ctx)
|
|
next.ServeHTTP(w, r)
|
|
|
|
if hook, ok := idn.(identn.IdentNWithPostHook); ok {
|
|
hook.Post(context.WithoutCancel(r.Context()), r, claims)
|
|
}
|
|
})
|
|
}
|