Skip to content

Commit 3c78a1d

Browse files
vardhanapoorvdna2github
authored andcommitted
feat(GraphQL): Allow more control over custom logic header names (dgraph-io#5600)
* Added support for custom names in headers * Continue to support old header definition * Addressed comments * Added integration test * Moved validations to rules.go
1 parent 8551200 commit 3c78a1d

5 files changed

Lines changed: 110 additions & 8 deletions

File tree

graphql/e2e/custom_logic/cmd/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,26 @@ func verifyHeadersHandler(w http.ResponseWriter, r *http.Request) {
295295
check2(w.Write([]byte(`[{"id":"0x3","name":"Star Wars"}]`)))
296296
}
297297

298+
func verifyCustomNameHeadersHandler(w http.ResponseWriter, r *http.Request) {
299+
err := verifyRequest(r, expectedRequest{
300+
method: http.MethodGet,
301+
urlSuffix: "/verifyCustomNameHeaders",
302+
body: "",
303+
headers: map[string][]string{
304+
"X-App-Token": {"app-token"},
305+
"X-User-Id": {"123"},
306+
"Authorization": {"random-fake-token"},
307+
"Accept-Encoding": nil,
308+
"User-Agent": nil,
309+
},
310+
})
311+
if err != nil {
312+
check2(w.Write([]byte(err.Error())))
313+
return
314+
}
315+
check2(w.Write([]byte(`[{"id":"0x3","name":"Star Wars"}]`)))
316+
}
317+
298318
func twitterFollwerHandler(w http.ResponseWriter, r *http.Request) {
299319
err := verifyRequest(r, expectedRequest{
300320
method: http.MethodGet,
@@ -1114,6 +1134,7 @@ func main() {
11141134
http.HandleFunc("/favMovies/", getFavMoviesHandler)
11151135
http.HandleFunc("/favMoviesPost/", postFavMoviesHandler)
11161136
http.HandleFunc("/verifyHeaders", verifyHeadersHandler)
1137+
http.HandleFunc("/verifyCustomNameHeaders", verifyCustomNameHeadersHandler)
11171138
http.HandleFunc("/twitterfollowers", twitterFollwerHandler)
11181139

11191140
// for mutations

graphql/e2e/custom_logic/custom_logic_test.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,45 @@ func TestCustomQueryShouldForwardHeaders(t *testing.T) {
168168
secretHeaders: ["Github-Api-Token", "X-App-Token"]
169169
})
170170
}
171+
172+
# Dgraph.Secret Github-Api-Token "random-fake-token"
173+
# Dgraph.Secret app "should-be-overriden"
174+
`
175+
updateSchemaRequireNoGQLErrors(t, schema)
176+
time.Sleep(2 * time.Second)
177+
178+
query := `
179+
query {
180+
verifyHeaders(id: "0x123") {
181+
id
182+
name
183+
}
184+
}`
185+
params := &common.GraphQLParams{
186+
Query: query,
187+
Headers: map[string][]string{
188+
"X-App-Token": []string{"app-token"},
189+
"X-User-Id": []string{"123"},
190+
"Random-header": []string{"random"},
191+
},
192+
}
193+
194+
result := params.ExecuteAsPost(t, alphaURL)
195+
require.Nil(t, result.Errors)
196+
expected := `{"verifyHeaders":[{"id":"0x3","name":"Star Wars"}]}`
197+
require.Equal(t, expected, string(result.Data))
198+
}
199+
200+
func TestCustomNameForwardHeaders(t *testing.T) {
201+
schema := customTypes + `
202+
type Query {
203+
verifyHeaders(id: ID!): [Movie] @custom(http: {
204+
url: "http://mock:8888/verifyCustomNameHeaders",
205+
method: "GET",
206+
forwardHeaders: ["X-App-Token:App", "X-User-Id"],
207+
secretHeaders: ["Authorization:Github-Api-Token", "X-App-Token"]
208+
})
209+
}
171210
172211
# Dgraph.Secret Github-Api-Token "random-fake-token"
173212
# Dgraph.Secret X-App-Token "should-be-overriden"
@@ -185,7 +224,7 @@ func TestCustomQueryShouldForwardHeaders(t *testing.T) {
185224
params := &common.GraphQLParams{
186225
Query: query,
187226
Headers: map[string][]string{
188-
"X-App-Token": []string{"app-token"},
227+
"App": []string{"app-token"},
189228
"X-User-Id": []string{"123"},
190229
"Random-header": []string{"random"},
191230
},

graphql/schema/rules.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,32 @@ func customDirectiveValidation(sch *ast.Schema,
16041604
}
16051605
}
16061606

1607+
forwardHeaders := httpArg.Value.Children.ForName("forwardHeaders")
1608+
if forwardHeaders != nil {
1609+
for _, h := range forwardHeaders.Children {
1610+
key := strings.Split(h.Value.Raw, ":")
1611+
if len(key) > 2 {
1612+
return append(errs, gqlerror.ErrorPosf(graphql.Position,
1613+
"Type %s; Field %s; forwardHeaders in @custom directive should be of the form 'remote_headername:local_headername' or just 'headername'"+
1614+
", found: `%s`.",
1615+
typ.Name, field.Name, h.Value.Raw))
1616+
}
1617+
}
1618+
}
1619+
1620+
secretHeaders := httpArg.Value.Children.ForName("secretHeaders")
1621+
if secretHeaders != nil {
1622+
for _, h := range secretHeaders.Children {
1623+
key := strings.Split(h.Value.Raw, ":")
1624+
if len(key) > 2 {
1625+
return append(errs, gqlerror.ErrorPosf(graphql.Position,
1626+
"Type %s; Field %s; secretHeaders in @custom directive should be of the form 'remote_headername:local_headername' or just 'headername'"+
1627+
", found: `%s`.",
1628+
typ.Name, field.Name, h.Value.Raw))
1629+
}
1630+
}
1631+
}
1632+
16071633
if errs != nil {
16081634
return errs
16091635
}
@@ -1613,9 +1639,13 @@ func customDirectiveValidation(sch *ast.Schema,
16131639
headers := http.Header{}
16141640
if secretHeaders != nil {
16151641
for _, h := range secretHeaders.Children {
1642+
key := strings.Split(h.Value.Raw, ":")
1643+
if len(key) == 1 {
1644+
key = []string{h.Value.Raw, h.Value.Raw}
1645+
}
16161646
// We try and fetch the value from the stored secrets.
1617-
val := secrets[h.Value.Raw]
1618-
headers.Add(h.Value.Raw, string(val))
1647+
val := secrets[key[1]]
1648+
headers.Add(key[0], string(val))
16191649
}
16201650
}
16211651
if err := validateRemoteGraphql(&remoteGraphqlMetadata{

graphql/schema/schemagen.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,11 @@ func getAllowedHeaders(sch *ast.Schema, definitions []string) string {
259259
return
260260
}
261261
for _, h := range forwardHeaders.Children {
262-
headers[h.Value.Raw] = struct{}{}
262+
key := strings.Split(h.Value.Raw, ":")
263+
if len(key) == 1 {
264+
key = []string{h.Value.Raw, h.Value.Raw}
265+
}
266+
headers[key[1]] = struct{}{}
263267
}
264268
}
265269

graphql/schema/wrappers.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,12 @@ func getCustomHTTPConfig(f *field, isQueryOrMutation bool) (FieldHTTPConfig, err
847847
if secretHeaders != nil {
848848
hc.RLock()
849849
for _, h := range secretHeaders.Children {
850-
val := string(hc.secrets[h.Value.Raw])
851-
fconf.ForwardHeaders.Set(h.Value.Raw, val)
850+
key := strings.Split(h.Value.Raw, ":")
851+
if len(key) == 1 {
852+
key = []string{h.Value.Raw, h.Value.Raw}
853+
}
854+
val := string(hc.secrets[key[1]])
855+
fconf.ForwardHeaders.Set(key[0], val)
852856
}
853857
hc.RUnlock()
854858
}
@@ -857,8 +861,12 @@ func getCustomHTTPConfig(f *field, isQueryOrMutation bool) (FieldHTTPConfig, err
857861
if forwardHeaders != nil {
858862
for _, h := range forwardHeaders.Children {
859863
// We would override the header if it was also specified as part of secretHeaders.
860-
reqHeaderVal := f.op.header.Get(h.Value.Raw)
861-
fconf.ForwardHeaders.Set(h.Value.Raw, reqHeaderVal)
864+
key := strings.Split(h.Value.Raw, ":")
865+
if len(key) == 1 {
866+
key = []string{h.Value.Raw, h.Value.Raw}
867+
}
868+
reqHeaderVal := f.op.header.Get(key[1])
869+
fconf.ForwardHeaders.Set(key[0], reqHeaderVal)
862870
}
863871
}
864872

0 commit comments

Comments
 (0)