Auth: Add access token to in-proc communication and ServiceIdentity (#98926)
Use fake access token for in-proc grpc and add ServiceIdentity --------- Co-authored-by: gamab <gabriel.mabille@grafana.com> Co-authored-by: Karl Persson <23356117+kalleep@users.noreply.github.com>
This commit is contained in:
co-authored by
gamab
Karl Persson
parent
eb2d276a42
commit
437b7a565d
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
@@ -12,7 +13,8 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/authn/grpcutils"
|
||||
@@ -70,8 +72,8 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
}
|
||||
|
||||
clientInt, _ := authnlib.NewGrpcClientInterceptor(
|
||||
&authnlib.GrpcClientConfig{},
|
||||
authnlib.WithDisableAccessTokenOption(),
|
||||
&authnlib.GrpcClientConfig{TokenRequest: &authnlib.TokenExchangeRequest{}},
|
||||
authnlib.WithTokenClientOption(grpcutils.ProvideInProcExchanger()),
|
||||
authnlib.WithIDTokenExtractorOption(idTokenExtractor),
|
||||
)
|
||||
|
||||
@@ -131,15 +133,28 @@ func NewCloudResourceClient(tracer tracing.Tracer, conn *grpc.ClientConn, cfg au
|
||||
}, nil
|
||||
}
|
||||
|
||||
var authLogger = slog.Default().With("logger", "resource-client-auth-interceptor")
|
||||
|
||||
func idTokenExtractor(ctx context.Context) (string, error) {
|
||||
authInfo, ok := claims.AuthInfoFrom(ctx)
|
||||
if identity.IsServiceIdentity(ctx) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
info, ok := types.AuthInfoFrom(ctx)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no claims found")
|
||||
}
|
||||
|
||||
extra := authInfo.GetExtra()
|
||||
if token, exists := extra["id-token"]; exists && len(token) != 0 && token[0] != "" {
|
||||
return token[0], nil
|
||||
if token := info.GetIDToken(); len(token) != 0 {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
if !types.IsIdentityType(info.GetIdentityType(), types.TypeAccessPolicy) {
|
||||
authLogger.Warn(
|
||||
"calling resource store as the service without id token or marking it as the service identity",
|
||||
"subject", info.GetSubject(),
|
||||
"uid", info.GetUID(),
|
||||
)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
@@ -16,11 +15,8 @@ func TestIDTokenExtractor(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, token)
|
||||
})
|
||||
t.Run("should return an empty token for static requester of type service account as grafana admin ", func(t *testing.T) {
|
||||
ctx := identity.WithRequester(context.Background(), &identity.StaticRequester{
|
||||
Type: claims.TypeServiceAccount,
|
||||
IsGrafanaAdmin: true,
|
||||
})
|
||||
t.Run("should return an empty token when grafana identity is set", func(t *testing.T) {
|
||||
ctx, _ := identity.WithServiceIdentitiy(context.Background(), 0)
|
||||
token, err := idTokenExtractor(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, token)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
)
|
||||
|
||||
@@ -233,13 +232,7 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) {
|
||||
}
|
||||
|
||||
// Make this cancelable
|
||||
ctx, cancel := context.WithCancel(claims.WithAuthInfo(context.Background(),
|
||||
&identity.StaticRequester{
|
||||
Type: claims.TypeServiceAccount,
|
||||
Login: "watcher", // admin user for watch
|
||||
UserID: 1,
|
||||
IsGrafanaAdmin: true,
|
||||
}))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s := &server{
|
||||
tracer: opts.Tracer,
|
||||
log: logger,
|
||||
|
||||
@@ -5,15 +5,16 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/services"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -70,15 +71,13 @@ func TestIntegrationBackendHappyPath(t *testing.T) {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
testUserA := &identity.StaticRequester{
|
||||
Type: claims.TypeUser,
|
||||
Login: "testuser",
|
||||
UserID: 123,
|
||||
UserUID: "u123",
|
||||
OrgRole: identity.RoleAdmin,
|
||||
IsGrafanaAdmin: true, // can do anything
|
||||
}
|
||||
ctx := identity.WithRequester(context.Background(), testUserA)
|
||||
ctx := types.WithAuthInfo(context.Background(), authn.NewAccessTokenAuthInfo(authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Subject: "testuser",
|
||||
},
|
||||
Rest: authn.AccessTokenClaims{},
|
||||
}))
|
||||
|
||||
backend, server := newServer(t, nil)
|
||||
|
||||
stream, err := backend.WatchWriteEvents(context.Background()) // Using a different context to avoid canceling the stream after the DefaultContextTimeout
|
||||
@@ -420,15 +419,12 @@ func TestClientServer(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
var client resource.ResourceStoreClient
|
||||
|
||||
// Test with an admin identity
|
||||
clientCtx := identity.WithRequester(ctx, &identity.StaticRequester{
|
||||
Type: claims.TypeUser,
|
||||
Login: "testuser",
|
||||
UserID: 123,
|
||||
UserUID: "u123",
|
||||
OrgRole: identity.RoleAdmin,
|
||||
IsGrafanaAdmin: true, // can do anything
|
||||
})
|
||||
clientCtx := types.WithAuthInfo(context.Background(), authn.NewAccessTokenAuthInfo(authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Subject: "testuser",
|
||||
},
|
||||
Rest: authn.AccessTokenClaims{},
|
||||
}))
|
||||
|
||||
t.Run("Start and stop service", func(t *testing.T) {
|
||||
err = services.StartAndAwaitRunning(ctx, svc)
|
||||
|
||||
Reference in New Issue
Block a user