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
pull/101340/head
Karl Persson 10 months ago committed by GitHub
parent a7ecb19c31
commit fa74d1c36d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      go.mod
  2. 8
      go.sum
  3. 4
      pkg/apimachinery/go.mod
  4. 8
      pkg/apimachinery/go.sum
  5. 2
      pkg/apiserver/go.mod
  6. 4
      pkg/apiserver/go.sum
  7. 6
      pkg/registry/apis/iam/common/common.go
  8. 2
      pkg/services/accesscontrol/authorizer.go
  9. 21
      pkg/services/apiserver/options/storage.go
  10. 28
      pkg/services/authn/grpcutils/inproc_exchanger.go
  11. 4
      pkg/services/authz/token_auth.go
  12. 4
      pkg/services/authz/zanzana/client/client.go
  13. 4
      pkg/storage/unified/apistore/go.mod
  14. 8
      pkg/storage/unified/apistore/go.sum
  15. 25
      pkg/storage/unified/client.go
  16. 2
      pkg/storage/unified/resource/access.go
  17. 5
      pkg/storage/unified/resource/access_test.go
  18. 2
      pkg/storage/unified/resource/batch.go
  19. 45
      pkg/storage/unified/resource/client.go
  20. 4
      pkg/storage/unified/resource/go.mod
  21. 8
      pkg/storage/unified/resource/go.sum
  22. 4
      pkg/storage/unified/resource/server.go
  23. 2
      pkg/storage/unified/search/bleve.go
  24. 2
      pkg/storage/unified/search/bleve_test.go
  25. 6
      pkg/storage/unified/sql/test/integration_test.go

