Auth: id response header (#77871)

* Add config options for identity id response header

* Add feature to add identity id response header to all responses

* Use util.SplitString
This commit is contained in:
Karl Persson 2023-11-29 15:51:13 +01:00 committed by GitHub
parent 27b7d1de6f
commit 21f94c5b78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 0 deletions

View File

@ -565,6 +565,17 @@ azure_auth_enabled = false
# Use email lookup in addition to the unique ID provided by the IdP
oauth_allow_insecure_email_lookup = false
# Set to true to include id of identity as a response header
id_response_header_enabled = false
# Prefix used for the id response header, X-Grafana-Identity-Id
id_response_header_prefix = X-Grafana
# List of identity namespaces to add id response headers for, separated by space.
# Available namespaces are user, api-key and service-account.
# The header value will encode the namespace ("user:<id>", "api-key:<id>", "service-account:<id>")
id_response_header_namespaces = user api-key service-account
#################################### Anonymous Auth ######################
[auth.anonymous]
# enable anonymous access

View File

@ -554,6 +554,17 @@
# Use email lookup in addition to the unique ID provided by the IdP
;oauth_allow_insecure_email_lookup = false
# Set to true to include id of identity as a response header
;id_response_header_enabled = false
# Prefix used for the id response header, X-Grafana-Identity-Id
;id_response_header_prefix = X-Grafana
# List of identity namespaces to add id response headers for, separated by space.
# Available namespaces are user, api-key and service-account.
# The header value will encode the namespace ("user:<id>", "api-key:<id>", "service-account:<id>")
;id_response_header_namespaces = user api-key service-account
#################################### Anonymous Auth ######################
[auth.anonymous]
# enable anonymous access

View File

@ -4,7 +4,9 @@ package contexthandler
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
@ -135,10 +137,51 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
attribute.Int64("userId", reqContext.UserID),
))
if h.Cfg.IDResponseHeaderEnabled && reqContext.SignedInUser != nil {
namespace, id := getNamespaceAndID(reqContext.SignedInUser)
reqContext.Resp.Before(h.addIDHeaderEndOfRequestFunc(namespace, id))
}
next.ServeHTTP(w, r)
})
}
// TODO(kalleep): Refactor to user identity.Requester interface and methods after we have backported this
func getNamespaceAndID(user *user.SignedInUser) (string, string) {
var namespace, id string
if user.UserID > 0 && user.IsServiceAccount {
id = strconv.Itoa(int(user.UserID))
namespace = "service-account"
} else if user.UserID > 0 {
id = strconv.Itoa(int(user.UserID))
namespace = "user"
} else if user.ApiKeyID > 0 {
id = strconv.Itoa(int(user.ApiKeyID))
namespace = "api-key"
}
return namespace, id
}
func (h *ContextHandler) addIDHeaderEndOfRequestFunc(namespace, id string) web.BeforeFunc {
return func(w web.ResponseWriter) {
if w.Written() {
return
}
if namespace == "" || id == "" {
return
}
if _, ok := h.Cfg.IDResponseHeaderNamespaces[namespace]; !ok {
return
}
headerName := fmt.Sprintf("%s-Identity-Id", h.Cfg.IDResponseHeaderPrefix)
w.Header().Add(headerName, fmt.Sprintf("%s:%s", namespace, id))
}
}
func (h *ContextHandler) deleteInvalidCookieEndOfRequestFunc(reqContext *contextmodel.ReqContext) web.BeforeFunc {
return func(w web.ResponseWriter) {
if h.features.IsEnabled(reqContext.Req.Context(), featuremgmt.FlagClientTokenRotation) {

View File

@ -285,6 +285,9 @@ type Cfg struct {
AdminEmail string
DisableLoginForm bool
SignoutRedirectUrl string
IDResponseHeaderEnabled bool
IDResponseHeaderPrefix string
IDResponseHeaderNamespaces map[string]struct{}
// Not documented & not supported
// stand in until a more complete solution is implemented
AuthConfigUIAdminAccess bool
@ -1607,6 +1610,17 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
// Azure Auth
AzureAuthEnabled = auth.Key("azure_auth_enabled").MustBool(false)
cfg.AzureAuthEnabled = AzureAuthEnabled
// ID response header
cfg.IDResponseHeaderEnabled = auth.Key("id_response_header_enabled").MustBool(false)
cfg.IDResponseHeaderPrefix = auth.Key("id_response_header_prefix").MustString("X-Grafana-")
idHeaderNamespaces := util.SplitString(auth.Key("id_response_header_namespaces").MustString(""))
cfg.IDResponseHeaderNamespaces = make(map[string]struct{}, len(idHeaderNamespaces))
for _, namespace := range idHeaderNamespaces {
cfg.IDResponseHeaderNamespaces[namespace] = struct{}{}
}
readAuthAzureADSettings(cfg)
// Google Auth