K8s: use contexthandler in standalone handler chain (#90102)

pull/90210/head
Charandas 1 year ago committed by GitHub
parent 3b6a8775bb
commit c210617735
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      pkg/api/common_test.go
  2. 3
      pkg/apimachinery/identity/requester.go
  3. 5
      pkg/apimachinery/identity/static.go
  4. 3
      pkg/apiserver/endpoints/filters/requester.go
  5. 10
      pkg/cmd/grafana/apiserver/cmd.go
  6. 3
      pkg/cmd/grafana/apiserver/server.go
  7. 2
      pkg/middleware/auth_test.go
  8. 3
      pkg/middleware/middleware_test.go
  9. 1
      pkg/server/wire.go
  10. 10
      pkg/services/apiserver/builder/helper.go
  11. 8
      pkg/services/apiserver/standalone/factory.go
  12. 9
      pkg/services/authn/authn.go
  13. 5
      pkg/services/authn/authnimpl/service.go
  14. 20
      pkg/services/authn/clients/ext_jwt.go
  15. 45
      pkg/services/authn/clients/ext_jwt_test.go
  16. 6
      pkg/services/authn/identity.go
  17. 19
      pkg/services/contexthandler/contexthandler.go
  18. 7
      pkg/services/contexthandler/contexthandler_test.go
  19. 6
      pkg/services/user/identity.go
  20. 8
      pkg/setting/setting_jwt.go
  21. 11
      pkg/web/macaron.go

@ -189,7 +189,6 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
return contexthandler.ProvideService(
cfg,
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: &authn.Identity{ID: authn.AnonymousNamespaceID, SessionToken: &usertoken.UserToken{}}},
)
}

