Context: Clean up contexthandler.Middleware spans (#108188)

* Context: Clean up contexthandler.Middleware spans

* seperate the middleware span from the rest of the request

* shuffle

* put of back

* span name

* Update pkg/services/contexthandler/contexthandler.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

---------

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
This commit is contained in:
Josh Hunt
2025-07-22 08:53:28 +00:00
committed by GitHub
co-authored by Dave Henderson
parent abd519899f
commit c3243bd100
+73 -65
View File
@@ -6,11 +6,11 @@ import (
"fmt"
"net/http"
"github.com/open-feature/go-sdk/openfeature"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
claims "github.com/grafana/authlib/types"
"github.com/open-feature/go-sdk/openfeature"
authnClients "github.com/grafana/grafana/pkg/services/authn/clients"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -88,77 +88,85 @@ func CopyWithReqContext(ctx context.Context) context.Context {
func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Don't modify context so that the auth middleware span doesn't get propagated as a parent elsewhere
_, span := tracing.Start(ctx, "Auth - Middleware")
reqContext := &contextmodel.ReqContext{
Context: web.FromContext(ctx),
SignedInUser: &user.SignedInUser{
Permissions: map[int64]map[string][]string{},
},
IsSignedIn: false,
AllowAnonymous: false,
SkipDSCache: false,
Logger: log.New("context"),
UseSessionStorageRedirect: h.features.IsEnabledGlobally(featuremgmt.FlagUseSessionStorageForRedirection),
}
span := trace.SpanFromContext(ctx)
ctx = h.setRequestContext(ctx)
// inject ReqContext in the context
ctx = context.WithValue(ctx, reqContextKey{}, reqContext)
// store list of possible auth header in context
ctx = WithAuthHTTPHeaders(ctx, h.cfg)
// Set the context for the http.Request.Context
// This modifies both r and reqContext.Req since they point to the same value
*reqContext.Req = *reqContext.Req.WithContext(ctx)
// Preserve the original span so the setRequestContext span doesn't get propagated as a parent of the rest of the request
ctx = trace.ContextWithSpan(ctx, span)
ctx = trace.ContextWithSpan(reqContext.Req.Context(), span)
traceID := tracing.TraceIDFromContext(ctx, false)
if traceID != "" {
reqContext.Logger = reqContext.Logger.New("traceID", traceID)
}
id, err := h.authenticator.Authenticate(ctx, &authn.Request{HTTPRequest: reqContext.Req})
if err != nil {
// Hack: set all errors on LookupTokenErr, so we can check it in auth middlewares
reqContext.LookupTokenErr = err
} else {
reqContext.SignedInUser = id.SignedInUser()
reqContext.UserToken = id.SessionToken
reqContext.IsSignedIn = !reqContext.IsAnonymous
reqContext.AllowAnonymous = reqContext.IsAnonymous
reqContext.IsRenderCall = id.IsAuthenticatedBy(login.RenderModule)
ctx = identity.WithRequester(ctx, id)
}
h.excludeSensitiveHeadersFromRequest(reqContext.Req)
reqContext.Logger = reqContext.Logger.New("userId", reqContext.UserID, "orgId", reqContext.OrgID, "uname", reqContext.Login)
span.AddEvent("user", trace.WithAttributes(
attribute.String("uname", reqContext.Login),
attribute.Int64("orgId", reqContext.OrgID),
attribute.Int64("userId", reqContext.UserID),
))
if h.cfg.IDResponseHeaderEnabled && reqContext.SignedInUser != nil {
reqContext.Resp.Before(h.addIDHeaderEndOfRequestFunc(reqContext.SignedInUser))
}
// Set open feature evaluation context with namespace
ns := "default"
if id != nil {
ns = id.Namespace
}
evalCtx := openfeature.NewEvaluationContext(ns, map[string]any{
"namespace": ns,
})
ctx = openfeature.MergeTransactionContext(ctx, evalCtx)
// End the span to make next handlers not wrapped within middleware span
span.End()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (h *ContextHandler) setRequestContext(ctx context.Context) context.Context {
ctx, span := tracing.Start(ctx, "ContextHandler.setRequestContext")
defer span.End()
reqContext := &contextmodel.ReqContext{
Context: web.FromContext(ctx),
SignedInUser: &user.SignedInUser{
Permissions: map[int64]map[string][]string{},
},
IsSignedIn: false,
AllowAnonymous: false,
SkipDSCache: false,
Logger: log.New("context"),
UseSessionStorageRedirect: h.features.IsEnabledGlobally(featuremgmt.FlagUseSessionStorageForRedirection),
}
// inject ReqContext in the context
ctx = context.WithValue(ctx, reqContextKey{}, reqContext)
// store list of possible auth header in context
ctx = WithAuthHTTPHeaders(ctx, h.cfg)
// Set the context for the http.Request.Context
// This modifies both r and reqContext.Req since they point to the same value
*reqContext.Req = *reqContext.Req.WithContext(ctx)
traceID := tracing.TraceIDFromContext(ctx, false)
if traceID != "" {
reqContext.Logger = reqContext.Logger.New("traceID", traceID)
}
id, err := h.authenticator.Authenticate(ctx, &authn.Request{HTTPRequest: reqContext.Req})
if err != nil {
// Hack: set all errors on LookupTokenErr, so we can check it in auth middlewares
reqContext.LookupTokenErr = err
} else {
reqContext.SignedInUser = id.SignedInUser()
reqContext.UserToken = id.SessionToken
reqContext.IsSignedIn = !reqContext.IsAnonymous
reqContext.AllowAnonymous = reqContext.IsAnonymous
reqContext.IsRenderCall = id.IsAuthenticatedBy(login.RenderModule)
ctx = identity.WithRequester(ctx, id)
}
h.excludeSensitiveHeadersFromRequest(reqContext.Req)
reqContext.Logger = reqContext.Logger.New("userId", reqContext.UserID, "orgId", reqContext.OrgID, "uname", reqContext.Login)
span.AddEvent("user", trace.WithAttributes(
attribute.String("uname", reqContext.Login),
attribute.Int64("orgId", reqContext.OrgID),
attribute.Int64("userId", reqContext.UserID),
))
if h.cfg.IDResponseHeaderEnabled && reqContext.SignedInUser != nil {
reqContext.Resp.Before(h.addIDHeaderEndOfRequestFunc(reqContext.SignedInUser))
}
// Set open feature evaluation context with namespace
ns := "default"
if id != nil {
ns = id.Namespace
}
evalCtx := openfeature.NewEvaluationContext(ns, map[string]any{
"namespace": ns,
})
ctx = openfeature.MergeTransactionContext(ctx, evalCtx)
return ctx
}
func (h *ContextHandler) excludeSensitiveHeadersFromRequest(req *http.Request) {
req.Header.Del(authnClients.ExtJWTAuthenticationHeaderName)
req.Header.Del(authnClients.ExtJWTAuthorizationHeaderName)