unistore: handle auth when fallback is used (#96772)

* handle auth when fallback is used

* handle auth when fallback is used

* add traces
pull/96824/head
Georges Chaudy 7 months ago committed by GitHub
parent ef3759449d
commit 8bb59c64f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      pkg/services/authn/grpcutils/grpc_authenticator.go
  2. 52
      pkg/storage/unified/resource/access.go
  3. 4
      pkg/storage/unified/resource/access_test.go
  4. 2
      pkg/storage/unified/sql/server.go

@ -66,6 +66,8 @@ func NewGrpcAuthenticator(cfg *setting.Cfg, tracer tracing.Tracer) (*authnlib.Gr
)
}
type contextFallbackKey struct{}
type AuthenticatorWithFallback struct {
authenticator *authnlib.GrpcAuthenticator
fallback interceptors.Authenticator
@ -96,6 +98,10 @@ func NewGrpcAuthenticatorWithFallback(cfg *setting.Cfg, reg prometheus.Registere
}, nil
}
func FallbackUsed(ctx context.Context) bool {
return ctx.Value(contextFallbackKey{}) != nil
}
func (f *AuthenticatorWithFallback) Authenticate(ctx context.Context) (context.Context, error) {
ctx, span := f.tracer.Start(ctx, "grpcutils.AuthenticatorWithFallback.Authenticate")
defer span.End()
@ -107,6 +113,7 @@ func (f *AuthenticatorWithFallback) Authenticate(ctx context.Context) (context.C
newCtx, err = f.fallback.Authenticate(ctx)
f.metrics.fallbackCounter.WithLabelValues(fmt.Sprintf("%t", err == nil)).Inc()
span.SetAttributes(attribute.Bool("fallback_used", true))
newCtx = context.WithValue(newCtx, contextFallbackKey{}, true)
}
return newCtx, err
}

@ -7,6 +7,10 @@ import (
"github.com/grafana/authlib/authz"
"github.com/grafana/authlib/claims"
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
)
type staticAuthzClient struct {
@ -38,11 +42,19 @@ type authzLimitedClient struct {
// whitelist is a map of group to resources that are compatible with RBAC.
whitelist groupResource
logger *slog.Logger
tracer trace.Tracer
}
type AuthzOptions struct {
Tracer trace.Tracer
}
// NewAuthzLimitedClient creates a new authzLimitedClient.
func NewAuthzLimitedClient(client authz.AccessChecker) authz.AccessClient {
func NewAuthzLimitedClient(client authz.AccessChecker, opts AuthzOptions) authz.AccessClient {
logger := slog.Default().With("logger", "limited-authz-client")
if opts.Tracer == nil {
opts.Tracer = noop.NewTracerProvider().Tracer("limited-authz-client")
}
return &authzLimitedClient{
client: client,
whitelist: groupResource{
@ -50,31 +62,57 @@ func NewAuthzLimitedClient(client authz.AccessChecker) authz.AccessClient {
"folder.grafana.app": map[string]interface{}{"folders": nil},
},
logger: logger,
tracer: opts.Tracer,
}
}
// Check implements authz.AccessClient.
func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req authz.CheckRequest) (authz.CheckResponse, error) {
ctx, span := c.tracer.Start(ctx, "authzLimitedClient.Check", trace.WithAttributes(
attribute.String("group", req.Group),
attribute.String("resource", req.Resource),
attribute.Bool("fallback", grpcutils.FallbackUsed(ctx)),
))
defer span.End()
if grpcutils.FallbackUsed(ctx) {
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "fallback", true, "rbac", false, "allowed", true)
return authz.CheckResponse{Allowed: true}, nil
}
if !c.IsCompatibleWithRBAC(req.Group, req.Resource) {
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "rbac", false, "allowed", true)
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "fallback", false, "rbac", false, "allowed", true)
return authz.CheckResponse{Allowed: true}, nil
}
t := time.Now()
resp, err := c.client.Check(ctx, id, req)
if err != nil {
c.logger.Error("Check", "group", req.Group, "resource", req.Resource, "rbac", true, "error", err, "duration", time.Since(t))
c.logger.Error("Check", "group", req.Group, "resource", req.Resource, "fallback", false, "rbac", true, "error", err, "duration", time.Since(t))
return resp, err
}
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "rbac", true, "allowed", resp.Allowed, "duration", time.Since(t))
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "fallback", false, "rbac", true, "allowed", resp.Allowed, "duration", time.Since(t))
return resp, nil
}
// Compile implements authz.AccessClient.
func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req authz.ListRequest) (authz.ItemChecker, error) {
ctx, span := c.tracer.Start(ctx, "authzLimitedClient.Compile", trace.WithAttributes(
attribute.String("group", req.Group),
attribute.String("resource", req.Resource),
))
defer span.End()
return func(namespace string, name, folder string) bool {
ctx, span := c.tracer.Start(ctx, "authzLimitedClient.Compile.Check", trace.WithAttributes(
attribute.String("group", req.Group),
attribute.String("resource", req.Resource),
attribute.Bool("fallback", grpcutils.FallbackUsed(ctx)),
))
defer span.End()
if grpcutils.FallbackUsed(ctx) {
c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "fallback", true, "rbac", false, "allowed", true)
return true
}
// TODO: Implement For now we perform the check for each item.
if !c.IsCompatibleWithRBAC(req.Group, req.Resource) {
c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", false, "allowed", true)
c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "fallback", false, "rbac", false, "allowed", true)
return true
}
t := time.Now()
@ -87,10 +125,10 @@ func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req
Folder: folder,
})
if err != nil {
c.logger.Error("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", true, "error", err, "duration", time.Since(t))
c.logger.Error("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "fallback", false, "rbac", true, "error", err, "duration", time.Since(t))
return false
}
c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", true, "allowed", r.Allowed, "duration", time.Since(t))
c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "fallback", false, "rbac", true, "allowed", r.Allowed, "duration", time.Since(t))
return r.Allowed
}, nil
}

@ -10,7 +10,7 @@ import (
func TestAuthzLimitedClient_Check(t *testing.T) {
mockClient := &staticAuthzClient{allowed: false}
client := NewAuthzLimitedClient(mockClient)
client := NewAuthzLimitedClient(mockClient, AuthzOptions{})
tests := []struct {
group string
@ -35,7 +35,7 @@ func TestAuthzLimitedClient_Check(t *testing.T) {
func TestAuthzLimitedClient_Compile(t *testing.T) {
mockClient := &staticAuthzClient{allowed: false}
client := NewAuthzLimitedClient(mockClient)
client := NewAuthzLimitedClient(mockClient, AuthzOptions{})
tests := []struct {
group string

@ -31,7 +31,7 @@ func NewResourceServer(ctx context.Context, db infraDB.DB, cfg *setting.Cfg,
Reg: reg,
}
if ac != nil {
opts.AccessClient = resource.NewAuthzLimitedClient(ac)
opts.AccessClient = resource.NewAuthzLimitedClient(ac, resource.AuthzOptions{Tracer: tracer})
}
// Support local file blob
if strings.HasPrefix(opts.Blob.URL, "./data/") {

Loading…
Cancel
Save