Skip to content

Commit

Permalink
sync with tyk master (#240)
Browse files Browse the repository at this point in the history
* Add datasource hooks (#67)

* add hooks for graphql datasource

* add graphql hooks to the contract

* add hooks for http json data source

* add hook test for http json

* fix tests

* fix extracting variables with multiple queries on contract (#71)

* implement root field depths (TT-204) (#72)

* provide per root field complexity

* change internal complexity return values

* wip: per field complexity

* implement per root field complexity

* fix linter errors

* change namings according some PR remarks

* Fix/tt 571 update field depth calculation (#73)

* rename operation_complexity.FieldComplexityResult to RootFieldStats
fix depth calculation for the root fields

* remove unnecessary allocations

* reset counters after living root field (#75)

* Feat/add introspectionhelper to graphql (#74)

* add node modules to gitignore

* add IsIntrospectionQuery to graphql.Request

* add IntrospectionResponse to graphql.Schema

* add detecting introspection query without name

* add more safe conditions

Co-authored-by: Sergey Petrunin <sergey@tyk.io>

* fix detecting introspection (#76)

add data root field to introspection response

* add helper introspection as an execution result (#77)

* implement missing subscription and websocket protocol parts (TT-904) (#78)

* send complete on unsubscribe

* update tests and function returns

* add ability to execute non-subscription query

* add test for server returning error on invalid queries

* add buffer pool

* expose some websocket functionality

* fix potential issues with websocket handler

* go fmt & imports

* fix build

* fix websocket client
cleanup websocket client test

* add tests for operation selection

* fix operation name selection

* add error cases

* add compatibility for go 1.12

* use WithContext correct

* refactor OperationNameExists function

* iterate root nodes instead of operation definition as it could lead to bugs

* select number of operations in document from rootnodes

* update deps

* introduce EngineV2Configuration

* introduce EngineResultWriter

* implement engine execution with first test

* refactor tests

* port all v1 tests to v2

* add benchmark for v2

* use context.Background instead of nil

* fix typo

* change writer to flush writer

* introduce engine executor abstraction for subscriptions

* add function to get operation type from graphql request

* improve errors in graphql package

* fix typo

* revert typo

* rearrange handler test

* implement websockets for engine v2

* fix small issues

* fix broken test

* add hooks implementation for v2 (#85)

* remove comments

* implement hook context with path (#87)

* implement hook context with path

* fix matching issue

Co-authored-by: Patric Vormstein <pvormstein@googlemail.com>

* Feat/implement header injection (#88)

* implement resolving header from request

* implement header injection

* fix subscription initialization

* fix planning for nested datasources

* add test for current implementation

* add function to check for blocked and allowed fields

* add missing type in enum

* rename test lists correctly

Co-authored-by: Patric Vormstein <pvormstein@googlemail.com>
Co-authored-by: Sergey Petrunin <spetrunin@users.noreply.github.com>
Co-authored-by: Sergey Petrunin <sergey@tyk.io>
  • Loading branch information
4 people committed Feb 2, 2021
1 parent 7aa9e35 commit 92fdd8e
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 6 deletions.
72 changes: 66 additions & 6 deletions pkg/graphql/request_fields_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,86 @@ type RequestFieldsValidator interface {
Validate(request *Request, schema *Schema, restrictions []Type) (RequestFieldsValidationResult, error)
}

type FieldRestrictionValidator interface {
ValidateByFieldList(request *Request, schema *Schema, restrictionList FieldRestrictionList) (RequestFieldsValidationResult, error)
}

type FieldRestrictionListKind int

const (
AllowList FieldRestrictionListKind = iota
BlockList
)

type FieldRestrictionList struct {
Kind FieldRestrictionListKind
Types []Type
}

type fieldsValidator struct {
}

// Validate validates a request by checking if `restrictions` contains blocked fields.
//
// Deprecated: This function can only handle blocked fields. Use `ValidateByFieldList` if you
// want to check for blocked or allowed fields instead.
func (d fieldsValidator) Validate(request *Request, schema *Schema, restrictions []Type) (RequestFieldsValidationResult, error) {
restrictionList := FieldRestrictionList{
Kind: BlockList,
Types: restrictions,
}

return d.ValidateByFieldList(request, schema, restrictionList)
}

// ValidateByFieldList will validate a request by using a list of allowed or blocked fields.
func (d fieldsValidator) ValidateByFieldList(request *Request, schema *Schema, restrictionList FieldRestrictionList) (RequestFieldsValidationResult, error) {
report := operationreport.Report{}
if len(restrictions) == 0 {
if len(restrictionList.Types) == 0 {
return fieldsValidationResult(report, true, "", "")
}

requestedTypes := make(RequestTypes)
NewExtractor().ExtractFieldsFromRequest(request, schema, &report, requestedTypes)

for _, restrictedType := range restrictions {
requestedFields, hasRestrictedType := requestedTypes[restrictedType.Name]
if restrictionList.Kind == BlockList {
return d.checkForBlockedFields(restrictionList, requestedTypes, report)
}

return d.checkForAllowedFields(restrictionList, requestedTypes, report)
}

func (d fieldsValidator) checkForBlockedFields(restrictionList FieldRestrictionList, requestTypes RequestTypes, report operationreport.Report) (RequestFieldsValidationResult, error) {
for _, typeFromList := range restrictionList.Types {
requestedFields, hasRestrictedType := requestTypes[typeFromList.Name]
if !hasRestrictedType {
continue
}
for _, field := range restrictedType.Fields {
if _, hasRestrictedField := requestedFields[field]; hasRestrictedField {
return fieldsValidationResult(report, false, restrictedType.Name, field)
for _, field := range typeFromList.Fields {
_, requestHasField := requestedFields[field]
if requestHasField {
return fieldsValidationResult(report, false, typeFromList.Name, field)
}
}
}

return fieldsValidationResult(report, true, "", "")
}

func (d fieldsValidator) checkForAllowedFields(restrictionList FieldRestrictionList, requestTypes RequestTypes, report operationreport.Report) (RequestFieldsValidationResult, error) {
allowedFieldsLookupMap := make(map[string]map[string]bool)
for _, allowedType := range restrictionList.Types {
allowedFieldsLookupMap[allowedType.Name] = make(map[string]bool)
for _, allowedField := range allowedType.Fields {
allowedFieldsLookupMap[allowedType.Name][allowedField] = true
}
}

for requestType, requestFields := range requestTypes {
for requestField := range requestFields {
isAllowedField := allowedFieldsLookupMap[requestType][requestField]
if !isAllowedField {
return fieldsValidationResult(report, false, requestType, requestField)
}
}
}
Expand Down
136 changes: 136 additions & 0 deletions pkg/graphql/request_fields_validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package graphql

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/jensneuse/graphql-go-tools/pkg/starwars"
)

func TestFieldsValidator_Validate(t *testing.T) {
schema := starwarsSchema(t)
request := requestForQuery(t, starwars.FileSimpleHeroQuery)

t.Run("should invalidate if blocked fields are used", func(t *testing.T) {

blockedFields := []Type{
{
Name: "Character",
Fields: []string{"name"},
},
}

validator := fieldsValidator{}
result, err := validator.Validate(&request, schema, blockedFields)
assert.NoError(t, err)
assert.False(t, result.Valid)
assert.Equal(t, 1, result.Errors.Count())
})

t.Run("should validate if non-blocked fields are used", func(t *testing.T) {

blockedFields := []Type{
{
Name: "Character",
Fields: []string{"friends"},
},
}

validator := fieldsValidator{}
result, err := validator.Validate(&request, schema, blockedFields)
assert.NoError(t, err)
assert.True(t, result.Valid)
assert.Equal(t, 0, result.Errors.Count())
})
}

func TestFieldsValidator_ValidateByFieldList(t *testing.T) {
schema := starwarsSchema(t)
request := requestForQuery(t, starwars.FileSimpleHeroQuery)

t.Run("block list", func(t *testing.T) {
t.Run("should invalidate if blocked fields are used", func(t *testing.T) {
blockList := FieldRestrictionList{
Kind: BlockList,
Types: []Type{
{
Name: "Character",
Fields: []string{"name"},
},
},
}

validator := fieldsValidator{}
result, err := validator.ValidateByFieldList(&request, schema, blockList)
assert.NoError(t, err)
assert.False(t, result.Valid)
assert.Equal(t, 1, result.Errors.Count())
})

t.Run("should validate if non-blocked fields are used", func(t *testing.T) {
blockList := FieldRestrictionList{
Kind: BlockList,
Types: []Type{
{
Name: "Character",
Fields: []string{"friends"},
},
},
}

validator := fieldsValidator{}
result, err := validator.ValidateByFieldList(&request, schema, blockList)
assert.NoError(t, err)
assert.True(t, result.Valid)
assert.Equal(t, 0, result.Errors.Count())
})
})

t.Run("allow list", func(t *testing.T) {
t.Run("should invalidate if a field which is not allowed is used", func(t *testing.T) {
allowList := FieldRestrictionList{
Kind: AllowList,
Types: []Type{
{
Name: "Query",
Fields: []string{"hero"},
},
{
Name: "Character",
Fields: []string{"friends"},
},
},
}

validator := fieldsValidator{}
result, err := validator.ValidateByFieldList(&request, schema, allowList)
assert.NoError(t, err)
assert.False(t, result.Valid)
assert.Equal(t, 1, result.Errors.Count())
})

t.Run("should validate if all fields are allowed", func(t *testing.T) {
allowList := FieldRestrictionList{
Kind: AllowList,
Types: []Type{
{
Name: "Query",
Fields: []string{"hero"},
},
{
Name: "Character",
Fields: []string{"name"},
},
},
}

validator := fieldsValidator{}
result, err := validator.ValidateByFieldList(&request, schema, allowList)
assert.NoError(t, err)
assert.True(t, result.Valid)
assert.Equal(t, 0, result.Errors.Count())
})
})

}

0 comments on commit 92fdd8e

Please sign in to comment.