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:
@@ -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}
|
||||
return authn.NewStaticTokenExchanger(token)
|
||||
}
|
||||
|
||||
func (e *inProcExchanger) Exchange(ctx context.Context, r authn.TokenExchangeRequest) (*authn.TokenExchangeResponse, error) {
|
||||
return e.tokenResponse, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user