@ -72,8 +72,8 @@ require (
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20250224133628-2acbeef29642 // @grafana/alerting-backend
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 // @grafana/identity-access-team
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 // @grafana/identity-access-team
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // @grafana/grafana-backend-group

@ -1513,10 +1513,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=

@ -3,8 +3,8 @@ module github.com/grafana/grafana/pkg/apimachinery
go 1.23.1
require (
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 // @grafana/identity-access-team
github.com/grafana/authlib v0.0.0-20250225105729-99e678595501 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 // @grafana/identity-access-team
github.com/stretchr/testify v1.10.0
k8s.io/apimachinery v0.32.1
k8s.io/apiserver v0.32.1

@ -32,10 +32,10 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=

@ -6,7 +6,7 @@ toolchain go1.23.6
require (
github.com/google/go-cmp v0.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-app-sdk/logging v0.30.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1
github.com/prometheus/client_golang v1.20.5

@ -79,8 +79,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/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/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME=
github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 h1:ItDcDxUjVLPKja+hogpqgW/kj8LxUL2qscelXIsN1Bs=

@ -61,7 +61,7 @@ func List[T Resource](
return nil, err
}
check := func(_, _, _ string) bool { return true }
check := func(_, _ string) bool { return true }
if ac != nil {
var err error
check, err = ac.Compile(ctx, ident, authlib.ListRequest{
@ -82,7 +82,7 @@ func List[T Resource](
}
for _, item := range first.Items {
if !check(ns.Value, item.AuthID(), "") {
if !check(item.AuthID(), "") {
continue
}
res.Items = append(res.Items, item)
@ -105,7 +105,7 @@ outer:
break outer
}
if !check(ns.Value, item.AuthID(), "") {
if !check(item.AuthID(), "") {
continue
}

@ -158,7 +158,7 @@ func (c *LegacyAccessClient) Compile(ctx context.Context, id claims.AuthInfo, re
}
check := Checker(ident, action)
return func(_, name, _ string) bool {
return func(name, _ string) bool {
return check(fmt.Sprintf("%s:%s:%s", opts.Resource, opts.Attr, name))
}, nil
}

@ -11,7 +11,6 @@ import (
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"github.com/grafana/authlib/authn"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
@ -123,17 +122,15 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi
if err != nil {
return err
}
authCfg := authn.GrpcClientConfig{
TokenClientConfig: &authn.TokenExchangeConfig{
Token: o.GrpcClientAuthenticationToken,
TokenExchangeURL: o.GrpcClientAuthenticationTokenExchangeURL,
},
TokenRequest: &authn.TokenExchangeRequest{
Audiences: []string{"resourceStore"},
Namespace: o.GrpcClientAuthenticationTokenNamespace,
},
}
unified, err := resource.NewRemoteResourceClient(tracer, conn, authCfg, o.GrpcClientAuthenticationAllowInsecure)
const resourceStoreAudience = "resourceStore"
unified, err := resource.NewRemoteResourceClient(tracer, conn, resource.RemoteResourceClientConfig{
Token: o.GrpcClientAuthenticationToken,
TokenExchangeURL: o.GrpcClientAuthenticationTokenExchangeURL,
Namespace: o.GrpcClientAuthenticationTokenNamespace,
Audiences: []string{resourceStoreAudience},
})
if err != nil {
return err
}

@ -1,10 +1,8 @@
package grpcutils
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/grafana/authlib/authn"
@ -12,29 +10,21 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
)
type inProcExchanger struct {
tokenResponse *authn.TokenExchangeResponse
}
func ProvideInProcExchanger() *inProcExchanger {
tokenResponse, err := createInProcToken()
func ProvideInProcExchanger() authn.StaticTokenExchanger {
token, err := createInProcToken()
if err != nil {
panic(err)
}
return &inProcExchanger{tokenResponse}
}
func (e *inProcExchanger) Exchange(ctx context.Context, r authn.TokenExchangeRequest) (*authn.TokenExchangeResponse, error) {
return e.tokenResponse, nil
return authn.NewStaticTokenExchanger(token)
}
func createInProcToken() (*authn.TokenExchangeResponse, error) {
func createInProcToken() (string, error) {
claims := authn.Claims[authn.AccessTokenClaims]{
Claims: jwt.Claims{
Audience: []string{"resourceStore"},
Issuer: "grafana",
Subject: types.NewTypeID(types.TypeAccessPolicy, "grafana"),
Audience: []string{"resourceStore"},
},
Rest: authn.AccessTokenClaims{
Namespace: "*",
@ -48,15 +38,13 @@ func createInProcToken() (*authn.TokenExchangeResponse, error) {
"typ": authn.TokenTypeAccess,
})
if err != nil {
return nil, err
return "", err
}
payload, err := json.Marshal(claims)
if err != nil {
return nil, err
return "", err
}
return &authn.TokenExchangeResponse{
Token: fmt.Sprintf("%s.%s.", base64.RawURLEncoding.EncodeToString(header), base64.RawURLEncoding.EncodeToString(payload)),
}, nil
return base64.RawURLEncoding.EncodeToString(header) + "." + base64.RawURLEncoding.EncodeToString(payload) + ".", nil
}

@ -25,7 +25,9 @@ func (t *tokenAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[st
return nil, err
}
return map[string]string{authn.DefaultAccessTokenMetadataKey: token.Token}, nil
const metadataKey = "X-Access-Token"
return map[string]string{metadataKey: token.Token}, nil
}
func (t *tokenAuth) RequireTransportSecurity() bool { return false }

@ -79,7 +79,7 @@ func (c *Client) Compile(ctx context.Context, id authlib.AuthInfo, req authlib.L
func newItemChecker(res *authzv1.ListResponse) authlib.ItemChecker {
// if we can see all resource of this type we can just return a function that always return true
if res.GetAll() {
return func(_, _, _ string) bool { return true }
return func(_, _ string) bool { return true }
}
folders := make(map[string]struct{}, len(res.Folders))
@ -92,7 +92,7 @@ func newItemChecker(res *authzv1.ListResponse) authlib.ItemChecker {
items[i] = struct{}{}
}
return func(_, name, folder string) bool {
return func(name, folder string) bool {
if _, ok := items[name]; ok {
return true
}

@ -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

@ -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=

@ -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.

@ -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
}

@ -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)
}
}

@ -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
}

@ -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
}
if allowInsecure {
opts = allowInsecureTransportOpt(&cfg, opts)
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}}}))
}
clientInt, err := authnlib.NewGrpcClientInterceptor(&cfg, opts...)
tc, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
Token: cfg.Token,
TokenExchangeURL: cfg.TokenExchangeURL,
}, exchangeOpts...)
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))
}

@ -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

@ -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=

@ -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
}

@ -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)
}

@ -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)
})

Loading…
Cancel
Save