Skip to content

Commit c4e3ad1

Browse files
AidanWelchdaveshanley
authored andcommitted
Add support for multiple possible security schemes without requiring they're all true
1 parent 0a73ef5 commit c4e3ad1

File tree

2 files changed

+140
-12
lines changed

2 files changed

+140
-12
lines changed

parameters/validate_security.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010

1111
"github.com/pb33f/libopenapi-validator/errors"
1212
"github.com/pb33f/libopenapi-validator/helpers"
13+
"github.com/pb33f/libopenapi-validator/paths"
1314
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
1415
"github.com/pb33f/libopenapi/orderedmap"
15-
"github.com/pb33f/libopenapi-validator/paths"
1616
)
1717

1818
func (v *paramValidator) ValidateSecurity(request *http.Request) (bool, []*errors.ValidationError) {
@@ -44,7 +44,13 @@ func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pat
4444
return true, nil
4545
}
4646

47+
allErrors := []*errors.ValidationError{}
48+
4749
for _, sec := range security {
50+
if sec.ContainsEmptyRequirement {
51+
return true, nil
52+
}
53+
4854
for pair := orderedmap.First(sec.Requirements); pair != nil; pair = pair.Next() {
4955
secName := pair.Key()
5056

@@ -85,8 +91,9 @@ func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pat
8591
}
8692

8793
errors.PopulateValidationErrors(validationErrors, request, pathValue)
88-
89-
return false, validationErrors
94+
allErrors = append(allErrors, validationErrors...)
95+
} else {
96+
return true, nil
9097
}
9198
}
9299

@@ -107,8 +114,9 @@ func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pat
107114
}
108115

109116
errors.PopulateValidationErrors(validationErrors, request, pathValue)
110-
111-
return false, validationErrors
117+
allErrors = append(allErrors, validationErrors...)
118+
} else {
119+
return true, nil
112120
}
113121
}
114122
if secScheme.In == "query" {
@@ -133,8 +141,9 @@ func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pat
133141
}
134142

135143
errors.PopulateValidationErrors(validationErrors, request, pathValue)
136-
137-
return false, validationErrors
144+
allErrors = append(allErrors, validationErrors...)
145+
} else {
146+
return true, nil
138147
}
139148
}
140149
if secScheme.In == "cookie" {
@@ -160,12 +169,14 @@ func (v *paramValidator) ValidateSecurityWithPathItem(request *http.Request, pat
160169
}
161170

162171
errors.PopulateValidationErrors(validationErrors, request, pathValue)
163-
164-
return false, validationErrors
172+
allErrors = append(allErrors, validationErrors...)
173+
} else {
174+
return true, nil
165175
}
166176
}
167177
}
168178
}
169179
}
170-
return true, nil
180+
181+
return false, allErrors
171182
}

parameters/validate_security_test.go

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
package parameters
55

66
import (
7+
"net/http"
8+
"testing"
9+
710
"github.com/pb33f/libopenapi"
811
"github.com/pb33f/libopenapi-validator/paths"
912
"github.com/stretchr/testify/assert"
10-
"net/http"
11-
"testing"
1213
)
1314

1415
func TestParamValidator_ValidateSecurity_APIKeyHeader_NotFound(t *testing.T) {
@@ -398,3 +399,119 @@ paths:
398399
assert.Len(t, errors, 1)
399400
assert.Equal(t, "POST Path '/beef' not found", errors[0].Message)
400401
}
402+
403+
func TestParamValidator_ValidateSecurity_MultipleSecurity(t *testing.T) {
404+
405+
spec := `openapi: 3.1.0
406+
paths:
407+
/products:
408+
post:
409+
security:
410+
- ApiKeyAuthQuery:
411+
- write:products
412+
- ApiKeyAuthHeader:
413+
- write:products
414+
components:
415+
securitySchemes:
416+
ApiKeyAuthQuery:
417+
type: apiKey
418+
in: query
419+
name: X-API-Key
420+
ApiKeyAuthHeader:
421+
type: apiKey
422+
in: header
423+
name: X-API-Key
424+
`
425+
426+
doc, _ := libopenapi.NewDocument([]byte(spec))
427+
428+
m, _ := doc.BuildV3Model()
429+
430+
v := NewParameterValidator(&m.Model)
431+
432+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil)
433+
request.Header.Add("X-API-Key", "1234")
434+
435+
valid, errors := v.ValidateSecurity(request)
436+
assert.True(t, valid)
437+
assert.Equal(t, 0, len(errors))
438+
}
439+
440+
func TestParamValidator_ValidateSecurity_MultipleSecurity_EmptyOption(t *testing.T) {
441+
442+
spec := `openapi: 3.1.0
443+
paths:
444+
/products:
445+
post:
446+
security:
447+
- ApiKeyAuth:
448+
- write:products
449+
- {}
450+
components:
451+
securitySchemes:
452+
ApiKeyAuth:
453+
type: apiKey
454+
in: header
455+
name: X-API-Key
456+
`
457+
458+
doc, _ := libopenapi.NewDocument([]byte(spec))
459+
460+
m, _ := doc.BuildV3Model()
461+
462+
v := NewParameterValidator(&m.Model)
463+
464+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil)
465+
466+
valid, errors := v.ValidateSecurity(request)
467+
assert.True(t, valid)
468+
assert.Equal(t, 0, len(errors))
469+
}
470+
471+
func TestParamValidator_ValidateSecurity_MultipleSecurity_NotFound(t *testing.T) {
472+
473+
spec := `openapi: 3.1.0
474+
paths:
475+
/products:
476+
post:
477+
security:
478+
- ApiKeyAuthQuery:
479+
- write:products
480+
- ApiKeyAuthHeader:
481+
- write:products
482+
components:
483+
securitySchemes:
484+
ApiKeyAuthQuery:
485+
type: apiKey
486+
in: query
487+
name: X-API-Key
488+
ApiKeyAuthHeader:
489+
type: apiKey
490+
in: header
491+
name: X-API-Key
492+
`
493+
494+
doc, _ := libopenapi.NewDocument([]byte(spec))
495+
496+
m, _ := doc.BuildV3Model()
497+
498+
v := NewParameterValidator(&m.Model)
499+
500+
request, _ := http.NewRequest(http.MethodPost, "https://things.com/products", nil)
501+
502+
valid, errors := v.ValidateSecurity(request)
503+
assert.False(t, valid)
504+
assert.Equal(t, 2, len(errors))
505+
506+
assert.Equal(t, "API Key X-API-Key not found in query", errors[0].Message)
507+
assert.Equal(t, "Add an API Key via 'X-API-Key' to the query string of the URL, "+
508+
"for example 'https://things.com/products?X-API-Key=your-api-key'", errors[0].HowToFix)
509+
assert.Equal(t, request.Method, errors[0].RequestMethod)
510+
assert.Equal(t, request.URL.Path, errors[0].RequestPath)
511+
assert.Equal(t, "/products", errors[0].SpecPath)
512+
513+
assert.Equal(t, "API Key X-API-Key not found in header", errors[1].Message)
514+
assert.Equal(t, request.Method, errors[1].RequestMethod)
515+
assert.Equal(t, request.URL.Path, errors[1].RequestPath)
516+
assert.Equal(t, "/products", errors[1].SpecPath)
517+
}

0 commit comments

Comments
 (0)