diff --git a/pkg/storage/unified/resource/client_wrapper.go b/pkg/storage/unified/resource/client_wrapper.go index 7eef6c144f7..b85554c8edc 100644 --- a/pkg/storage/unified/resource/client_wrapper.go +++ b/pkg/storage/unified/resource/client_wrapper.go @@ -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 NewResourceStoreClientLocal(server ResourceStoreServer) ResourceStoreClient { diff --git a/pkg/storage/unified/resource/grpc/authenticator.go b/pkg/storage/unified/resource/grpc/authenticator.go new file mode 100644 index 00000000000..d82643be41f --- /dev/null +++ b/pkg/storage/unified/resource/grpc/authenticator.go @@ -0,0 +1,104 @@ +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/services/grpcserver/interceptors" +) + +type Authenticator struct{} + +const ( + keyIDToken = "grafana-idtoken" + keyLogin = "grafana-login" + keyUserID = "grafana-userid" + keyUserUID = "grafana-useruid" + keyOrgID = "grafana-orgid" +) + +func (f *Authenticator) Authenticate(ctx context.Context) (context.Context, error) { + 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 context") + } + userID, err := strconv.ParseInt(md.Get(keyUserID)[0], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid user id: %w", err) + } + orgID, err := strconv.ParseInt(md.Get(keyOrgID)[0], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid org id: %w", err) + } + + return identity.WithRequester(ctx, &identity.StaticRequester{ + Login: login, + UserID: userID, + OrgID: orgID, + UserUID: md.Get(keyUserUID)[0], + }), 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( + keyIDToken, user.GetIDToken(), + keyLogin, user.GetLogin(), + keyOrgID, fmt.Sprintf("%d", user.GetOrgID()), + keyUserID, user.GetID().ID(), + keyUserUID, user.GetUID().ID(), + )), nil +}