From 68c60f5723cf2c65533b983e8814db7a27b4881f Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 11:06:02 +0200 Subject: [PATCH 01/16] implement upstream basic authentication --- apidef/api_definitions.go | 19 ++++ apidef/oas/upstream.go | 78 ++++++++++++++ ctx/ctx.go | 35 ++++++ gateway/api_loader.go | 2 + gateway/mw_upstream_basic_auth.go | 49 +++++++++ gateway/mw_upstream_basic_auth_test.go | 143 +++++++++++++++++++++++++ gateway/reverse_proxy.go | 16 +++ test/http.go | 1 + 8 files changed, 343 insertions(+) create mode 100644 gateway/mw_upstream_basic_auth.go create mode 100644 gateway/mw_upstream_basic_auth_test.go diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index d1c17a4200b..4c25b6e079a 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -762,6 +762,25 @@ type APIDefinition struct { VersionName string `bson:"-" json:"-"` DetailedTracing bool `bson:"detailed_tracing" json:"detailed_tracing"` + + // UpstreamAuth stores information about authenticating against upstream. + UpstreamAuth UpstreamAuth `bson:"upstream_auth" json:"upstream_auth"` +} + +type UpstreamAuth struct { + Enabled bool `bson:"enabled" json:"enabled"` + BasicAuth UpstreamBasicAuth `bson:"basic_auth" json:"basic_auth"` +} + +func (u *UpstreamAuth) IsEnabled() bool { + return u.Enabled && u.BasicAuth.Enabled +} + +type UpstreamBasicAuth struct { + Enabled bool `bson:"enabled" json:"enabled,omitempty"` + Username string `bson:"username" json:"username"` + Password string `bson:"password" json:"password"` + HeaderName string `bson:"auth_header_name" json:"authHeaderName"` } type AnalyticsPluginConfig struct { diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index 0077bdd3961..f7bea08241e 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -29,6 +29,9 @@ type Upstream struct { // RateLimit contains the configuration related to API level rate limit. RateLimit *RateLimit `bson:"rateLimit,omitempty" json:"rateLimit,omitempty"` + + // Authentication contains the configuration related to upstream authentication. + Authentication *UpstreamAuth `bson:"authentication,omitempty" json:"authentication,omitempty"` } // Fill fills *Upstream from apidef.APIDefinition. @@ -79,6 +82,15 @@ func (u *Upstream) Fill(api apidef.APIDefinition) { if ShouldOmit(u.RateLimit) { u.RateLimit = nil } + + if u.Authentication == nil { + u.Authentication = &UpstreamAuth{} + } + + u.Authentication.Fill(api.UpstreamAuth) + if ShouldOmit(u.Authentication) { + u.Authentication = nil + } } // ExtractTo extracts *Upstream into *apidef.APIDefinition. @@ -129,6 +141,15 @@ func (u *Upstream) ExtractTo(api *apidef.APIDefinition) { } u.RateLimit.ExtractTo(api) + + if u.Authentication == nil { + u.Authentication = &UpstreamAuth{} + defer func() { + u.Authentication = nil + }() + } + + u.Authentication.ExtractTo(&api.UpstreamAuth) } // ServiceDiscovery holds configuration required for service discovery. @@ -529,3 +550,60 @@ func (r *RateLimitEndpoint) ExtractTo(meta *apidef.RateLimitMeta) { meta.Rate = float64(r.Rate) meta.Per = r.Per.Seconds() } + +type UpstreamAuth struct { + Enabled bool `bson:"enabled" json:"enabled"` + BasicAuth *UpstreamBasicAuth `bson:"basicAuth,omitempty" json:"basicAuth,omitempty"` +} + +// Fill fills *UpstreamAuth from apidef.UpstreamAuth. +func (u *UpstreamAuth) Fill(api apidef.UpstreamAuth) { + u.Enabled = api.Enabled + + if u.BasicAuth == nil { + u.BasicAuth = &UpstreamBasicAuth{} + } + + u.BasicAuth.Fill(api.BasicAuth) + if ShouldOmit(u.BasicAuth) { + u.BasicAuth = nil + } +} + +// ExtractTo extracts *UpstreamAuth into *apidef.UpstreamAuth. +func (u *UpstreamAuth) ExtractTo(api *apidef.UpstreamAuth) { + api.Enabled = u.Enabled + + if u.BasicAuth == nil { + u.BasicAuth = &UpstreamBasicAuth{} + defer func() { + u.BasicAuth = nil + }() + } + + u.BasicAuth.ExtractTo(&api.BasicAuth) +} + +type UpstreamBasicAuth struct { + Enabled bool `bson:"enabled" json:"enabled"` + HeaderName string `bson:"headerName" json:"headerName"` + Username string `bson:"username" json:"username"` + Password string `bson:"password" json:"password"` +} + +// Fill fills *UpstreamBasicAuth from apidef.UpstreamBasicAuth. +func (u *UpstreamBasicAuth) Fill(api apidef.UpstreamBasicAuth) { + u.Enabled = api.Enabled + u.HeaderName = api.HeaderName + u.Username = api.Username + u.Password = api.Password +} + +// ExtractTo extracts *UpstreamBasicAuth into *apidef.UpstreamBasicAuth. +func (u *UpstreamBasicAuth) ExtractTo(api *apidef.UpstreamBasicAuth) { + api.Enabled = u.Enabled + api.Enabled = u.Enabled + api.HeaderName = u.HeaderName + api.Username = u.Username + api.Password = u.Password +} diff --git a/ctx/ctx.go b/ctx/ctx.go index 13b7fd76aeb..b13b743e837 100644 --- a/ctx/ctx.go +++ b/ctx/ctx.go @@ -51,6 +51,11 @@ const ( // CacheOptions holds cache options required for cache writer middleware. CacheOptions OASDefinition + + // UpstreamAuthHeader sets the header name to be used for upstream authentication. + UpstreamAuthHeader + // UpstreamAuthValue sets the value for upstream authentication. + UpstreamAuthValue ) func setContext(r *http.Request, ctx context.Context) { @@ -158,3 +163,33 @@ func GetOASDefinition(r *http.Request) *oas.OAS { return ret } + +// SetUpstreamAuthHeader sets the header name to be used for upstream authentication. +func SetUpstreamAuthHeader(r *http.Request, name string) { + ctx := r.Context() + ctx = context.WithValue(ctx, UpstreamAuthHeader, name) + setContext(r, ctx) +} + +// GetUpstreamAuthHeader returns the header name to be used for upstream authentication. +func GetUpstreamAuthHeader(r *http.Request) string { + if v := r.Context().Value(UpstreamAuthHeader); v != nil { + return v.(string) + } + return "" +} + +// SetUpstreamAuthValue sets the auth header value to be used for upstream authentication. +func SetUpstreamAuthValue(r *http.Request, name string) { + ctx := r.Context() + ctx = context.WithValue(ctx, UpstreamAuthValue, name) + setContext(r, ctx) +} + +// GetUpstreamAuthValue gets the auth header value to be used for upstream authentication. +func GetUpstreamAuthValue(r *http.Request) string { + if v := r.Context().Value(UpstreamAuthValue); v != nil { + return v.(string) + } + return "" +} diff --git a/gateway/api_loader.go b/gateway/api_loader.go index 01ad37e940e..8175e0dea0a 100644 --- a/gateway/api_loader.go +++ b/gateway/api_loader.go @@ -468,6 +468,8 @@ func (gw *Gateway) processSpec(spec *APISpec, apisByListen map[string]int, } } + gw.mwAppendEnabled(&chainArray, &UpstreamBasicAuth{BaseMiddleware: baseMid}) + chain = alice.New(chainArray...).Then(&DummyProxyHandler{SH: SuccessHandler{baseMid}, Gw: gw}) if !spec.UseKeylessAccess { diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go new file mode 100644 index 00000000000..4e6397c4bf1 --- /dev/null +++ b/gateway/mw_upstream_basic_auth.go @@ -0,0 +1,49 @@ +package gateway + +import ( + "encoding/base64" + "fmt" + "net/http" + + "github.com/TykTechnologies/tyk/ctx" + "github.com/TykTechnologies/tyk/header" +) + +// UpstreamBasicAuth is a middleware that will do basic authentication for upstream connections. +// UpstreamBasicAuth middleware is only supported in Tyk OAS API definitions. +type UpstreamBasicAuth struct { + *BaseMiddleware +} + +func (t *UpstreamBasicAuth) Name() string { + return "UpstreamBasicAuth" +} + +func (t *UpstreamBasicAuth) EnabledForSpec() bool { + if !t.Spec.UpstreamAuth.Enabled { + return false + } + + if !t.Spec.UpstreamAuth.BasicAuth.Enabled { + return false + } + + return true +} + +// ProcessRequest will inject basic auth info into request context so that it can be used during reverse proxy. +func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { + basicAuthConfig := t.Spec.UpstreamAuth.BasicAuth + + authHeaderName := header.Authorization + if basicAuthConfig.HeaderName != "" { + authHeaderName = basicAuthConfig.HeaderName + } + ctx.SetUpstreamAuthHeader(r, authHeaderName) + + payload := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", basicAuthConfig.Username, basicAuthConfig.Password))) + + ctx.SetUpstreamAuthValue(r, payload) + + return nil, http.StatusOK +} diff --git a/gateway/mw_upstream_basic_auth_test.go b/gateway/mw_upstream_basic_auth_test.go new file mode 100644 index 00000000000..b0c4966bb63 --- /dev/null +++ b/gateway/mw_upstream_basic_auth_test.go @@ -0,0 +1,143 @@ +package gateway + +import ( + "encoding/base64" + "encoding/json" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/TykTechnologies/tyk/apidef" + "github.com/TykTechnologies/tyk/header" + "github.com/TykTechnologies/tyk/test" +) + +func TestUpstreamBasicAuthentication(t *testing.T) { + + ts := StartTest(nil) + t.Cleanup(func() { + ts.Close() + }) + + userName, password, customAuthHeader := "user", "password", "Custom-Auth" + expectedAuth := base64.StdEncoding.EncodeToString([]byte(userName + ":" + password)) + + ts.Gw.BuildAndLoadAPI( + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-enabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: true, + Username: userName, + Password: password, + }, + } + spec.Proxy.StripListenPath = true + }, func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-custom-header/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: true, + Username: userName, + Password: password, + HeaderName: customAuthHeader, + }, + } + spec.Proxy.StripListenPath = true + }, + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-disabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: false, + Username: userName, + Password: password, + }, + } + spec.Proxy.StripListenPath = true + }, + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-auth-disabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: false, + } + spec.Proxy.StripListenPath = true + }, + ) + + _, _ = ts.Run(t, test.TestCases{ + { + Path: "/upstream-basic-auth-enabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.Contains(t, resp.Headers, header.Authorization) + assert.NotEmpty(t, resp.Headers[header.Authorization]) + assert.Equal(t, expectedAuth, resp.Headers[header.Authorization]) + + return true + }, + }, + { + Path: "/upstream-basic-auth-custom-header/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.Contains(t, resp.Headers, customAuthHeader) + assert.NotEmpty(t, resp.Headers[customAuthHeader]) + assert.Equal(t, expectedAuth, resp.Headers[customAuthHeader]) + + return true + }, + }, + { + Path: "/upstream-basic-auth-disabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.NotContains(t, resp.Headers, header.Authorization) + + return true + }, + }, + { + Path: "/upstream-auth-disabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.NotContains(t, resp.Headers, header.Authorization) + + return true + }, + }, + }...) + +} diff --git a/gateway/reverse_proxy.go b/gateway/reverse_proxy.go index a7755c63f5a..f252677b3f2 100644 --- a/gateway/reverse_proxy.go +++ b/gateway/reverse_proxy.go @@ -1219,6 +1219,8 @@ func (p *ReverseProxy) WrappedServeHTTP(rw http.ResponseWriter, req *http.Reques } + p.addAuthInfo(outreq, req) + // do request round trip var ( res *http.Response @@ -1845,3 +1847,17 @@ func (p *ReverseProxy) IsUpgrade(req *http.Request) (string, bool) { return httputil.IsUpgrade(req) } + +func (p *ReverseProxy) addAuthInfo(outReq, req *http.Request) { + if !p.TykAPISpec.UpstreamAuth.IsEnabled() { + return + } + + authHeaderName := ctx.GetUpstreamAuthHeader(req) + if authHeaderName == "" { + return + } + + authHeaderValue := ctx.GetUpstreamAuthValue(req) + outReq.Header.Add(authHeaderName, authHeaderValue) +} diff --git a/test/http.go b/test/http.go index 5c09b3e4389..e46463df46b 100644 --- a/test/http.go +++ b/test/http.go @@ -16,6 +16,7 @@ import ( "time" ) +type TestCases []TestCase type TestCase struct { Host string `json:",omitempty"` Method string `json:",omitempty"` From b2127b759994909e5d761389176b20374c4619e7 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 11:30:19 +0200 Subject: [PATCH 02/16] add godocs --- apidef/api_definitions.go | 18 ++++++++++++++---- apidef/oas/upstream.go | 17 +++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 4c25b6e079a..7fee8332a01 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -767,19 +767,29 @@ type APIDefinition struct { UpstreamAuth UpstreamAuth `bson:"upstream_auth" json:"upstream_auth"` } +// UpstreamAuth holds the configurations related to upstream API authentication. type UpstreamAuth struct { - Enabled bool `bson:"enabled" json:"enabled"` + // Enabled enables upstream API authentication. + Enabled bool `bson:"enabled" json:"enabled"` + // BasicAuth holds the basic authentication configuration for upstream API authentication. BasicAuth UpstreamBasicAuth `bson:"basic_auth" json:"basic_auth"` } +// IsEnabled checks if UpstreamAuthentication is enabled for the API. func (u *UpstreamAuth) IsEnabled() bool { return u.Enabled && u.BasicAuth.Enabled } +// UpstreamBasicAuth holds upstream basic authentication configuration. type UpstreamBasicAuth struct { - Enabled bool `bson:"enabled" json:"enabled,omitempty"` - Username string `bson:"username" json:"username"` - Password string `bson:"password" json:"password"` + // Enabled enables upstream basic authentication. + Enabled bool `bson:"enabled" json:"enabled,omitempty"` + // Username is the username to be used for upstream basic authentication. + Username string `bson:"username" json:"username"` + // Password is the password to be used for upstream basic authentication. + Password string `bson:"password" json:"password"` + // HeaderName is the custom header name to be used for upstream basic authentication. + // Defaults to `Authorization`. HeaderName string `bson:"auth_header_name" json:"authHeaderName"` } diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index f7bea08241e..03abd6e9be4 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -551,8 +551,11 @@ func (r *RateLimitEndpoint) ExtractTo(meta *apidef.RateLimitMeta) { meta.Per = r.Per.Seconds() } +// UpstreamAuth holds the configurations related to upstream API authentication. type UpstreamAuth struct { - Enabled bool `bson:"enabled" json:"enabled"` + // Enabled enables upstream API authentication. + Enabled bool `bson:"enabled" json:"enabled"` + // BasicAuth holds the basic authentication configuration for upstream API authentication. BasicAuth *UpstreamBasicAuth `bson:"basicAuth,omitempty" json:"basicAuth,omitempty"` } @@ -584,11 +587,17 @@ func (u *UpstreamAuth) ExtractTo(api *apidef.UpstreamAuth) { u.BasicAuth.ExtractTo(&api.BasicAuth) } +// UpstreamBasicAuth holds upstream basic authentication configuration. type UpstreamBasicAuth struct { - Enabled bool `bson:"enabled" json:"enabled"` + // Enabled enables upstream basic authentication.v + Enabled bool `bson:"enabled" json:"enabled"` + // Username is the username to be used for upstream basic authentication. HeaderName string `bson:"headerName" json:"headerName"` - Username string `bson:"username" json:"username"` - Password string `bson:"password" json:"password"` + // Password is the password to be used for upstream basic authentication. + Username string `bson:"username" json:"username"` + // HeaderName is the custom header name to be used for upstream basic authentication. + // Defaults to `Authorization`. + Password string `bson:"password" json:"password"` } // Fill fills *UpstreamBasicAuth from apidef.UpstreamBasicAuth. From 29fe340b8cdf9e7b383a4e1b8d7ee8c651822ca5 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 11:52:41 +0200 Subject: [PATCH 03/16] add godocs --- apidef/oas/upstream.go | 10 +++++----- gateway/mw_upstream_basic_auth.go | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index 03abd6e9be4..9d322f11af2 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -589,14 +589,14 @@ func (u *UpstreamAuth) ExtractTo(api *apidef.UpstreamAuth) { // UpstreamBasicAuth holds upstream basic authentication configuration. type UpstreamBasicAuth struct { - // Enabled enables upstream basic authentication.v + // Enabled enables upstream basic authentication. Enabled bool `bson:"enabled" json:"enabled"` - // Username is the username to be used for upstream basic authentication. - HeaderName string `bson:"headerName" json:"headerName"` - // Password is the password to be used for upstream basic authentication. - Username string `bson:"username" json:"username"` // HeaderName is the custom header name to be used for upstream basic authentication. // Defaults to `Authorization`. + HeaderName string `bson:"headerName" json:"headerName"` + // Username is the username to be used for upstream basic authentication. + Username string `bson:"username" json:"username"` + // Password is the password to be used for upstream basic authentication. Password string `bson:"password" json:"password"` } diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 4e6397c4bf1..4be7060bf67 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -15,10 +15,12 @@ type UpstreamBasicAuth struct { *BaseMiddleware } +// Name returns the name of middleware. func (t *UpstreamBasicAuth) Name() string { return "UpstreamBasicAuth" } +// EnabledForSpec returns true if the middleware is enabled based on API Spec. func (t *UpstreamBasicAuth) EnabledForSpec() bool { if !t.Spec.UpstreamAuth.Enabled { return false From 504b9fc16efc239cf94fa7a15e74eecb1cdef0af Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 12:04:22 +0200 Subject: [PATCH 04/16] add schema --- apidef/api_definitions.go | 4 +-- apidef/oas/schema/x-tyk-api-gateway.json | 37 ++++++++++++++++++++++++ apidef/oas/upstream.go | 8 ++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 7fee8332a01..2e573efa166 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -784,8 +784,8 @@ func (u *UpstreamAuth) IsEnabled() bool { type UpstreamBasicAuth struct { // Enabled enables upstream basic authentication. Enabled bool `bson:"enabled" json:"enabled,omitempty"` - // Username is the username to be used for upstream basic authentication. - Username string `bson:"username" json:"username"` + // UserName is the username to be used for upstream basic authentication. + UserName string `bson:"user_name" json:"user_name"` // Password is the password to be used for upstream basic authentication. Password string `bson:"password" json:"password"` // HeaderName is the custom header name to be used for upstream basic authentication. diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 8a88d02000c..0cf8690c539 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -1349,6 +1349,9 @@ }, "rateLimit": { "$ref": "#/definitions/X-Tyk-RateLimit" + }, + "authentication": { + "$ref": "#/definitions/X-Tyk-UpstreamAuthentication" } }, "required": [ @@ -2065,6 +2068,40 @@ "X-Tyk-DomainDef": { "type": "string", "pattern": "^([*a-zA-Z0-9-]+(\\.[*a-zA-Z0-9-]+)*)(:\\d+)?$" + }, + "X-Tyk-UpstreamAuthentication": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "basicAuth": { + "$ref": "#/definitions/X-Tyk-UpstreamBasicAuthentication" + } + }, + "required": [ + "enabled" + ] + }, + "X-Tyk-UpstreamBasicAuthentication": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "headerName": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "enabled" + ] } } } diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index 9d322f11af2..ea3de33b7f6 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -594,8 +594,8 @@ type UpstreamBasicAuth struct { // HeaderName is the custom header name to be used for upstream basic authentication. // Defaults to `Authorization`. HeaderName string `bson:"headerName" json:"headerName"` - // Username is the username to be used for upstream basic authentication. - Username string `bson:"username" json:"username"` + // UserName is the username to be used for upstream basic authentication. + UserName string `bson:"userName" json:"userName"` // Password is the password to be used for upstream basic authentication. Password string `bson:"password" json:"password"` } @@ -604,7 +604,7 @@ type UpstreamBasicAuth struct { func (u *UpstreamBasicAuth) Fill(api apidef.UpstreamBasicAuth) { u.Enabled = api.Enabled u.HeaderName = api.HeaderName - u.Username = api.Username + u.UserName = api.UserName u.Password = api.Password } @@ -613,6 +613,6 @@ func (u *UpstreamBasicAuth) ExtractTo(api *apidef.UpstreamBasicAuth) { api.Enabled = u.Enabled api.Enabled = u.Enabled api.HeaderName = u.HeaderName - api.Username = u.Username + api.UserName = u.UserName api.Password = u.Password } From a7de91418ed8c52decc5d032b112b6921bc193e8 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 12:10:08 +0200 Subject: [PATCH 05/16] Revert userName to username --- apidef/api_definitions.go | 4 ++-- apidef/oas/schema/x-tyk-api-gateway.json | 2 +- apidef/oas/upstream.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 2e573efa166..7fee8332a01 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -784,8 +784,8 @@ func (u *UpstreamAuth) IsEnabled() bool { type UpstreamBasicAuth struct { // Enabled enables upstream basic authentication. Enabled bool `bson:"enabled" json:"enabled,omitempty"` - // UserName is the username to be used for upstream basic authentication. - UserName string `bson:"user_name" json:"user_name"` + // Username is the username to be used for upstream basic authentication. + Username string `bson:"username" json:"username"` // Password is the password to be used for upstream basic authentication. Password string `bson:"password" json:"password"` // HeaderName is the custom header name to be used for upstream basic authentication. diff --git a/apidef/oas/schema/x-tyk-api-gateway.json b/apidef/oas/schema/x-tyk-api-gateway.json index 0cf8690c539..84b21cd6302 100644 --- a/apidef/oas/schema/x-tyk-api-gateway.json +++ b/apidef/oas/schema/x-tyk-api-gateway.json @@ -2092,7 +2092,7 @@ "headerName": { "type": "string" }, - "userName": { + "username": { "type": "string" }, "password": { diff --git a/apidef/oas/upstream.go b/apidef/oas/upstream.go index ea3de33b7f6..9d322f11af2 100644 --- a/apidef/oas/upstream.go +++ b/apidef/oas/upstream.go @@ -594,8 +594,8 @@ type UpstreamBasicAuth struct { // HeaderName is the custom header name to be used for upstream basic authentication. // Defaults to `Authorization`. HeaderName string `bson:"headerName" json:"headerName"` - // UserName is the username to be used for upstream basic authentication. - UserName string `bson:"userName" json:"userName"` + // Username is the username to be used for upstream basic authentication. + Username string `bson:"username" json:"username"` // Password is the password to be used for upstream basic authentication. Password string `bson:"password" json:"password"` } @@ -604,7 +604,7 @@ type UpstreamBasicAuth struct { func (u *UpstreamBasicAuth) Fill(api apidef.UpstreamBasicAuth) { u.Enabled = api.Enabled u.HeaderName = api.HeaderName - u.UserName = api.UserName + u.Username = api.Username u.Password = api.Password } @@ -613,6 +613,6 @@ func (u *UpstreamBasicAuth) ExtractTo(api *apidef.UpstreamBasicAuth) { api.Enabled = u.Enabled api.Enabled = u.Enabled api.HeaderName = u.HeaderName - api.UserName = u.UserName + api.Username = u.Username api.Password = u.Password } From 6dfa2651baa1c37ae76c008d71b22e5df0407ee0 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 1 Oct 2024 12:57:16 +0200 Subject: [PATCH 06/16] update with upstream_auth schema --- apidef/api_definitions.go | 2 +- apidef/schema.go | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 7fee8332a01..9b49975d169 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -790,7 +790,7 @@ type UpstreamBasicAuth struct { Password string `bson:"password" json:"password"` // HeaderName is the custom header name to be used for upstream basic authentication. // Defaults to `Authorization`. - HeaderName string `bson:"auth_header_name" json:"authHeaderName"` + HeaderName string `bson:"header_name" json:"header_name"` } type AnalyticsPluginConfig struct { diff --git a/apidef/schema.go b/apidef/schema.go index 04c7eb0682f..c080bddec47 100644 --- a/apidef/schema.go +++ b/apidef/schema.go @@ -761,7 +761,32 @@ const Schema = `{ }, "detailed_tracing": { "type": "boolean" - } + }, + "upstream_auth": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "basic_auth": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "header_name": { + "type": "string" + } + } + } + } + } }, "required": [ "name", From 4520a1770dde5e153121395a595a40f7d7dc4f7f Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 7 Oct 2024 10:28:23 +0200 Subject: [PATCH 07/16] append Basic prefix --- gateway/mw_upstream_basic_auth.go | 3 +- gateway/mw_upstream_basic_auth_test.go | 143 ------------------------- 2 files changed, 2 insertions(+), 144 deletions(-) delete mode 100644 gateway/mw_upstream_basic_auth_test.go diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 4be7060bf67..087dbedd7d9 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -45,7 +45,8 @@ func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Reques payload := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", basicAuthConfig.Username, basicAuthConfig.Password))) - ctx.SetUpstreamAuthValue(r, payload) + headerValue := fmt.Sprintf("Basic %s", payload) + ctx.SetUpstreamAuthValue(r, headerValue) return nil, http.StatusOK } diff --git a/gateway/mw_upstream_basic_auth_test.go b/gateway/mw_upstream_basic_auth_test.go deleted file mode 100644 index b0c4966bb63..00000000000 --- a/gateway/mw_upstream_basic_auth_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package gateway - -import ( - "encoding/base64" - "encoding/json" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/TykTechnologies/tyk/apidef" - "github.com/TykTechnologies/tyk/header" - "github.com/TykTechnologies/tyk/test" -) - -func TestUpstreamBasicAuthentication(t *testing.T) { - - ts := StartTest(nil) - t.Cleanup(func() { - ts.Close() - }) - - userName, password, customAuthHeader := "user", "password", "Custom-Auth" - expectedAuth := base64.StdEncoding.EncodeToString([]byte(userName + ":" + password)) - - ts.Gw.BuildAndLoadAPI( - func(spec *APISpec) { - spec.Proxy.ListenPath = "/upstream-basic-auth-enabled/" - spec.UseKeylessAccess = true - spec.UpstreamAuth = apidef.UpstreamAuth{ - Enabled: true, - BasicAuth: apidef.UpstreamBasicAuth{ - Enabled: true, - Username: userName, - Password: password, - }, - } - spec.Proxy.StripListenPath = true - }, func(spec *APISpec) { - spec.Proxy.ListenPath = "/upstream-basic-auth-custom-header/" - spec.UseKeylessAccess = true - spec.UpstreamAuth = apidef.UpstreamAuth{ - Enabled: true, - BasicAuth: apidef.UpstreamBasicAuth{ - Enabled: true, - Username: userName, - Password: password, - HeaderName: customAuthHeader, - }, - } - spec.Proxy.StripListenPath = true - }, - func(spec *APISpec) { - spec.Proxy.ListenPath = "/upstream-basic-auth-disabled/" - spec.UseKeylessAccess = true - spec.UpstreamAuth = apidef.UpstreamAuth{ - Enabled: true, - BasicAuth: apidef.UpstreamBasicAuth{ - Enabled: false, - Username: userName, - Password: password, - }, - } - spec.Proxy.StripListenPath = true - }, - func(spec *APISpec) { - spec.Proxy.ListenPath = "/upstream-auth-disabled/" - spec.UseKeylessAccess = true - spec.UpstreamAuth = apidef.UpstreamAuth{ - Enabled: false, - } - spec.Proxy.StripListenPath = true - }, - ) - - _, _ = ts.Run(t, test.TestCases{ - { - Path: "/upstream-basic-auth-enabled/", - Code: http.StatusOK, - BodyMatchFunc: func(body []byte) bool { - resp := struct { - Headers map[string]string `json:"headers"` - }{} - err := json.Unmarshal(body, &resp) - assert.NoError(t, err) - - assert.Contains(t, resp.Headers, header.Authorization) - assert.NotEmpty(t, resp.Headers[header.Authorization]) - assert.Equal(t, expectedAuth, resp.Headers[header.Authorization]) - - return true - }, - }, - { - Path: "/upstream-basic-auth-custom-header/", - Code: http.StatusOK, - BodyMatchFunc: func(body []byte) bool { - resp := struct { - Headers map[string]string `json:"headers"` - }{} - err := json.Unmarshal(body, &resp) - assert.NoError(t, err) - - assert.Contains(t, resp.Headers, customAuthHeader) - assert.NotEmpty(t, resp.Headers[customAuthHeader]) - assert.Equal(t, expectedAuth, resp.Headers[customAuthHeader]) - - return true - }, - }, - { - Path: "/upstream-basic-auth-disabled/", - Code: http.StatusOK, - BodyMatchFunc: func(body []byte) bool { - resp := struct { - Headers map[string]string `json:"headers"` - }{} - err := json.Unmarshal(body, &resp) - assert.NoError(t, err) - - assert.NotContains(t, resp.Headers, header.Authorization) - - return true - }, - }, - { - Path: "/upstream-auth-disabled/", - Code: http.StatusOK, - BodyMatchFunc: func(body []byte) bool { - resp := struct { - Headers map[string]string `json:"headers"` - }{} - err := json.Unmarshal(body, &resp) - assert.NoError(t, err) - - assert.NotContains(t, resp.Headers, header.Authorization) - - return true - }, - }, - }...) - -} From 4118e4c9c007b8b25fffedc1786ea46672c6b280 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 7 Oct 2024 12:09:39 +0200 Subject: [PATCH 08/16] update with upstream basic auth test --- gateway/mw_upstream_basic_auth_test.go | 143 +++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 gateway/mw_upstream_basic_auth_test.go diff --git a/gateway/mw_upstream_basic_auth_test.go b/gateway/mw_upstream_basic_auth_test.go new file mode 100644 index 00000000000..b0c4966bb63 --- /dev/null +++ b/gateway/mw_upstream_basic_auth_test.go @@ -0,0 +1,143 @@ +package gateway + +import ( + "encoding/base64" + "encoding/json" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/TykTechnologies/tyk/apidef" + "github.com/TykTechnologies/tyk/header" + "github.com/TykTechnologies/tyk/test" +) + +func TestUpstreamBasicAuthentication(t *testing.T) { + + ts := StartTest(nil) + t.Cleanup(func() { + ts.Close() + }) + + userName, password, customAuthHeader := "user", "password", "Custom-Auth" + expectedAuth := base64.StdEncoding.EncodeToString([]byte(userName + ":" + password)) + + ts.Gw.BuildAndLoadAPI( + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-enabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: true, + Username: userName, + Password: password, + }, + } + spec.Proxy.StripListenPath = true + }, func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-custom-header/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: true, + Username: userName, + Password: password, + HeaderName: customAuthHeader, + }, + } + spec.Proxy.StripListenPath = true + }, + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-basic-auth-disabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: true, + BasicAuth: apidef.UpstreamBasicAuth{ + Enabled: false, + Username: userName, + Password: password, + }, + } + spec.Proxy.StripListenPath = true + }, + func(spec *APISpec) { + spec.Proxy.ListenPath = "/upstream-auth-disabled/" + spec.UseKeylessAccess = true + spec.UpstreamAuth = apidef.UpstreamAuth{ + Enabled: false, + } + spec.Proxy.StripListenPath = true + }, + ) + + _, _ = ts.Run(t, test.TestCases{ + { + Path: "/upstream-basic-auth-enabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.Contains(t, resp.Headers, header.Authorization) + assert.NotEmpty(t, resp.Headers[header.Authorization]) + assert.Equal(t, expectedAuth, resp.Headers[header.Authorization]) + + return true + }, + }, + { + Path: "/upstream-basic-auth-custom-header/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.Contains(t, resp.Headers, customAuthHeader) + assert.NotEmpty(t, resp.Headers[customAuthHeader]) + assert.Equal(t, expectedAuth, resp.Headers[customAuthHeader]) + + return true + }, + }, + { + Path: "/upstream-basic-auth-disabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.NotContains(t, resp.Headers, header.Authorization) + + return true + }, + }, + { + Path: "/upstream-auth-disabled/", + Code: http.StatusOK, + BodyMatchFunc: func(body []byte) bool { + resp := struct { + Headers map[string]string `json:"headers"` + }{} + err := json.Unmarshal(body, &resp) + assert.NoError(t, err) + + assert.NotContains(t, resp.Headers, header.Authorization) + + return true + }, + }, + }...) + +} From d4e3b0e3dae68f5f347c309789fcbe6a8e27fff6 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 7 Oct 2024 12:10:15 +0200 Subject: [PATCH 09/16] update with upstream basic auth test --- gateway/mw_upstream_basic_auth_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gateway/mw_upstream_basic_auth_test.go b/gateway/mw_upstream_basic_auth_test.go index b0c4966bb63..8ecdf5c120d 100644 --- a/gateway/mw_upstream_basic_auth_test.go +++ b/gateway/mw_upstream_basic_auth_test.go @@ -3,6 +3,7 @@ package gateway import ( "encoding/base64" "encoding/json" + "fmt" "net/http" "testing" @@ -21,7 +22,7 @@ func TestUpstreamBasicAuthentication(t *testing.T) { }) userName, password, customAuthHeader := "user", "password", "Custom-Auth" - expectedAuth := base64.StdEncoding.EncodeToString([]byte(userName + ":" + password)) + expectedAuth := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(userName+":"+password))) ts.Gw.BuildAndLoadAPI( func(spec *APISpec) { From 2df5906c13fc06e2ec3f4830045b5bd57b1f0d81 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 13:36:22 +0200 Subject: [PATCH 10/16] refactor ctx methods. add upstream auth provider. --- ctx/ctx.go | 45 +-------------- gateway/mw_upstream_basic_auth.go | 24 ++++++-- gateway/reverse_proxy.go | 10 +--- internal/ctxutil/ctx.go | 38 ++++++++++++ internal/ctxutil/ctx_test.go | 96 +++++++++++++++++++++++++++++++ internal/proxy/upstream_auth.go | 7 +++ 6 files changed, 165 insertions(+), 55 deletions(-) create mode 100644 internal/ctxutil/ctx.go create mode 100644 internal/ctxutil/ctx_test.go create mode 100644 internal/proxy/upstream_auth.go diff --git a/ctx/ctx.go b/ctx/ctx.go index b13b743e837..f6265ee57d7 100644 --- a/ctx/ctx.go +++ b/ctx/ctx.go @@ -3,6 +3,7 @@ package ctx import ( "context" "encoding/json" + "github.com/TykTechnologies/tyk/internal/ctxutil" "net/http" "github.com/TykTechnologies/tyk/apidef/oas" @@ -51,18 +52,8 @@ const ( // CacheOptions holds cache options required for cache writer middleware. CacheOptions OASDefinition - - // UpstreamAuthHeader sets the header name to be used for upstream authentication. - UpstreamAuthHeader - // UpstreamAuthValue sets the value for upstream authentication. - UpstreamAuthValue ) -func setContext(r *http.Request, ctx context.Context) { - r2 := r.WithContext(ctx) - *r = *r2 -} - func ctxSetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, hashKey bool) { if s == nil { @@ -86,7 +77,7 @@ func ctxSetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, h s.Touch() } - setContext(r, ctx) + ctxutil.SetContext(r, ctx) } func GetAuthToken(r *http.Request) string { @@ -124,7 +115,7 @@ func SetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, hash func SetDefinition(r *http.Request, s *apidef.APIDefinition) { ctx := r.Context() ctx = context.WithValue(ctx, Definition, s) - setContext(r, ctx) + ctxutil.SetContext(r, ctx) } func GetDefinition(r *http.Request) *apidef.APIDefinition { @@ -163,33 +154,3 @@ func GetOASDefinition(r *http.Request) *oas.OAS { return ret } - -// SetUpstreamAuthHeader sets the header name to be used for upstream authentication. -func SetUpstreamAuthHeader(r *http.Request, name string) { - ctx := r.Context() - ctx = context.WithValue(ctx, UpstreamAuthHeader, name) - setContext(r, ctx) -} - -// GetUpstreamAuthHeader returns the header name to be used for upstream authentication. -func GetUpstreamAuthHeader(r *http.Request) string { - if v := r.Context().Value(UpstreamAuthHeader); v != nil { - return v.(string) - } - return "" -} - -// SetUpstreamAuthValue sets the auth header value to be used for upstream authentication. -func SetUpstreamAuthValue(r *http.Request, name string) { - ctx := r.Context() - ctx = context.WithValue(ctx, UpstreamAuthValue, name) - setContext(r, ctx) -} - -// GetUpstreamAuthValue gets the auth header value to be used for upstream authentication. -func GetUpstreamAuthValue(r *http.Request) string { - if v := r.Context().Value(UpstreamAuthValue); v != nil { - return v.(string) - } - return "" -} diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 087dbedd7d9..15b34ed66a3 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -3,9 +3,9 @@ package gateway import ( "encoding/base64" "fmt" + "github.com/TykTechnologies/tyk/internal/ctxutil" "net/http" - "github.com/TykTechnologies/tyk/ctx" "github.com/TykTechnologies/tyk/header" ) @@ -37,16 +37,28 @@ func (t *UpstreamBasicAuth) EnabledForSpec() bool { func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { basicAuthConfig := t.Spec.UpstreamAuth.BasicAuth - authHeaderName := header.Authorization + upstreamBasicAuthProvider := UpstreamBasicAuthProvider{ + HeaderName: header.Authorization, + } + if basicAuthConfig.HeaderName != "" { - authHeaderName = basicAuthConfig.HeaderName + upstreamBasicAuthProvider.HeaderName = basicAuthConfig.HeaderName } - ctx.SetUpstreamAuthHeader(r, authHeaderName) payload := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", basicAuthConfig.Username, basicAuthConfig.Password))) - headerValue := fmt.Sprintf("Basic %s", payload) - ctx.SetUpstreamAuthValue(r, headerValue) + upstreamBasicAuthProvider.AuthValue = fmt.Sprintf("Basic %s", payload) + ctxutil.SetUpstreamAuth(r, upstreamBasicAuthProvider) return nil, http.StatusOK } + +// UpstreamBasicAuthProvider implements auth provider +type UpstreamBasicAuthProvider struct { + HeaderName string + AuthValue string +} + +func (u UpstreamBasicAuthProvider) Fill(r *http.Request) { + r.Header.Add(u.HeaderName, u.AuthValue) +} diff --git a/gateway/reverse_proxy.go b/gateway/reverse_proxy.go index f252677b3f2..ff773ca9bb9 100644 --- a/gateway/reverse_proxy.go +++ b/gateway/reverse_proxy.go @@ -18,6 +18,7 @@ import ( "crypto/x509" "errors" "fmt" + "github.com/TykTechnologies/tyk/internal/ctxutil" "io" "io/ioutil" "net" @@ -1853,11 +1854,6 @@ func (p *ReverseProxy) addAuthInfo(outReq, req *http.Request) { return } - authHeaderName := ctx.GetUpstreamAuthHeader(req) - if authHeaderName == "" { - return - } - - authHeaderValue := ctx.GetUpstreamAuthValue(req) - outReq.Header.Add(authHeaderName, authHeaderValue) + authProvider := ctxutil.GetUpstreamAuth(req) + authProvider.Fill(outReq) } diff --git a/internal/ctxutil/ctx.go b/internal/ctxutil/ctx.go new file mode 100644 index 00000000000..43c4eff856f --- /dev/null +++ b/internal/ctxutil/ctx.go @@ -0,0 +1,38 @@ +package ctxutil + +import ( + "context" + "github.com/TykTechnologies/tyk/internal/proxy" + "net/http" +) + +const ( + upstreamAuth = "upstream-auth" +) + +func SetContext(r *http.Request, ctx context.Context) { + r2 := r.WithContext(ctx) + *r = *r2 +} + +// SetUpstreamAuth sets the header name to be used for upstream authentication. +func SetUpstreamAuth(r *http.Request, auth proxy.UpstreamAuthProvider) { + ctx := r.Context() + ctx = context.WithValue(ctx, upstreamAuth, auth) + SetContext(r, ctx) +} + +// GetUpstreamAuth returns the header name to be used for upstream authentication. +func GetUpstreamAuth(r *http.Request) proxy.UpstreamAuthProvider { + auth := r.Context().Value(upstreamAuth) + if auth == nil { + return nil + } + + provider, ok := auth.(proxy.UpstreamAuthProvider) + if !ok { + return nil + } + + return provider +} diff --git a/internal/ctxutil/ctx_test.go b/internal/ctxutil/ctx_test.go new file mode 100644 index 00000000000..bc950a1fc52 --- /dev/null +++ b/internal/ctxutil/ctx_test.go @@ -0,0 +1,96 @@ +package ctxutil_test + +import ( + "context" + "github.com/TykTechnologies/tyk/internal/ctxutil" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func createReq(tb testing.TB) *http.Request { + tb.Helper() + req, err := http.NewRequest(http.MethodGet, "http://example.com", nil) + assert.NoError(tb, err) + return req +} + +type mockUpstreamAuthProvider struct{} + +// Fill is the mock implementation for upstream auth provider. +func (m *mockUpstreamAuthProvider) Fill(r *http.Request) {} + +func TestUpstreamAuth(t *testing.T) { + t.Run("valid auth provider", func(t *testing.T) { + mockAuthProvider := &mockUpstreamAuthProvider{} + req := createReq(t) + + ctxutil.SetUpstreamAuth(req, mockAuthProvider) + + // Retrieve the auth provider from the request's context to verify it was set + retrievedAuth := ctxutil.GetUpstreamAuth(req) + assert.NotNil(t, retrievedAuth) + assert.Equal(t, mockAuthProvider, retrievedAuth) + }) + + t.Run("no auth provider", func(t *testing.T) { + req := createReq(t) + + retrievedAuth := ctxutil.GetUpstreamAuth(req) + assert.Nil(t, retrievedAuth) + }) + + t.Run("invalid auth provider", func(t *testing.T) { + req := createReq(t) + + // Set a context with a value that is not of type proxy.UpstreamAuthProvider + ctx := context.WithValue(req.Context(), "upstream-auth", "invalid-type") + ctxutil.SetContext(req, ctx) + + retrievedAuth := ctxutil.GetUpstreamAuth(req) + assert.Nil(t, retrievedAuth) + }) +} + +func TestSetContext(t *testing.T) { + t.Run("add key", func(t *testing.T) { + req := createReq(t) + + // Create a new context with a key-value pair + ctx := context.WithValue(context.Background(), "key", "value") + + // Call SetContext to update the request's context + ctxutil.SetContext(req, ctx) + + // Verify that the request's context has been updated + retrievedValue := req.Context().Value("key") + assert.Equal(t, "value", retrievedValue) + }) + + t.Run("override key", func(t *testing.T) { + + req := createReq(t) + existingCtx := context.WithValue(context.Background(), "existingKey", "existingValue") + req = req.WithContext(existingCtx) + + // Create a new context to override the existing context + newCtx := context.WithValue(context.Background(), "newKey", "newValue") + + // Call SetContext to update the request's context with the new context + ctxutil.SetContext(req, newCtx) + + assert.Nil(t, req.Context().Value("existingKey")) + assert.Equal(t, "newValue", req.Context().Value("newKey")) + }) + + t.Run("empty context", func(t *testing.T) { + req := createReq(t) + + emptyCtx := context.Background() + + ctxutil.SetContext(req, emptyCtx) + + assert.Equal(t, emptyCtx, req.Context()) + }) +} diff --git a/internal/proxy/upstream_auth.go b/internal/proxy/upstream_auth.go new file mode 100644 index 00000000000..28c62bf52bb --- /dev/null +++ b/internal/proxy/upstream_auth.go @@ -0,0 +1,7 @@ +package proxy + +import "net/http" + +type UpstreamAuthProvider interface { + Fill(r *http.Request) +} From b439f635efd3c27c8428e07df9ca2f7ee3c43f28 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 14:18:57 +0200 Subject: [PATCH 11/16] address review comments --- ctx/ctx.go | 3 ++- gateway/mw_upstream_basic_auth.go | 3 ++- gateway/mw_upstream_basic_auth_test.go | 3 +-- gateway/reverse_proxy.go | 8 +++++--- internal/ctxutil/ctx.go | 13 ++++++++----- internal/ctxutil/ctx_test.go | 13 +++++-------- internal/model/upstream_auth.go | 14 ++++++++++++++ internal/proxy/upstream_auth.go | 7 ------- 8 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 internal/model/upstream_auth.go delete mode 100644 internal/proxy/upstream_auth.go diff --git a/ctx/ctx.go b/ctx/ctx.go index f6265ee57d7..6b2d1a286cb 100644 --- a/ctx/ctx.go +++ b/ctx/ctx.go @@ -3,9 +3,10 @@ package ctx import ( "context" "encoding/json" - "github.com/TykTechnologies/tyk/internal/ctxutil" "net/http" + "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/apidef/oas" "github.com/TykTechnologies/tyk/config" diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 15b34ed66a3..3880b0bd1e2 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -3,9 +3,10 @@ package gateway import ( "encoding/base64" "fmt" - "github.com/TykTechnologies/tyk/internal/ctxutil" "net/http" + "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/header" ) diff --git a/gateway/mw_upstream_basic_auth_test.go b/gateway/mw_upstream_basic_auth_test.go index 8ecdf5c120d..a2b1870538a 100644 --- a/gateway/mw_upstream_basic_auth_test.go +++ b/gateway/mw_upstream_basic_auth_test.go @@ -15,7 +15,6 @@ import ( ) func TestUpstreamBasicAuthentication(t *testing.T) { - ts := StartTest(nil) t.Cleanup(func() { ts.Close() @@ -74,7 +73,7 @@ func TestUpstreamBasicAuthentication(t *testing.T) { }, ) - _, _ = ts.Run(t, test.TestCases{ + ts.Run(t, test.TestCases{ { Path: "/upstream-basic-auth-enabled/", Code: http.StatusOK, diff --git a/gateway/reverse_proxy.go b/gateway/reverse_proxy.go index ff773ca9bb9..af02be9c701 100644 --- a/gateway/reverse_proxy.go +++ b/gateway/reverse_proxy.go @@ -18,7 +18,6 @@ import ( "crypto/x509" "errors" "fmt" - "github.com/TykTechnologies/tyk/internal/ctxutil" "io" "io/ioutil" "net" @@ -30,6 +29,8 @@ import ( "sync" "time" + "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/akutz/memconn" "github.com/gorilla/websocket" "github.com/opentracing/opentracing-go" @@ -1854,6 +1855,7 @@ func (p *ReverseProxy) addAuthInfo(outReq, req *http.Request) { return } - authProvider := ctxutil.GetUpstreamAuth(req) - authProvider.Fill(outReq) + if authProvider := ctxutil.GetUpstreamAuth(req); authProvider != nil { + authProvider.Fill(outReq) + } } diff --git a/internal/ctxutil/ctx.go b/internal/ctxutil/ctx.go index 43c4eff856f..1a0aa58b8b5 100644 --- a/internal/ctxutil/ctx.go +++ b/internal/ctxutil/ctx.go @@ -2,12 +2,15 @@ package ctxutil import ( "context" - "github.com/TykTechnologies/tyk/internal/proxy" "net/http" + + "github.com/TykTechnologies/tyk/internal/model" ) +type ContextKey string + const ( - upstreamAuth = "upstream-auth" + upstreamAuth = ContextKey("upstream-auth") ) func SetContext(r *http.Request, ctx context.Context) { @@ -16,20 +19,20 @@ func SetContext(r *http.Request, ctx context.Context) { } // SetUpstreamAuth sets the header name to be used for upstream authentication. -func SetUpstreamAuth(r *http.Request, auth proxy.UpstreamAuthProvider) { +func SetUpstreamAuth(r *http.Request, auth model.UpstreamAuthProvider) { ctx := r.Context() ctx = context.WithValue(ctx, upstreamAuth, auth) SetContext(r, ctx) } // GetUpstreamAuth returns the header name to be used for upstream authentication. -func GetUpstreamAuth(r *http.Request) proxy.UpstreamAuthProvider { +func GetUpstreamAuth(r *http.Request) model.UpstreamAuthProvider { auth := r.Context().Value(upstreamAuth) if auth == nil { return nil } - provider, ok := auth.(proxy.UpstreamAuthProvider) + provider, ok := auth.(model.UpstreamAuthProvider) if !ok { return nil } diff --git a/internal/ctxutil/ctx_test.go b/internal/ctxutil/ctx_test.go index bc950a1fc52..afcf22c3a25 100644 --- a/internal/ctxutil/ctx_test.go +++ b/internal/ctxutil/ctx_test.go @@ -2,10 +2,12 @@ package ctxutil_test import ( "context" - "github.com/TykTechnologies/tyk/internal/ctxutil" "net/http" "testing" + "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/internal/model" + "github.com/stretchr/testify/assert" ) @@ -16,14 +18,9 @@ func createReq(tb testing.TB) *http.Request { return req } -type mockUpstreamAuthProvider struct{} - -// Fill is the mock implementation for upstream auth provider. -func (m *mockUpstreamAuthProvider) Fill(r *http.Request) {} - func TestUpstreamAuth(t *testing.T) { t.Run("valid auth provider", func(t *testing.T) { - mockAuthProvider := &mockUpstreamAuthProvider{} + mockAuthProvider := &model.MockUpstreamAuthProvider{} req := createReq(t) ctxutil.SetUpstreamAuth(req, mockAuthProvider) @@ -45,7 +42,7 @@ func TestUpstreamAuth(t *testing.T) { req := createReq(t) // Set a context with a value that is not of type proxy.UpstreamAuthProvider - ctx := context.WithValue(req.Context(), "upstream-auth", "invalid-type") + ctx := context.WithValue(req.Context(), ctxutil.ContextKey("upstream-auth"), "invalid-type") ctxutil.SetContext(req, ctx) retrievedAuth := ctxutil.GetUpstreamAuth(req) diff --git a/internal/model/upstream_auth.go b/internal/model/upstream_auth.go new file mode 100644 index 00000000000..bd907995a24 --- /dev/null +++ b/internal/model/upstream_auth.go @@ -0,0 +1,14 @@ +package model + +import "net/http" + +// UpstreamAuthProvider is an interface that can fill in upstream authentication details to the request. +type UpstreamAuthProvider interface { + Fill(r *http.Request) +} + +// MockUpstreamAuthProvider is a mock implementation of UpstreamAuthProvider. +type MockUpstreamAuthProvider struct{} + +// Fill is a mock implementation to be used in tests. +func (m *MockUpstreamAuthProvider) Fill(r *http.Request) {} diff --git a/internal/proxy/upstream_auth.go b/internal/proxy/upstream_auth.go deleted file mode 100644 index 28c62bf52bb..00000000000 --- a/internal/proxy/upstream_auth.go +++ /dev/null @@ -1,7 +0,0 @@ -package proxy - -import "net/http" - -type UpstreamAuthProvider interface { - Fill(r *http.Request) -} From 25c31c4371adf7407a1dc8f4cdf8b19f51b8cc47 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 14:27:40 +0200 Subject: [PATCH 12/16] refactor httputil.headers.go --- gateway/mw_upstream_basic_auth.go | 7 ++----- internal/httputil/headers.go | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 internal/httputil/headers.go diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 3880b0bd1e2..373b35648b7 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -1,8 +1,7 @@ package gateway import ( - "encoding/base64" - "fmt" + "github.com/TykTechnologies/tyk/internal/httputil" "net/http" "github.com/TykTechnologies/tyk/internal/ctxutil" @@ -46,9 +45,7 @@ func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Reques upstreamBasicAuthProvider.HeaderName = basicAuthConfig.HeaderName } - payload := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", basicAuthConfig.Username, basicAuthConfig.Password))) - - upstreamBasicAuthProvider.AuthValue = fmt.Sprintf("Basic %s", payload) + upstreamBasicAuthProvider.AuthValue = httputil.AuthHeader(basicAuthConfig.Username, basicAuthConfig.Password) ctxutil.SetUpstreamAuth(r, upstreamBasicAuthProvider) return nil, http.StatusOK diff --git a/internal/httputil/headers.go b/internal/httputil/headers.go new file mode 100644 index 00000000000..4aed5f6749e --- /dev/null +++ b/internal/httputil/headers.go @@ -0,0 +1,26 @@ +package httputil + +import ( + "encoding/base64" + "fmt" + "strings" +) + +// CORSHeaders is a list of CORS headers. +var CORSHeaders = []string{ + "Access-Control-Allow-Origin", + "Access-Control-Expose-Headers", + "Access-Control-Max-Age", + "Access-Control-Allow-Credentials", + "Access-Control-Allow-Methods", + "Access-Control-Allow-Headers", +} + +// AuthHeader will take username and password and return +// "Basic " + base64 encoded `username:password` for use +// in an Authorization header. +func AuthHeader(username, password string) string { + toEncode := strings.Join([]string{username, password}, ":") + encodedPass := base64.StdEncoding.EncodeToString([]byte(toEncode)) + return fmt.Sprintf("Basic %s", encodedPass) +} From a63094b80c7ce2d6916ce348c2ed94728900a4f1 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 14:44:44 +0200 Subject: [PATCH 13/16] refactor ctxutil to httputil --- ctx/ctx.go | 6 ++--- gateway/mw_upstream_basic_auth.go | 5 ++-- gateway/reverse_proxy.go | 4 +--- .../{ctxutil/ctx.go => httputil/context.go} | 2 +- .../ctx_test.go => httputil/context_test.go} | 23 ++++++++++--------- 5 files changed, 19 insertions(+), 21 deletions(-) rename internal/{ctxutil/ctx.go => httputil/context.go} (97%) rename internal/{ctxutil/ctx_test.go => httputil/context_test.go} (80%) diff --git a/ctx/ctx.go b/ctx/ctx.go index 6b2d1a286cb..43a31a2d838 100644 --- a/ctx/ctx.go +++ b/ctx/ctx.go @@ -5,7 +5,7 @@ import ( "encoding/json" "net/http" - "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/internal/httputil" "github.com/TykTechnologies/tyk/apidef/oas" @@ -78,7 +78,7 @@ func ctxSetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, h s.Touch() } - ctxutil.SetContext(r, ctx) + httputil.SetContext(r, ctx) } func GetAuthToken(r *http.Request) string { @@ -116,7 +116,7 @@ func SetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, hash func SetDefinition(r *http.Request, s *apidef.APIDefinition) { ctx := r.Context() ctx = context.WithValue(ctx, Definition, s) - ctxutil.SetContext(r, ctx) + httputil.SetContext(r, ctx) } func GetDefinition(r *http.Request) *apidef.APIDefinition { diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 373b35648b7..696b58d4794 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -1,10 +1,9 @@ package gateway import ( - "github.com/TykTechnologies/tyk/internal/httputil" "net/http" - "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/internal/httputil" "github.com/TykTechnologies/tyk/header" ) @@ -47,7 +46,7 @@ func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Reques upstreamBasicAuthProvider.AuthValue = httputil.AuthHeader(basicAuthConfig.Username, basicAuthConfig.Password) - ctxutil.SetUpstreamAuth(r, upstreamBasicAuthProvider) + httputil.SetUpstreamAuth(r, upstreamBasicAuthProvider) return nil, http.StatusOK } diff --git a/gateway/reverse_proxy.go b/gateway/reverse_proxy.go index af02be9c701..820c4201638 100644 --- a/gateway/reverse_proxy.go +++ b/gateway/reverse_proxy.go @@ -29,8 +29,6 @@ import ( "sync" "time" - "github.com/TykTechnologies/tyk/internal/ctxutil" - "github.com/akutz/memconn" "github.com/gorilla/websocket" "github.com/opentracing/opentracing-go" @@ -1855,7 +1853,7 @@ func (p *ReverseProxy) addAuthInfo(outReq, req *http.Request) { return } - if authProvider := ctxutil.GetUpstreamAuth(req); authProvider != nil { + if authProvider := httputil.GetUpstreamAuth(req); authProvider != nil { authProvider.Fill(outReq) } } diff --git a/internal/ctxutil/ctx.go b/internal/httputil/context.go similarity index 97% rename from internal/ctxutil/ctx.go rename to internal/httputil/context.go index 1a0aa58b8b5..d019d3da769 100644 --- a/internal/ctxutil/ctx.go +++ b/internal/httputil/context.go @@ -1,4 +1,4 @@ -package ctxutil +package httputil import ( "context" diff --git a/internal/ctxutil/ctx_test.go b/internal/httputil/context_test.go similarity index 80% rename from internal/ctxutil/ctx_test.go rename to internal/httputil/context_test.go index afcf22c3a25..a9a59083054 100644 --- a/internal/ctxutil/ctx_test.go +++ b/internal/httputil/context_test.go @@ -1,11 +1,12 @@ -package ctxutil_test +package httputil_test import ( "context" "net/http" "testing" - "github.com/TykTechnologies/tyk/internal/ctxutil" + "github.com/TykTechnologies/tyk/internal/httputil" + "github.com/TykTechnologies/tyk/internal/model" "github.com/stretchr/testify/assert" @@ -23,10 +24,10 @@ func TestUpstreamAuth(t *testing.T) { mockAuthProvider := &model.MockUpstreamAuthProvider{} req := createReq(t) - ctxutil.SetUpstreamAuth(req, mockAuthProvider) + httputil.SetUpstreamAuth(req, mockAuthProvider) // Retrieve the auth provider from the request's context to verify it was set - retrievedAuth := ctxutil.GetUpstreamAuth(req) + retrievedAuth := httputil.GetUpstreamAuth(req) assert.NotNil(t, retrievedAuth) assert.Equal(t, mockAuthProvider, retrievedAuth) }) @@ -34,7 +35,7 @@ func TestUpstreamAuth(t *testing.T) { t.Run("no auth provider", func(t *testing.T) { req := createReq(t) - retrievedAuth := ctxutil.GetUpstreamAuth(req) + retrievedAuth := httputil.GetUpstreamAuth(req) assert.Nil(t, retrievedAuth) }) @@ -42,10 +43,10 @@ func TestUpstreamAuth(t *testing.T) { req := createReq(t) // Set a context with a value that is not of type proxy.UpstreamAuthProvider - ctx := context.WithValue(req.Context(), ctxutil.ContextKey("upstream-auth"), "invalid-type") - ctxutil.SetContext(req, ctx) + ctx := context.WithValue(req.Context(), httputil.ContextKey("upstream-auth"), "invalid-type") + httputil.SetContext(req, ctx) - retrievedAuth := ctxutil.GetUpstreamAuth(req) + retrievedAuth := httputil.GetUpstreamAuth(req) assert.Nil(t, retrievedAuth) }) } @@ -58,7 +59,7 @@ func TestSetContext(t *testing.T) { ctx := context.WithValue(context.Background(), "key", "value") // Call SetContext to update the request's context - ctxutil.SetContext(req, ctx) + httputil.SetContext(req, ctx) // Verify that the request's context has been updated retrievedValue := req.Context().Value("key") @@ -75,7 +76,7 @@ func TestSetContext(t *testing.T) { newCtx := context.WithValue(context.Background(), "newKey", "newValue") // Call SetContext to update the request's context with the new context - ctxutil.SetContext(req, newCtx) + httputil.SetContext(req, newCtx) assert.Nil(t, req.Context().Value("existingKey")) assert.Equal(t, "newValue", req.Context().Value("newKey")) @@ -86,7 +87,7 @@ func TestSetContext(t *testing.T) { emptyCtx := context.Background() - ctxutil.SetContext(req, emptyCtx) + httputil.SetContext(req, emptyCtx) assert.Equal(t, emptyCtx, req.Context()) }) From a98bd6bb51cc7219b1d2c632eef8436a91df2881 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 15:21:48 +0200 Subject: [PATCH 14/16] fix sonarcloud reported issues --- gateway/mw_upstream_basic_auth.go | 7 +++++-- internal/httputil/context.go | 2 ++ internal/httputil/context_test.go | 10 +++++----- internal/model/upstream_auth.go | 4 +++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/gateway/mw_upstream_basic_auth.go b/gateway/mw_upstream_basic_auth.go index 696b58d4794..066d560d107 100644 --- a/gateway/mw_upstream_basic_auth.go +++ b/gateway/mw_upstream_basic_auth.go @@ -50,12 +50,15 @@ func (t *UpstreamBasicAuth) ProcessRequest(_ http.ResponseWriter, r *http.Reques return nil, http.StatusOK } -// UpstreamBasicAuthProvider implements auth provider +// UpstreamBasicAuthProvider implements upstream auth provider. type UpstreamBasicAuthProvider struct { + // HeaderName is the header name to be used to fill upstream auth with. HeaderName string - AuthValue string + // AuthValue is the value of auth header. + AuthValue string } +// Fill sets the request's HeaderName with AuthValue func (u UpstreamBasicAuthProvider) Fill(r *http.Request) { r.Header.Add(u.HeaderName, u.AuthValue) } diff --git a/internal/httputil/context.go b/internal/httputil/context.go index d019d3da769..2ac0b07ab2c 100644 --- a/internal/httputil/context.go +++ b/internal/httputil/context.go @@ -7,12 +7,14 @@ import ( "github.com/TykTechnologies/tyk/internal/model" ) +// ContextKey is the key type to be used for context interactions. type ContextKey string const ( upstreamAuth = ContextKey("upstream-auth") ) +// SetContext updates the context of a request. func SetContext(r *http.Request, ctx context.Context) { r2 := r.WithContext(ctx) *r = *r2 diff --git a/internal/httputil/context_test.go b/internal/httputil/context_test.go index a9a59083054..825644db1c8 100644 --- a/internal/httputil/context_test.go +++ b/internal/httputil/context_test.go @@ -14,7 +14,7 @@ import ( func createReq(tb testing.TB) *http.Request { tb.Helper() - req, err := http.NewRequest(http.MethodGet, "http://example.com", nil) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://example.com", nil) assert.NoError(tb, err) return req } @@ -69,17 +69,17 @@ func TestSetContext(t *testing.T) { t.Run("override key", func(t *testing.T) { req := createReq(t) - existingCtx := context.WithValue(context.Background(), "existingKey", "existingValue") + existingCtx := context.WithValue(context.Background(), httputil.ContextKey("existingKey"), "existingValue") req = req.WithContext(existingCtx) // Create a new context to override the existing context - newCtx := context.WithValue(context.Background(), "newKey", "newValue") + newCtx := context.WithValue(context.Background(), httputil.ContextKey("newKey"), "newValue") // Call SetContext to update the request's context with the new context httputil.SetContext(req, newCtx) - assert.Nil(t, req.Context().Value("existingKey")) - assert.Equal(t, "newValue", req.Context().Value("newKey")) + assert.Nil(t, req.Context().Value(httputil.ContextKey("existingKey"))) + assert.Equal(t, "newValue", req.Context().Value(httputil.ContextKey("newKey"))) }) t.Run("empty context", func(t *testing.T) { diff --git a/internal/model/upstream_auth.go b/internal/model/upstream_auth.go index bd907995a24..a064b42e212 100644 --- a/internal/model/upstream_auth.go +++ b/internal/model/upstream_auth.go @@ -11,4 +11,6 @@ type UpstreamAuthProvider interface { type MockUpstreamAuthProvider struct{} // Fill is a mock implementation to be used in tests. -func (m *MockUpstreamAuthProvider) Fill(r *http.Request) {} +func (m *MockUpstreamAuthProvider) Fill(_ *http.Request) { + // empty mock implementation. +} From 651ef9743f03ff9c3e7f692ae32afc07ed44746d Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 15:59:43 +0200 Subject: [PATCH 15/16] fix sonarcloud reported issues --- internal/httputil/context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/httputil/context_test.go b/internal/httputil/context_test.go index 825644db1c8..81892b2688d 100644 --- a/internal/httputil/context_test.go +++ b/internal/httputil/context_test.go @@ -56,7 +56,7 @@ func TestSetContext(t *testing.T) { req := createReq(t) // Create a new context with a key-value pair - ctx := context.WithValue(context.Background(), "key", "value") + ctx := context.WithValue(context.Background(), httputil.ContextKey("key"), "value") // Call SetContext to update the request's context httputil.SetContext(req, ctx) From 71dfe14d5ccdc09389ebca32b0010f8fa6811544 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Tue, 8 Oct 2024 16:23:48 +0200 Subject: [PATCH 16/16] fix tests --- internal/httputil/context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/httputil/context_test.go b/internal/httputil/context_test.go index 81892b2688d..2fffe60b167 100644 --- a/internal/httputil/context_test.go +++ b/internal/httputil/context_test.go @@ -62,7 +62,7 @@ func TestSetContext(t *testing.T) { httputil.SetContext(req, ctx) // Verify that the request's context has been updated - retrievedValue := req.Context().Value("key") + retrievedValue := req.Context().Value(httputil.ContextKey("key")) assert.Equal(t, "value", retrievedValue) })