merge main
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,6 @@ require (
|
||||
k8s.io/apiserver v0.29.2
|
||||
k8s.io/client-go v0.29.3
|
||||
k8s.io/component-base v0.29.2
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/klog/v2 v2.120.1
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340
|
||||
)
|
||||
|
||||
@@ -440,7 +440,6 @@ k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ=
|
||||
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||
k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8=
|
||||
k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
|
||||
@@ -5,18 +5,17 @@ import (
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/services/store/entity/grpc"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
grpcUtils "github.com/grafana/grafana/pkg/services/store/entity/grpc"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
)
|
||||
|
||||
func NewEntityStoreClientLocal(server EntityStoreServer) EntityStoreClient {
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type Authenticator struct{}
|
||||
|
||||
func (f *Authenticator) Authenticate(ctx context.Context) (context.Context, error) {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no metadata found")
|
||||
}
|
||||
|
||||
// TODO: use id token instead of these fields
|
||||
login := md.Get("grafana-login")[0]
|
||||
if login == "" {
|
||||
return nil, fmt.Errorf("no login found in context")
|
||||
}
|
||||
userID, err := strconv.ParseInt(md.Get("grafana-userid")[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid user id: %w", err)
|
||||
}
|
||||
orgID, err := strconv.ParseInt(md.Get("grafana-orgid")[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid org id: %w", err)
|
||||
}
|
||||
|
||||
// TODO: validate id token
|
||||
/*
|
||||
idToken := md.Get("grafana-idtoken")[0]
|
||||
if idToken == "" {
|
||||
return nil, fmt.Errorf("no id token found in context")
|
||||
}
|
||||
jwtToken, err := jwt.ParseSigned(idToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid id token: %w", err)
|
||||
}
|
||||
claims := jwt.Claims{}
|
||||
err = jwtToken.UnsafeClaimsWithoutVerification(&claims)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid id token: %w", err)
|
||||
}
|
||||
// fmt.Printf("JWT CLAIMS: %+v\n", claims)
|
||||
*/
|
||||
|
||||
return appcontext.WithUser(ctx, &user.SignedInUser{
|
||||
Login: login,
|
||||
UserID: userID,
|
||||
OrgID: orgID,
|
||||
}), nil
|
||||
}
|
||||
|
||||
var _ interceptors.Authenticator = (*Authenticator)(nil)
|
||||
|
||||
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx, err := WrapContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
|
||||
var _ grpc.UnaryClientInterceptor = UnaryClientInterceptor
|
||||
|
||||
func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ctx, err := WrapContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
|
||||
var _ grpc.StreamClientInterceptor = StreamClientInterceptor
|
||||
|
||||
func WrapContext(ctx context.Context) (context.Context, error) {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// set grpc metadata into the context to pass to the grpc server
|
||||
return metadata.NewOutgoingContext(ctx, metadata.Pairs(
|
||||
"grafana-idtoken", user.GetIDToken(),
|
||||
"grafana-userid", user.GetID().ID(),
|
||||
"grafana-useruid", user.GetUID().ID(),
|
||||
"grafana-orgid", strconv.FormatInt(user.GetOrgID(), 10),
|
||||
"grafana-login", user.GetLogin(),
|
||||
)), nil
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/dskit/services"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/modules"
|
||||
@@ -13,11 +16,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db/dbimpl"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/grpc"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -6,7 +6,7 @@ require (
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/fullstorydev/grpchan v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240613114114-5e2f08de316d
|
||||
github.com/grafana/authlib v0.0.0-20240611075137-331cbe4e840f
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
@@ -25,6 +25,7 @@ require (
|
||||
github.com/bufbuild/protocompile v0.4.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -33,9 +34,9 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||
github.com/jhump/protoreflect v1.15.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.53.0 // indirect
|
||||
@@ -44,14 +45,15 @@ require (
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
||||
go.opentelemetry.io/otel v1.26.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/api v0.176.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
@@ -30,6 +30,7 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -42,7 +43,7 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240613114114-5e2f08de316d h1:/UE5JdF+0hxll7EuuO7zRzAxXrvAxQo5M9eqOepc2mQ=
|
||||
github.com/grafana/authlib v0.0.0-20240611075137-331cbe4e840f h1:hvRCAv+TgcHu3i/Sd7lFJx84iEtgzDCYuk7OWeXatD0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
@@ -51,6 +52,7 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
|
||||
@@ -5,75 +5,116 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
)
|
||||
|
||||
type Authenticator struct{}
|
||||
|
||||
const (
|
||||
keyIDToken = "grafana-idtoken"
|
||||
keyLogin = "grafana-login"
|
||||
keyUserID = "grafana-userid"
|
||||
keyUserUID = "grafana-useruid"
|
||||
keyOrgID = "grafana-orgid"
|
||||
mdToken = "grafana-idtoken"
|
||||
mdLogin = "grafana-login"
|
||||
mdUserID = "grafana-user-id"
|
||||
mdUserUID = "grafana-user-uid"
|
||||
mdOrgName = "grafana-org-name"
|
||||
mdOrgID = "grafana-org-id"
|
||||
mdOrgRole = "grafana-org-role"
|
||||
)
|
||||
|
||||
// This is in a package we can no import
|
||||
// var _ interceptors.Authenticator = (*Authenticator)(nil)
|
||||
|
||||
type Authenticator struct {
|
||||
IDTokenVerifier authn.Verifier[authn.IDTokenClaims]
|
||||
}
|
||||
|
||||
func (f *Authenticator) Authenticate(ctx context.Context) (context.Context, error) {
|
||||
rrr, _ := identity.GetRequester(ctx)
|
||||
if rrr != nil {
|
||||
return ctx, nil
|
||||
r, err := identity.GetRequester(ctx)
|
||||
if err == nil && r != nil {
|
||||
return ctx, nil // noop, requester exists
|
||||
}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no metadata found")
|
||||
}
|
||||
|
||||
token := md.Get(keyIDToken)[0]
|
||||
if token != "" {
|
||||
fmt.Printf("TODO, create the requester from the token!")
|
||||
|
||||
// jwtToken, err := jwt.ParseSigned(idToken)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("invalid id token: %w", err)
|
||||
// }
|
||||
// claims := jwt.Claims{}
|
||||
// err = jwtToken.UnsafeClaimsWithoutVerification(&claims)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("invalid id token: %w", err)
|
||||
// }
|
||||
// // fmt.Printf("JWT CLAIMS: %+v\n", claims)
|
||||
}
|
||||
|
||||
login := md.Get(keyLogin)[0]
|
||||
if login == "" {
|
||||
return nil, fmt.Errorf("no login found in grpc context")
|
||||
}
|
||||
userID, err := strconv.ParseInt(md.Get(keyUserID)[0], 10, 64)
|
||||
user, err := f.DecodeMetadata(ctx, md)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid grpc user id: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
orgID, err := strconv.ParseInt(md.Get(keyOrgID)[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid grpc org id: %w", err)
|
||||
}
|
||||
|
||||
return identity.WithRequester(ctx, &identity.StaticRequester{
|
||||
Login: login,
|
||||
UserID: userID,
|
||||
OrgID: orgID,
|
||||
UserUID: md.Get(keyUserUID)[0],
|
||||
}), nil
|
||||
return identity.WithRequester(ctx, user), nil
|
||||
}
|
||||
|
||||
var _ interceptors.Authenticator = (*Authenticator)(nil)
|
||||
func (f *Authenticator) DecodeMetadata(ctx context.Context, meta metadata.MD) (identity.Requester, error) {
|
||||
// Avoid NPE/panic with getting keys
|
||||
getter := func(key string) string {
|
||||
v := meta.Get(key)
|
||||
if len(v) > 0 {
|
||||
return v[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// First try the token
|
||||
token := getter(mdToken)
|
||||
if token != "" && f.IDTokenVerifier != nil {
|
||||
claims, err := f.IDTokenVerifier.Verify(ctx, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("TODO, convert CLAIMS to an identity %+v\n", claims)
|
||||
}
|
||||
|
||||
user := &identity.StaticRequester{}
|
||||
user.Login = getter(mdLogin)
|
||||
if user.Login == "" {
|
||||
return nil, fmt.Errorf("no login found in grpc metadata")
|
||||
}
|
||||
|
||||
// The namespaced verisons have a "-" in the key
|
||||
// TODO, remove after this has been deployed to unified storage
|
||||
if getter(mdUserID) == "" {
|
||||
var err error
|
||||
user.Namespace = identity.NamespaceUser
|
||||
user.UserID, err = strconv.ParseInt(getter("grafana-userid"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid user id: %w", err)
|
||||
}
|
||||
user.OrgID, err = strconv.ParseInt(getter("grafana-orgid"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid org id: %w", err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
ns, err := identity.ParseNamespaceID(getter(mdUserID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid user id: %w", err)
|
||||
}
|
||||
user.Namespace = ns.Namespace()
|
||||
user.UserID, err = ns.ParseInt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid user id: %w", err)
|
||||
}
|
||||
|
||||
ns, err = identity.ParseNamespaceID(getter(mdUserUID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid user id: %w", err)
|
||||
}
|
||||
user.UserUID = ns.ID()
|
||||
|
||||
user.OrgName = getter(mdOrgName)
|
||||
user.OrgID, err = strconv.ParseInt(getter(mdOrgID), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid org id: %w", err)
|
||||
}
|
||||
user.OrgRole = identity.RoleType(getter(mdOrgRole))
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx, err := WrapContext(ctx)
|
||||
ctx, err := wrapContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,7 +124,7 @@ func UnaryClientInterceptor(ctx context.Context, method string, req, reply inter
|
||||
var _ grpc.UnaryClientInterceptor = UnaryClientInterceptor
|
||||
|
||||
func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ctx, err := WrapContext(ctx)
|
||||
ctx, err := wrapContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,18 +133,31 @@ func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grp
|
||||
|
||||
var _ grpc.StreamClientInterceptor = StreamClientInterceptor
|
||||
|
||||
func WrapContext(ctx context.Context) (context.Context, error) {
|
||||
func wrapContext(ctx context.Context) (context.Context, error) {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// set grpc metadata into the context to pass to the grpc server
|
||||
return metadata.NewOutgoingContext(ctx, metadata.Pairs(
|
||||
keyIDToken, user.GetIDToken(),
|
||||
keyLogin, user.GetLogin(),
|
||||
keyOrgID, fmt.Sprintf("%d", user.GetOrgID()),
|
||||
keyUserID, user.GetID().ID(),
|
||||
keyUserUID, user.GetUID().ID(),
|
||||
)), nil
|
||||
return metadata.NewOutgoingContext(ctx, encodeIdentityInMetadata(user)), nil
|
||||
}
|
||||
|
||||
func encodeIdentityInMetadata(user identity.Requester) metadata.MD {
|
||||
return metadata.Pairs(
|
||||
// This should be everything needed to recreate the user
|
||||
mdToken, user.GetIDToken(),
|
||||
|
||||
// Or we can create it directly
|
||||
mdUserID, user.GetID().String(),
|
||||
mdUserUID, user.GetUID().String(),
|
||||
mdOrgName, user.GetOrgName(),
|
||||
mdOrgID, strconv.FormatInt(user.GetOrgID(), 10),
|
||||
mdOrgRole, string(user.GetOrgRole()),
|
||||
mdLogin, user.GetLogin(),
|
||||
|
||||
// TODO, Remove after this is deployed to unified storage
|
||||
"grafana-userid", user.GetID().ID(),
|
||||
"grafana-useruid", user.GetUID().ID(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func TestBasicEncodeDecode(t *testing.T) {
|
||||
before := &identity.StaticRequester{
|
||||
UserID: 123,
|
||||
UserUID: "abc",
|
||||
Login: "test",
|
||||
Namespace: identity.NamespaceUser,
|
||||
OrgID: 456,
|
||||
OrgName: "org",
|
||||
OrgRole: identity.RoleAdmin,
|
||||
}
|
||||
|
||||
auth := &Authenticator{}
|
||||
|
||||
md := encodeIdentityInMetadata(before)
|
||||
after, err := auth.DecodeMetadata(context.Background(), md)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before.GetID(), after.GetID())
|
||||
require.Equal(t, before.GetUID(), after.GetUID())
|
||||
require.Equal(t, before.GetLogin(), after.GetLogin())
|
||||
require.Equal(t, before.GetOrgID(), after.GetOrgID())
|
||||
require.Equal(t, before.GetOrgName(), after.GetOrgName())
|
||||
require.Equal(t, before.GetOrgRole(), after.GetOrgRole())
|
||||
}
|
||||
Reference in New Issue
Block a user