From 8b4f70d8e31accdaf9efd2b7b34d3a0aee923dbd Mon Sep 17 00:00:00 2001 From: Dhia Abbassi Date: Thu, 8 Aug 2024 15:49:31 +0200 Subject: [PATCH] implement kontext client authorization --- js/modules/k6/experimental/kontext/kontext.go | 23 ++++++++++++++++++- js/modules/k6/experimental/kontext/module.go | 14 +++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/js/modules/k6/experimental/kontext/kontext.go b/js/modules/k6/experimental/kontext/kontext.go index 42917555f53..1b34c382f50 100644 --- a/js/modules/k6/experimental/kontext/kontext.go +++ b/js/modules/k6/experimental/kontext/kontext.go @@ -27,6 +27,7 @@ var ErrKontextKeyNotFound = errors.New("key not found") var ErrKontextWrongType = errors.New("wrong type") const k6ServiceURLEnvironmentVariable = "K6_KONTEXT_SERVICE_URL" +const k6ServiceAuthEnvironmentVariable = "K6_KONTEXT_AUTH_TOKEN" const secureEnvironmentVariable = "K6_KONTEXT_SECURE" const testRunIDHeader = "X-Test-Run-ID" @@ -447,8 +448,24 @@ type CloudKontext struct { // Ensure that CloudKontext implements the Kontexter interface. var _ Kontexter = &CloudKontext{} +// kontextTokenAuth implements the credentials.PerRPCCredentials interface +// to send the authorization token to the k6-cloud-kontext server. +type kontextTokenAuth struct { + Token string +} + +func (t *kontextTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "authorization": "Bearer " + t.Token, + }, nil +} + +func (t *kontextTokenAuth) RequireTransportSecurity() bool { + return true +} + // NewCloudKontext creates a new CloudKontext instance. -func NewCloudKontext(vu modules.VU, serviceURL string, secure bool, testRunID string) (*CloudKontext, error) { +func NewCloudKontext(vu modules.VU, serviceURL string, secure bool, testRunID string, authToken string) (*CloudKontext, error) { // create a gRPC connection to the server opts := []grpc.DialOption{} if secure { @@ -459,6 +476,10 @@ func NewCloudKontext(vu modules.VU, serviceURL string, secure bool, testRunID st opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) } + opts = append(opts, grpc.WithPerRPCCredentials(&kontextTokenAuth{ + Token: authToken, + })) + conn, err := grpc.NewClient(serviceURL, opts...) if err != nil { return nil, fmt.Errorf("failed to dial gRPC server: %w", err) diff --git a/js/modules/k6/experimental/kontext/module.go b/js/modules/k6/experimental/kontext/module.go index 30a3d3659c4..6c9c7ce5883 100644 --- a/js/modules/k6/experimental/kontext/module.go +++ b/js/modules/k6/experimental/kontext/module.go @@ -95,13 +95,17 @@ func (mi *ModuleInstance) NewKontext(_ sobek.ConstructorCall) *sobek.Object { common.Throw(mi.vu.Runtime(), fmt.Errorf("kontext instances can only be created in the init context")) } - serviceURL, hasServiceURL := os.LookupEnv(k6ServiceURLEnvironmentVariable) - secure := strings.ToLower(os.Getenv(secureEnvironmentVariable)) != "false" - var kv Kontexter var err error - if hasServiceURL { - kv, err = NewCloudKontext(mi.vu, serviceURL, secure, mi.rm.testRunID) + + if serviceURL, hasServiceURL := os.LookupEnv(k6ServiceURLEnvironmentVariable); hasServiceURL { + kontextAuthToken := os.Getenv(k6ServiceAuthEnvironmentVariable) + if kontextAuthToken == "" { + common.Throw(mi.vu.Runtime(), fmt.Errorf("Kontext module is missing the kontext authToken")) + } + secure := strings.ToLower(os.Getenv(secureEnvironmentVariable)) != "false" + + kv, err = NewCloudKontext(mi.vu, serviceURL, secure, mi.rm.testRunID, kontextAuthToken) if err != nil { common.Throw(mi.vu.Runtime(), fmt.Errorf("failed to create new Kontext instance: %w", err)) }