Authn: Sync authlib and update how we construct authn client interceptor (#101124)

* Sync authlib and update how we construct authn client interceptor

* Remove namespace from checker
This commit is contained in:
Karl Persson
2025-02-26 09:22:09 +01:00
committed by GitHub
parent a7ecb19c31
commit fa74d1c36d
25 changed files with 105 additions and 107 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ exclude k8s.io/client-go v12.0.0+incompatible
require (
github.com/bwmarrin/snowflake v0.3.0
github.com/google/uuid v1.6.0
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82
github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250220154326-6e5de80ef295
@@ -193,7 +193,7 @@ require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 // indirect
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 // indirect
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 // indirect
github.com/grafana/dataplane/sdata v0.0.9 // indirect
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect
github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect
+4 -4
View File
@@ -568,10 +568,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 h1:iQ0h/h+QoguSZDF+ZpPxcM/C+m1kjh+aXjMpxywowPA=
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 h1:NTMmow+74I3Jb033xhbRgWQS7A//5TDhiM4tl7bsVP4=
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7/go.mod h1:T3X4z0ejGfJOiOmZLFeKCRT/yxWJq/RtclAc/PHj/w4=
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 h1:EokLC5grHwLPs4tXW8T6E8187H1e5G9AP0QQ5B60HbA=
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 h1:FTuDRy/Shw8yOdG+v1DnkeuaCAl8fvwgcfaG9Wccuhg=
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 h1:DnRUYiAotHXnrfYJCvhH1NkiyWVcPm5Pd+P7Ugqt/d8=
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA=
github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg=
github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s=
+10 -15
View File
@@ -15,7 +15,6 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
authnlib "github.com/grafana/authlib/authn"
"github.com/grafana/authlib/types"
"github.com/grafana/dskit/flagext"
"github.com/grafana/dskit/grpcclient"
@@ -143,24 +142,20 @@ func newClient(opts options.StorageOptions,
}
}
func clientCfgMapping(clientCfg *grpcutils.GrpcClientConfig) authnlib.GrpcClientConfig {
return authnlib.GrpcClientConfig{
TokenClientConfig: &authnlib.TokenExchangeConfig{
Token: clientCfg.Token,
TokenExchangeURL: clientCfg.TokenExchangeURL,
},
TokenRequest: &authnlib.TokenExchangeRequest{
Namespace: clientCfg.TokenNamespace,
Audiences: []string{resourceStoreAudience},
},
}
}
func newResourceClient(conn *grpc.ClientConn, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) (resource.ResourceClient, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagAppPlatformGrpcClientAuth) {
return resource.NewLegacyResourceClient(conn), nil
}
return resource.NewRemoteResourceClient(tracer, conn, clientCfgMapping(grpcutils.ReadGrpcClientConfig(cfg)), cfg.Env == setting.Dev)
clientCfg := grpcutils.ReadGrpcClientConfig(cfg)
return resource.NewRemoteResourceClient(tracer, conn, resource.RemoteResourceClientConfig{
Token: clientCfg.Token,
TokenExchangeURL: clientCfg.TokenExchangeURL,
Audiences: []string{resourceStoreAudience},
Namespace: clientCfg.TokenNamespace,
AllowInsecure: cfg.Env == setting.Dev,
})
}
// GrpcConn creates a new gRPC connection to the provided address.
+1 -1
View File
@@ -153,7 +153,7 @@ func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req
))
defer span.End()
if fallbackUsed || !c.IsCompatibleWithRBAC(req.Group, req.Resource) {
return func(namespace string, name, folder string) bool {
return func(name, folder string) bool {
return true
}, nil
}
+4 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
authlib "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
func TestAuthzLimitedClient_Check(t *testing.T) {
@@ -27,6 +28,7 @@ func TestAuthzLimitedClient_Check(t *testing.T) {
req := authlib.CheckRequest{
Group: test.group,
Resource: test.resource,
Verb: utils.VerbGet,
}
resp, err := client.Check(context.Background(), nil, req)
assert.NoError(t, err)
@@ -52,12 +54,13 @@ func TestAuthzLimitedClient_Compile(t *testing.T) {
req := authlib.ListRequest{
Group: test.group,
Resource: test.resource,
Verb: utils.VerbGet,
}
checker, err := client.Compile(context.Background(), nil, req)
assert.NoError(t, err)
assert.NotNil(t, checker)
result := checker("namespace", "name", "folder")
result := checker("name", "folder")
assert.Equal(t, test.expected, result)
}
}
+1 -1
View File
@@ -270,7 +270,7 @@ func (b *batchRunner) Next() bool {
if !ok {
b.err = fmt.Errorf("missing access control for: %s", k)
b.rollback = true
} else if !checker(key.Namespace, key.Name, b.request.Folder) {
} else if !checker(key.Name, b.request.Folder) {
b.err = fmt.Errorf("not allowed to create resource")
b.rollback = true
}
+27 -18
View File
@@ -75,10 +75,9 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
)
}
clientInt, _ := authnlib.NewGrpcClientInterceptor(
&authnlib.GrpcClientConfig{TokenRequest: &authnlib.TokenExchangeRequest{}},
authnlib.WithTokenClientOption(grpcutils.ProvideInProcExchanger()),
authnlib.WithIDTokenExtractorOption(idTokenExtractor),
clientInt := authnlib.NewGrpcClientInterceptor(
grpcutils.ProvideInProcExchanger(),
authnlib.WithClientInterceptorIDTokenExtractor(idTokenExtractor),
)
cc := grpchan.InterceptClientConn(channel, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)
@@ -92,20 +91,36 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
}
}
func NewRemoteResourceClient(tracer tracing.Tracer, conn *grpc.ClientConn, cfg authnlib.GrpcClientConfig, allowInsecure bool) (ResourceClient, error) {
opts := []authnlib.GrpcClientInterceptorOption{
authnlib.WithIDTokenExtractorOption(idTokenExtractor),
authnlib.WithTracerOption(tracer),
type RemoteResourceClientConfig struct {
Token string
TokenExchangeURL string
Audiences []string
Namespace string
AllowInsecure bool
}
func NewRemoteResourceClient(tracer tracing.Tracer, conn *grpc.ClientConn, cfg RemoteResourceClientConfig) (ResourceClient, error) {
exchangeOpts := []authnlib.ExchangeClientOpts{}
if cfg.AllowInsecure {
exchangeOpts = append(exchangeOpts, authnlib.WithHTTPClient(&http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}))
}
if allowInsecure {
opts = allowInsecureTransportOpt(&cfg, opts)
}
tc, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
Token: cfg.Token,
TokenExchangeURL: cfg.TokenExchangeURL,
}, exchangeOpts...)
clientInt, err := authnlib.NewGrpcClientInterceptor(&cfg, opts...)
if err != nil {
return nil, err
}
clientInt := authnlib.NewGrpcClientInterceptor(
tc,
authnlib.WithClientInterceptorTracer(tracer),
authnlib.WithClientInterceptorNamespace(cfg.Namespace),
authnlib.WithClientInterceptorAudience(cfg.Audiences),
authnlib.WithClientInterceptorIDTokenExtractor(idTokenExtractor),
)
cc := grpchan.InterceptClientConn(conn, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)
return &resourceClient{
@@ -144,9 +159,3 @@ func idTokenExtractor(ctx context.Context) (string, error) {
return "", nil
}
func allowInsecureTransportOpt(grpcClientConfig *authnlib.GrpcClientConfig, opts []authnlib.GrpcClientInterceptorOption) []authnlib.GrpcClientInterceptorOption {
client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
tokenClient, _ := authnlib.NewTokenExchangeClient(*grpcClientConfig.TokenClientConfig, authnlib.WithHTTPClient(client))
return append(opts, authnlib.WithTokenClientOption(tokenClient))
}
+2 -2
View File
@@ -11,8 +11,8 @@ replace (
require (
github.com/fullstorydev/grpchan v1.1.1
github.com/google/uuid v1.6.0
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040
github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible
github.com/grafana/grafana-plugin-sdk-go v0.266.0
+4 -4
View File
@@ -399,10 +399,10 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 h1:iQ0h/h+QoguSZDF+ZpPxcM/C+m1kjh+aXjMpxywowPA=
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 h1:NTMmow+74I3Jb033xhbRgWQS7A//5TDhiM4tl7bsVP4=
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7/go.mod h1:T3X4z0ejGfJOiOmZLFeKCRT/yxWJq/RtclAc/PHj/w4=
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 h1:EokLC5grHwLPs4tXW8T6E8187H1e5G9AP0QQ5B60HbA=
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 h1:FTuDRy/Shw8yOdG+v1DnkeuaCAl8fvwgcfaG9Wccuhg=
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 h1:DnRUYiAotHXnrfYJCvhH1NkiyWVcPm5Pd+P7Ugqt/d8=
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s=
github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU=
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 h1:IR+UNYHqaU31t8/TArJk8K/GlDwOyxMpGNkWCXeZ28g=
+2 -2
View File
@@ -752,7 +752,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
Value: iter.Value(),
}
if !checker(iter.Namespace(), iter.Name(), iter.Folder()) {
if !checker(iter.Name(), iter.Folder()) {
continue
}
@@ -1035,7 +1035,7 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
}
s.log.Debug("Server Broadcasting", "type", event.Type, "rv", event.ResourceVersion, "previousRV", event.PreviousRV, "group", event.Key.Group, "namespace", event.Key.Namespace, "resource", event.Key.Resource, "name", event.Key.Name)
if event.ResourceVersion > since && matchesQueryKey(req.Options.Key, event.Key) {
if !checker(event.Key.Namespace, event.Key.Name, event.Folder) {
if !checker(event.Key.Name, event.Folder) {
continue
}
+1 -1
View File
@@ -965,7 +965,7 @@ func (q *permissionScopedQuery) Searcher(ctx context.Context, i index.IndexReade
q.log.Debug("No resource checker found", "resource", resource)
return false
}
allowed := q.checkers[resource](ns, name, folder)
allowed := q.checkers[resource](name, folder)
if !allowed {
q.log.Debug("Denying access", "ns", ns, "name", name, "folder", folder)
}
+1 -1
View File
@@ -582,7 +582,7 @@ func (nc *StubAccessClient) Check(ctx context.Context, id authlib.AuthInfo, req
}
func (nc *StubAccessClient) Compile(ctx context.Context, id authlib.AuthInfo, req authlib.ListRequest) (authlib.ItemChecker, error) {
return func(namespace string, name, folder string) bool {
return func(name, folder string) bool {
return nc.resourceResponses[req.Resource]
}, nil
}
@@ -99,7 +99,11 @@ func TestClientServer(t *testing.T) {
t.Run("Create a client", func(t *testing.T) {
conn, err := unified.GrpcConn(svc.GetAddress(), prometheus.NewPedanticRegistry())
require.NoError(t, err)
client, err = resource.NewRemoteResourceClient(tracing.NewNoopTracerService(), conn, authn.GrpcClientConfig{}, true)
client, err = resource.NewRemoteResourceClient(tracing.NewNoopTracerService(), conn, resource.RemoteResourceClientConfig{
Token: "some-token",
TokenExchangeURL: "http://some-change-url",
AllowInsecure: true,
})
require.NoError(t, err)
})