Skip to content

Commit

Permalink
[TT-7338] disable auth_headers and use request headers (TykTechnologi…
Browse files Browse the repository at this point in the history
…es#4562)

<!-- Provide a general summary of your changes in the Title above -->

## Description
The `auth_headers` are used by the gateway for upstream requests and are
used by the dashboard for introspection request, This
[ticket](https://tyktech.atlassian.net/browse/TT-7338) aims to only
allow a new field called `request_headers` be used for upstream requests
to allow the use of context variables. So this PR does that by disabling
the use of `auth_headers` in the gateway for upstream requests.

<!-- Describe your changes in detail -->

## Related Issue
[TT-7338](https://tyktech.atlassian.net/browse/TT-7338)

<!-- This project only accepts pull requests related to open issues. -->
<!-- If suggesting a new feature or change, please discuss it in an
issue first. -->
<!-- If fixing a bug, there should be an issue describing it with steps
to reproduce. -->
<!-- OSS: Please link to the issue here. Tyk: please create/link the
JIRA ticket. -->

## Motivation and Context

<!-- Why is this change required? What problem does it solve? -->

## How This Has Been Tested

<!-- Please describe in detail how you tested your changes -->
<!-- Include details of your testing environment, and the tests -->
<!-- you ran to see how your change affects other areas of the code,
etc. -->
<!-- This information is helpful for reviewers and QA. -->

## Screenshots (if appropriate)

## Types of changes

<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Refactoring or add test (improvements in base code or adds test
coverage to functionality)

## Checklist

<!-- Go over all the following points, and put an `x` in all the boxes
that apply -->
<!-- If there are no documentation updates required, mark the item as
checked. -->
<!-- Raise up any additional concerns not covered by the checklist. -->

- [ ] I ensured that the documentation is up to date
- [ ] I explained why this PR updates go.mod in detail with reasoning
why it's required
- [ ] I would like a code coverage CI quality gate exception and have
explained why


[TT-7338]:
https://tyktech.atlassian.net/browse/TT-7338?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TT-7338]:
https://tyktech.atlassian.net/browse/TT-7338?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TT-7338]:
https://tyktech.atlassian.net/browse/TT-7338?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
kofoworola authored Jan 10, 2023
1 parent f50674d commit 46cdcf7
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 18 deletions.
3 changes: 0 additions & 3 deletions apidef/adapter/graphql_config_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ func (g *GraphQLConfigAdapter) EngineConfigV2() (*graphql.EngineV2Configuration,

func (g *GraphQLConfigAdapter) createV2ConfigForProxyOnlyExecutionMode() (*graphql.EngineV2Configuration, error) {
staticHeaders := make(http.Header)
for authHeaderKey, authHeaderValue := range g.apiDefinition.GraphQL.Proxy.AuthHeaders {
staticHeaders.Add(authHeaderKey, authHeaderValue)
}

url := g.apiDefinition.Proxy.TargetURL
if strings.HasPrefix(url, "tyk://") {
Expand Down
13 changes: 4 additions & 9 deletions apidef/adapter/graphql_config_adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ func TestGraphQLConfigAdapter_EngineConfigV2(t *testing.T) {
},
Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{
Fetch: graphqlDataSource.FetchConfiguration{
URL: "http://localhost:8080",
Header: http.Header{
"Authorization": []string{"123abc"},
},
URL: "http://localhost:8080",
Header: map[string][]string{},
},
Subscription: graphqlDataSource.SubscriptionConfiguration{
URL: "http://localhost:8080",
Expand Down Expand Up @@ -125,7 +123,6 @@ func TestGraphQLConfigAdapter_EngineConfigV2(t *testing.T) {
Fetch: graphqlDataSource.FetchConfiguration{
URL: "http://api-name",
Header: http.Header{
"Authorization": []string{"123abc"},
"X-Tyk-Internal": []string{"true"},
},
},
Expand Down Expand Up @@ -295,10 +292,8 @@ func TestGraphQLConfigAdapter_EngineConfigV2(t *testing.T) {
},
Custom: graphqlDataSource.ConfigJson(graphqlDataSource.Configuration{
Fetch: graphqlDataSource.FetchConfiguration{
URL: "http://localhost:8080",
Header: http.Header{
"Authorization": []string{"123abc"},
},
URL: "http://localhost:8080",
Header: http.Header{},
},
Subscription: graphqlDataSource.SubscriptionConfiguration{
URL: "http://localhost:8080",
Expand Down
1 change: 1 addition & 0 deletions apidef/api_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ const (
type GraphQLProxyConfig struct {
AuthHeaders map[string]string `bson:"auth_headers" json:"auth_headers"`
SubscriptionType SubscriptionType `bson:"subscription_type" json:"subscription_type,omitempty"`
RequestHeaders map[string]string `bson:"request_headers" json:"request_headers"`
}

type GraphQLSubgraphConfig struct {
Expand Down
11 changes: 10 additions & 1 deletion gateway/mw_graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
spec.GraphQL.ExecutionMode = apidef.GraphQLExecutionModeProxyOnly
spec.GraphQL.Version = apidef.GraphQLConfigVersion2
spec.GraphQL.Schema = gqlProxyUpstreamSchema
spec.GraphQL.Proxy.AuthHeaders = map[string]string{
spec.GraphQL.Proxy.RequestHeaders = map[string]string{
"Authorization": "123abc",
}
spec.Proxy.ListenPath = "/"
Expand Down Expand Up @@ -965,6 +965,15 @@ input StringQueryOperatorInput {
scalar Upload`

const gqlContinentQuery = `
query {
continent(code: "NG"){
code
name
}
}
`

const gqlSubgraphSchemaAccounts = `scalar _Any
scalar _FieldSet
union _Entity = User
Expand Down
12 changes: 9 additions & 3 deletions gateway/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -1067,17 +1067,23 @@ func (p *ReverseProxy) handoverRequestToGraphQLExecutionEngine(roundTripper *Tyk
}

// if this context vars are enabled and this is a supergraph, inject the sub request id header
upstreamHeaders := http.Header{}
if p.TykAPISpec.EnableContextVars && p.TykAPISpec.GraphQL.ExecutionMode == apidef.GraphQLExecutionModeSupergraph {
ctxData := ctxGetData(outreq)
if reqID, exists := ctxData["request_id"]; !exists {
log.Warn("context variables enabled but request_id missing")
} else if requestID, ok := reqID.(string); ok {
execOptions = append(execOptions, graphql.WithUpstreamHeaders(http.Header{
"X-Tyk-Parent-Request-Id": {requestID},
}))
upstreamHeaders.Set("X-Tyk-Parent-Request-Id", requestID)
}
}

// append the request headers if any
for h, value := range p.TykAPISpec.GraphQL.Proxy.RequestHeaders {
val := p.Gw.replaceTykVariables(outreq, value, false)
upstreamHeaders.Set(h, val)
}
execOptions = append(execOptions, graphql.WithUpstreamHeaders(upstreamHeaders))

err = p.TykAPISpec.GraphQLExecutor.EngineV2.Execute(reqCtx, gqlRequest, &resultWriter, execOptions...)
if err != nil {
return
Expand Down
75 changes: 75 additions & 0 deletions gateway/reverse_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,81 @@ func TestGraphQL_HeadersInjection(t *testing.T) {
}...)
}

func TestGraphql_Headers(t *testing.T) {
g := StartTest(nil)
defer g.Close()

defaultSpec := BuildAPI(func(spec *APISpec) {
spec.Name = "tyk-api"
spec.APIID = "tyk-api"
spec.GraphQL.Enabled = true
spec.GraphQL.ExecutionMode = apidef.GraphQLExecutionModeProxyOnly
spec.GraphQL.Schema = gqlCountriesSchema
spec.GraphQL.Version = apidef.GraphQLConfigVersion2
spec.Proxy.TargetURL = TestHttpAny + "/dynamic"
spec.Proxy.ListenPath = "/"
})[0]

headerCheck := func(key, value string, headers map[string][]string) bool {
val, ok := headers[key]
return ok && len(val) > 0 && val[0] == value
}

t.Run("test introspection header", func(t *testing.T) {
spec := defaultSpec
spec.GraphQL.Proxy.AuthHeaders = map[string]string{
"Test-Header": "test-value",
}
spec.GraphQL.Proxy.RequestHeaders = map[string]string{
"Test-Request": "test-value",
}
g.Gw.LoadAPI(spec)
g.AddDynamicHandler("/dynamic", func(writer http.ResponseWriter, r *http.Request) {
if !headerCheck("Test-Request", "test-value", r.Header) {
t.Error("request header missing")
}
if headerCheck("Test-Header", "test-value", r.Header) {
t.Error("auth header missing")
}
})
_, err := g.Run(t,
test.TestCase{
Path: "/",
Method: http.MethodPost,
Data: graphql.Request{
Query: gqlContinentQuery,
},
},
)
assert.NoError(t, err)
})

t.Run("test context variable request headers", func(t *testing.T) {
spec := defaultSpec
spec.GraphQL.Proxy.RequestHeaders = map[string]string{
"Test-Request-Header": "$tyk_context.headers_Test_Header",
}
spec.EnableContextVars = true
g.Gw.LoadAPI(spec)
g.AddDynamicHandler("/dynamic", func(writer http.ResponseWriter, r *http.Request) {
if !headerCheck("Test-Request-Header", "test-value", r.Header) {
t.Error("context variable header missing/incorrect")
}
})
_, err := g.Run(t, test.TestCase{
Path: "/",
Headers: map[string]string{
"Test-Header": "test-value",
},
Method: http.MethodPost,
Data: graphql.Request{
Query: gqlContinentQuery,
},
})
assert.NoError(t, err)
})
}

func TestGraphQL_InternalDataSource(t *testing.T) {
g := StartTest(nil)
defer g.Close()
Expand Down
4 changes: 2 additions & 2 deletions gateway/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,9 @@ func (s *Test) start(genConf func(globalConf *config.Config)) *Gateway {
return gw
}

func (t *Test) AddDynamicHandler(path string, handlerFunc http.HandlerFunc) {
func (s *Test) AddDynamicHandler(path string, handlerFunc http.HandlerFunc) {
path = strings.Trim(path, "/")
t.dynamicHandlers[path] = handlerFunc
s.dynamicHandlers[path] = handlerFunc
}

func (s *Test) newGateway(genConf func(globalConf *config.Config)) *Gateway {
Expand Down

0 comments on commit 46cdcf7

Please sign in to comment.