@ -43,6 +43,9 @@ type Requester interface {
GetOrgName() string
// GetAuthID returns external id for entity.
GetAuthID() string
// GetAllowedKubernetesNamespace returns either "*" or the single namespace this requester has access to
// An empty value means the implementation has not specified a kubernetes namespace.
GetAllowedKubernetesNamespace() string
// GetAuthenticatedBy returns the authentication method used to authenticate the entity.
GetAuthenticatedBy() string
// IsAuthenticatedBy returns true if entity was authenticated by any of supplied providers.

@ -22,6 +22,7 @@ type StaticRequester struct {
EmailVerified bool
AuthID string
AuthenticatedBy string
AllowedKubernetesNamespace string
IsGrafanaAdmin bool
// Permissions grouped by orgID and actions
Permissions map[int64]map[string][]string
@ -123,6 +124,10 @@ func (u *StaticRequester) GetAuthID() string {
return u.AuthID
}
func (u *StaticRequester) GetAllowedKubernetesNamespace() string {
return u.AllowedKubernetesNamespace
}
func (u *StaticRequester) GetAuthenticatedBy() string {
return u.AuthenticatedBy
}

@ -39,7 +39,10 @@ func WithRequester(handler http.Handler) http.Handler {
Name: info.GetName(),
Login: info.GetName(),
OrgRole: identity.RoleAdmin,
IsGrafanaAdmin: true,
AllowedKubernetesNamespace: "default",
Permissions: map[int64]map[string][]string{
orgId: {
"*": {"*"}, // all resources, all scopes

@ -74,15 +74,15 @@ func newCommandStartExampleAPIServer(o *APIServerOptions, stopCh <-chan struct{}
return err
}
config, err := o.Config()
if err != nil {
return err
}
if o.Options.TracingOptions.TracingService != nil {
tracer.InitTracer(o.Options.TracingOptions.TracingService)
}
config, err := o.Config(tracer)
if err != nil {
return err
}
defer o.factory.Shutdown()
if err := o.RunAPIServer(config, stopCh); err != nil {

@ -76,7 +76,7 @@ func (o *APIServerOptions) loadAPIGroupBuilders(ctx context.Context, tracer trac
return nil
}
func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error) {
func (o *APIServerOptions) Config(tracer tracing.Tracer) (*genericapiserver.RecommendedConfig, error) {
if err := o.Options.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
"localhost", o.AlternateDNS, []net.IP{netutils.ParseIPSloppy("127.0.0.1")},
); err != nil {
@ -122,6 +122,7 @@ func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error)
setting.BuildVersion,
setting.BuildCommit,
setting.BuildBranch,
o.factory.GetOptionalMiddlewares(tracer)...,
)
return serverConfig, err
}

@ -26,7 +26,7 @@ import (
)
func setupAuthMiddlewareTest(t *testing.T, identity *authn.Identity, authErr error) *contexthandler.ContextHandler {
return contexthandler.ProvideService(setting.NewCfg(), tracing.InitializeTracerForTest(), featuremgmt.WithFeatures(), &authntest.FakeService{
return contexthandler.ProvideService(setting.NewCfg(), tracing.InitializeTracerForTest(), &authntest.FakeService{
ExpectedErr: authErr,
ExpectedIdentity: identity,
})

@ -19,7 +19,6 @@ import (
"github.com/grafana/grafana/pkg/services/authn/authntest"
"github.com/grafana/grafana/pkg/services/contexthandler"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/navtree"
"github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting"
@ -273,5 +272,5 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg, authnService authn.Servic
t.Helper()
tracer := tracing.InitializeTracerForTest()
return contexthandler.ProvideService(cfg, tracer, featuremgmt.WithFeatures(), authnService)
return contexthandler.ProvideService(cfg, tracer, authnService)
}

@ -360,6 +360,7 @@ var wireBasicSet = wire.NewSet(
authnimpl.ProvideService,
authnimpl.ProvideIdentitySynchronizer,
authnimpl.ProvideAuthnService,
authnimpl.ProvideAuthnServiceAuthenticateOnly,
authnimpl.ProvideRegistration,
supportbundlesimpl.ProvideService,
extsvcaccounts.ProvideExtSvcAccountsService,

@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/grafana/grafana/pkg/web"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/mod/semver"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -53,6 +54,7 @@ func SetupConfig(
buildVersion string,
buildCommit string,
buildBranch string,
optionalMiddlewares ...web.Middleware,
) error {
defsGetter := GetOpenAPIDefinitions(builders)
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(
@ -98,6 +100,14 @@ func SetupConfig(
handler := filters.WithTracingHTTPLoggingAttributes(requestHandler)
handler = filters.WithRequester(handler)
handler = genericapiserver.DefaultBuildHandlerChain(handler, c)
// If optional middlewares include auth function, they need to happen before DefaultBuildHandlerChain
if len(optionalMiddlewares) > 0 {
for _, m := range optionalMiddlewares {
handler = m(handler)
}
}
handler = filters.WithAcceptHeader(handler)
handler = filters.WithPathRewriters(handler, pathRewriters)
handler = k8stracing.WithTracing(handler, serverConfig.TracerProvider, "KubernetesAPI")

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/web"
"github.com/prometheus/client_golang/prometheus"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -33,6 +34,9 @@ type APIServerFactory interface {
// Given the flags, what can we produce
GetEnabled(runtime []RuntimeConfig) ([]schema.GroupVersion, error)
// Any optional middlewares this factory wants configured via apiserver's BuildHandlerChain facility
GetOptionalMiddlewares(tracer tracing.Tracer) []web.Middleware
// Make an API server for a given group+version
MakeAPIServer(ctx context.Context, tracer tracing.Tracer, gv schema.GroupVersion) (builder.APIGroupBuilder, error)
@ -50,6 +54,10 @@ func (p *DummyAPIFactory) GetOptions() options.OptionsProvider {
return nil
}
func (p *DummyAPIFactory) GetOptionalMiddlewares(_ tracing.Tracer) []web.Middleware {
return []web.Middleware{}
}
func (p *DummyAPIFactory) GetEnabled(runtime []RuntimeConfig) ([]schema.GroupVersion, error) {
gv := []schema.GroupVersion{}
for _, cfg := range runtime {

@ -73,9 +73,13 @@ type PostAuthHookFn func(ctx context.Context, identity *Identity, r *Request) er
type PostLoginHookFn func(ctx context.Context, identity *Identity, r *Request, err error)
type PreLogoutHookFn func(ctx context.Context, requester identity.Requester, sessionToken *usertoken.UserToken) error
type Service interface {
type Authenticator interface {
// Authenticate authenticates a request
Authenticate(ctx context.Context, r *Request) (*Identity, error)
}
type Service interface {
Authenticator
// RegisterPostAuthHook registers a hook with a priority that is called after a successful authentication.
// A lower number means higher priority.
RegisterPostAuthHook(hook PostAuthHookFn, priority uint)
@ -115,10 +119,9 @@ type IdentitySynchronizer interface {
}
type Client interface {
Authenticator
// Name returns the name of a client
Name() string
// Authenticate performs the authentication for the request
Authenticate(ctx context.Context, r *Request) (*Identity, error)
// IsEnabled returns the enabled status of the client
IsEnabled() bool
}

@ -39,6 +39,11 @@ func ProvideAuthnService(s *Service) authn.Service {
return s
}
// make sure service also implements authn.ServiceAuthenticateOnly interface
func ProvideAuthnServiceAuthenticateOnly(s *Service) authn.Authenticator {
return s
}
// make sure service implements authn.IdentitySynchronizer interface
func ProvideIdentitySynchronizer(s *Service) authn.IdentitySynchronizer {
return s

@ -20,9 +20,8 @@ import (
var _ authn.Client = new(ExtendedJWT)
const (
extJWTAuthenticationHeaderName = "X-Access-Token"
extJWTAuthorizationHeaderName = "X-Grafana-Id"
extJWTAccessTokenExpectAudience = "grafana"
ExtJWTAuthenticationHeaderName = "X-Access-Token"
ExtJWTAuthorizationHeaderName = "X-Grafana-Id"
)
var (
@ -46,7 +45,7 @@ func ProvideExtendedJWT(cfg *setting.Cfg) *ExtendedJWT {
})
accessTokenVerifier := authlib.NewAccessTokenVerifier(authlib.VerifierConfig{
AllowedAudiences: []string{extJWTAccessTokenExpectAudience},
AllowedAudiences: cfg.ExtJWTAuth.Audiences,
}, keys)
// For ID tokens, we explicitly do not validate audience, hence an empty AllowedAudiences
@ -129,11 +128,19 @@ func (s *ExtendedJWT) authenticateAsUser(
return nil, errExtJWTInvalidSubject.Errorf("unexpected identity: %s", userID.String())
}
// For use in service layer, allow higher privilege
allowedKubernetesNamespace := accessTokenClaims.Rest.Namespace
if len(s.cfg.StackID) > 0 {
// For single-tenant cloud use, choose the lower of the two (id token will always have the specific namespace)
allowedKubernetesNamespace = idTokenClaims.Rest.Namespace
}
return &authn.Identity{
ID: userID,
OrgID: s.getDefaultOrgID(),
AuthenticatedBy: login.ExtendedJWTModule,
AuthID: accessID.String(),
AllowedKubernetesNamespace: allowedKubernetesNamespace,
ClientParams: authn.ClientParams{
SyncPermissions: true,
FetchPermissionsParams: authn.FetchPermissionsParams{
@ -164,6 +171,7 @@ func (s *ExtendedJWT) authenticateAsService(claims *authlib.Claims[authlib.Acces
OrgID: s.getDefaultOrgID(),
AuthenticatedBy: login.ExtendedJWTModule,
AuthID: claims.Subject,
AllowedKubernetesNamespace: claims.Rest.Namespace,
ClientParams: authn.ClientParams{
SyncPermissions: true,
FetchPermissionsParams: authn.FetchPermissionsParams{
@ -208,7 +216,7 @@ func (s *ExtendedJWT) Priority() uint {
// retrieveAuthenticationToken retrieves the JWT token from the request.
func (s *ExtendedJWT) retrieveAuthenticationToken(httpRequest *http.Request) string {
jwtToken := httpRequest.Header.Get(extJWTAuthenticationHeaderName)
jwtToken := httpRequest.Header.Get(ExtJWTAuthenticationHeaderName)
// Strip the 'Bearer' prefix if it exists.
return strings.TrimPrefix(jwtToken, "Bearer ")
@ -216,7 +224,7 @@ func (s *ExtendedJWT) retrieveAuthenticationToken(httpRequest *http.Request) str
// retrieveAuthorizationToken retrieves the JWT token from the request.
func (s *ExtendedJWT) retrieveAuthorizationToken(httpRequest *http.Request) string {
jwtToken := httpRequest.Header.Get(extJWTAuthorizationHeaderName)
jwtToken := httpRequest.Header.Get(ExtJWTAuthorizationHeaderName)
// Strip the 'Bearer' prefix if it exists.
return strings.TrimPrefix(jwtToken, "Bearer ")

@ -51,6 +51,17 @@ var (
Namespace: "default", // org ID of 1 is special and translates to default
},
}
validIDTokenClaimsWithStackSet = idTokenClaims{
Claims: &jwt.Claims{
Subject: "user:2",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
},
Rest: authnlib.IDTokenClaims{
AuthenticatedBy: "extended_jwt",
Namespace: "stack-1234",
},
}
validAcessTokenClaimsWildcard = accessTokenClaims{
Claims: &jwt.Claims{
Subject: "access-policy:this-uid",
@ -183,6 +194,7 @@ func TestExtendedJWT_Test(t *testing.T) {
func TestExtendedJWT_Authenticate(t *testing.T) {
type testCase struct {
name string
cfg *setting.Cfg // optional, only used when overriding the cfg provided by default test setup
accessToken *accessTokenClaims
idToken *idTokenClaims
orgID int64
@ -198,6 +210,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
ID: authn.MustParseNamespaceID("access-policy:this-uid"),
UID: authn.MustParseNamespaceID("access-policy:this-uid"),
OrgID: 1,
AllowedKubernetesNamespace: "default",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -213,6 +226,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
ID: authn.MustParseNamespaceID("access-policy:this-uid"),
UID: authn.MustParseNamespaceID("access-policy:this-uid"),
OrgID: 1,
AllowedKubernetesNamespace: "*",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -228,6 +242,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
want: &authn.Identity{
ID: authn.MustParseNamespaceID("user:2"),
OrgID: 1,
AllowedKubernetesNamespace: "default",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -247,6 +262,32 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
want: &authn.Identity{
ID: authn.MustParseNamespaceID("user:2"),
OrgID: 1,
AllowedKubernetesNamespace: "*",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
FetchSyncedUser: true,
SyncPermissions: true,
},
},
},
{
name: "should authenticate as user using wildcard namespace for access token, setting allowed namespace to specific",
accessToken: &validAcessTokenClaimsWildcard,
idToken: &validIDTokenClaimsWithStackSet,
orgID: 1,
cfg: &setting.Cfg{
// default org set up by the authenticator is 1
StackID: "1234",
ExtJWTAuth: setting.ExtJWTSettings{
Enabled: true,
ExpectIssuer: "http://localhost:3000",
},
},
want: &authn.Identity{
ID: authn.MustParseNamespaceID("user:2"),
OrgID: 1,
AllowedKubernetesNamespace: "stack-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -301,7 +342,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
env := setupTestCtx(nil)
env := setupTestCtx(tc.cfg)
validHTTPReq := &http.Request{
Header: map[string][]string{
@ -313,7 +354,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
if tc.idToken != nil {
env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.accessToken}
env.s.idTokenVerifier = &mockIDVerifier{Claims: *tc.idToken}
validHTTPReq.Header.Add(extJWTAuthorizationHeaderName, generateIDToken(*tc.idToken, pk, jose.RS256))
validHTTPReq.Header.Add(ExtJWTAuthorizationHeaderName, generateIDToken(*tc.idToken, pk, jose.RS256))
}
id, err := env.s.Authenticate(context.Background(), &authn.Request{

@ -47,6 +47,8 @@ type Identity struct {
// AuthId is the unique identifier for the entity in the external system.
// Empty if the identity is provided by Grafana.
AuthID string
// AllowedKubernetesNamespace
AllowedKubernetesNamespace string
// IsDisabled is true if the entity is disabled.
IsDisabled bool
// HelpFlags1 is the help flags for the entity.
@ -127,6 +129,10 @@ func (i *Identity) GetLogin() string {
return i.Login
}
func (i *Identity) GetAllowedKubernetesNamespace() string {
return i.AllowedKubernetesNamespace
}
func (i *Identity) GetOrgID() int64 {
return i.OrgID
}

@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
authnClients "github.com/grafana/grafana/pkg/services/authn/clients"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
@ -16,20 +17,18 @@ import (
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
)
func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features featuremgmt.FeatureToggles, authnService authn.Service,
func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, authenticator authn.Authenticator,
) *ContextHandler {
return &ContextHandler{
Cfg: cfg,
tracer: tracer,
features: features,
authnService: authnService,
authenticator: authenticator,
}
}
@ -37,8 +36,7 @@ func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features featuremgm
type ContextHandler struct {
Cfg *setting.Cfg
tracer tracing.Tracer
features featuremgmt.FeatureToggles
authnService authn.Service
authenticator authn.Authenticator
}
type reqContextKey = ctxkey.Key
@ -112,7 +110,7 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
reqContext.Logger = reqContext.Logger.New("traceID", traceID)
}
id, err := h.authnService.Authenticate(ctx, &authn.Request{HTTPRequest: reqContext.Req})
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
@ -124,6 +122,8 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
reqContext.IsRenderCall = id.IsAuthenticatedBy(login.RenderModule)
}
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),
@ -142,6 +142,11 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
})
}
func (h *ContextHandler) excludeSensitiveHeadersFromRequest(req *http.Request) {
req.Header.Del(authnClients.ExtJWTAuthenticationHeaderName)
req.Header.Del(authnClients.ExtJWTAuthorizationHeaderName)
}
func (h *ContextHandler) addIDHeaderEndOfRequestFunc(ident identity.Requester) web.BeforeFunc {
return func(w web.ResponseWriter) {
if w.Written() {

@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/services/authn/authntest"
"github.com/grafana/grafana/pkg/services/contexthandler"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -27,7 +26,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
setting.NewCfg(),
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedErr: errors.New("some error")},
)
@ -49,7 +47,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
setting.NewCfg(),
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: id},
)
@ -75,7 +72,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
setting.NewCfg(),
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: identity},
)
@ -97,7 +93,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
setting.NewCfg(),
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: identity},
)
@ -128,7 +123,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
cfg,
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: &authn.Identity{}},
)
@ -154,7 +148,6 @@ func TestContextHandler(t *testing.T) {
handler := contexthandler.ProvideService(
cfg,
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID(id)}},
)

@ -28,6 +28,8 @@ type SignedInUser struct {
AuthID string
// AuthenticatedBy be set if user signed in using external method
AuthenticatedBy string
AllowedKubernetesNamespace string
ApiKeyID int64 `xorm:"api_key_id"`
IsServiceAccount bool `xorm:"is_service_account"`
IsGrafanaAdmin bool
@ -89,6 +91,10 @@ func (u *SignedInUser) HasUniqueId() bool {
return u.IsRealUser() || u.IsApiKeyUser() || u.IsServiceAccountUser()
}
func (u *SignedInUser) GetAllowedKubernetesNamespace() string {
return u.AllowedKubernetesNamespace
}
// GetCacheKey returns a unique key for the entity.
// Add an extra prefix to avoid collisions with other caches
func (u *SignedInUser) GetCacheKey() string {

@ -2,6 +2,10 @@ package setting
import "time"
const (
extJWTAccessTokenExpectAudience = "grafana"
)
type AuthJWTSettings struct {
// JWT Auth
Enabled bool
@ -29,6 +33,7 @@ type ExtJWTSettings struct {
Enabled bool
ExpectIssuer string
JWKSUrl string
Audiences []string
}
func (cfg *Cfg) readAuthExtJWTSettings() {
@ -36,6 +41,9 @@ func (cfg *Cfg) readAuthExtJWTSettings() {
jwtSettings := ExtJWTSettings{}
jwtSettings.Enabled = authExtendedJWT.Key("enabled").MustBool(false)
jwtSettings.JWKSUrl = authExtendedJWT.Key("jwks_url").MustString("")
// for Grafana, this is hard coded, but we leave it as a configurable param for other use-cases
jwtSettings.Audiences = []string{extJWTAccessTokenExpectAudience}
cfg.ExtJWTAuth = jwtSettings
}

@ -139,6 +139,17 @@ func mwFromHandler(handler Handler) Middleware {
}
}
// a convenience function that is provided for users of contexthandler package (standalone apiservers)
// who have an implicit dependency on Macron in context but don't want to take a dependency on
// router additionally
func EmptyMacronMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
m := New()
c := m.createContext(writer, request)
next.ServeHTTP(writer, c.Req) // since c.Req has the newer context attached
})
}
func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context {
// NOTE: we have to explicitly copy the middleware chain here to avoid
// passing a shared slice to the *Context, which leads to racy behavior in

Loading…
Cancel
Save