Skip to content

Commit 750c7f8

Browse files
authored
Merge branch 'master' into TT-9234-graceful-shutdown-of-gateway
2 parents 767dd13 + 8c27f68 commit 750c7f8

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

apidef/oas/oas.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package oas
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"strings"
89

@@ -439,6 +440,40 @@ func (s *OAS) ReplaceServers(apiURLs, oldAPIURLs []string) {
439440
s.Servers = append(newServers, userAddedServers...)
440441
}
441442

443+
// Validate validates OAS document by calling openapi3.T.Validate() function. In addition, it validates Security
444+
// Requirement section and it's requirements by calling OAS.validateSecurity() function.
445+
func (s *OAS) Validate(ctx context.Context, opts ...openapi3.ValidationOption) error {
446+
validationErr := s.T.Validate(ctx, opts...)
447+
securityErr := s.validateSecurity()
448+
449+
return errors.Join(validationErr, securityErr)
450+
}
451+
452+
// validateSecurity verifies that existing Security Requirement Objects has Security Schemes declared in the Security
453+
// Schemes under the Components Object. This function closes gap in validation provided by OAS.Validate func.
454+
func (s *OAS) validateSecurity() error {
455+
if len(s.Security) == 0 {
456+
return nil
457+
}
458+
459+
if s.Components == nil || s.Components.SecuritySchemes == nil || len(s.Components.SecuritySchemes) == 0 {
460+
return errors.New("No components or security schemes present in OAS")
461+
}
462+
463+
for _, requirement := range s.Security {
464+
for key := range requirement {
465+
if _, ok := s.Components.SecuritySchemes[key]; !ok {
466+
errorMsg := fmt.Sprintf("Missing required Security Scheme '%s' in Components.SecuritySchemes. "+
467+
"For more information please visit https://swagger.io/specification/#security-requirement-object",
468+
key)
469+
return errors.New(errorMsg)
470+
}
471+
}
472+
}
473+
474+
return nil
475+
}
476+
442477
// APIDef holds both OAS and Classic forms of an API definition.
443478
type APIDef struct {
444479
// OAS contains the OAS API definition.

apidef/oas/oas_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package oas
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
78
"testing"
89

@@ -1294,6 +1295,169 @@ func TestAPIContext_getValidationOptionsFromConfig(t *testing.T) {
12941295
})
12951296
}
12961297

1298+
func TestOAS_ValidateSecurity(t *testing.T) {
1299+
apiKey := "api_key"
1300+
oauth2 := "oauth2"
1301+
1302+
createSecurityReq := func(oas *OAS, key string, value []string) {
1303+
securityRequirement := openapi3.NewSecurityRequirement()
1304+
securityRequirement[key] = value
1305+
oas.Security = append(oas.Security, securityRequirement)
1306+
}
1307+
1308+
addSingleSecurityReq := func(oas *OAS) {
1309+
createSecurityReq(oas, apiKey, []string{})
1310+
}
1311+
1312+
addMultipleSecurityReq := func(oas *OAS) {
1313+
addSingleSecurityReq(oas)
1314+
createSecurityReq(oas, oauth2, []string{"read", "write"})
1315+
}
1316+
1317+
expectedErrorForNoComponentOrSchemes := "No components or security schemes present in OAS"
1318+
expectedErrorForMissingSchema := func(s string) string {
1319+
return fmt.Sprintf("Missing required Security Scheme '%s' in Components.SecuritySchemes", s)
1320+
}
1321+
1322+
tests := []struct {
1323+
name string
1324+
setupOAS func(oas *OAS)
1325+
expectedError string
1326+
}{
1327+
{
1328+
name: "no security requirements",
1329+
setupOAS: func(oas *OAS) {
1330+
oas.Security = openapi3.SecurityRequirements{}
1331+
},
1332+
expectedError: "",
1333+
},
1334+
{
1335+
name: "security requirements with matching security schemes",
1336+
setupOAS: func(oas *OAS) {
1337+
addSingleSecurityReq(oas)
1338+
1339+
if oas.Components == nil {
1340+
oas.Components = &openapi3.Components{}
1341+
}
1342+
if oas.Components.SecuritySchemes == nil {
1343+
oas.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
1344+
}
1345+
oas.Components.SecuritySchemes[apiKey] = &openapi3.SecuritySchemeRef{
1346+
Value: openapi3.NewJWTSecurityScheme(),
1347+
}
1348+
},
1349+
expectedError: "",
1350+
},
1351+
{
1352+
name: "security requirements but no Components",
1353+
setupOAS: func(oas *OAS) {
1354+
addSingleSecurityReq(oas)
1355+
1356+
oas.Components = nil
1357+
},
1358+
expectedError: expectedErrorForNoComponentOrSchemes,
1359+
},
1360+
{
1361+
name: "security requirements but no SecuritySchemes",
1362+
setupOAS: func(oas *OAS) {
1363+
addSingleSecurityReq(oas)
1364+
1365+
// Add Components but not SecuritySchemes
1366+
oas.Components = &openapi3.Components{}
1367+
oas.Components.SecuritySchemes = nil
1368+
},
1369+
expectedError: expectedErrorForNoComponentOrSchemes,
1370+
},
1371+
{
1372+
name: "security requirements but empty SecuritySchemes",
1373+
setupOAS: func(oas *OAS) {
1374+
addSingleSecurityReq(oas)
1375+
1376+
oas.Components = &openapi3.Components{}
1377+
oas.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
1378+
},
1379+
expectedError: expectedErrorForNoComponentOrSchemes,
1380+
},
1381+
{
1382+
name: "security requirements with missing security scheme for this requirement",
1383+
setupOAS: func(oas *OAS) {
1384+
addSingleSecurityReq(oas)
1385+
1386+
if oas.Components == nil {
1387+
oas.Components = &openapi3.Components{}
1388+
}
1389+
if oas.Components.SecuritySchemes == nil {
1390+
oas.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
1391+
}
1392+
oas.Components.SecuritySchemes["other_key"] = &openapi3.SecuritySchemeRef{
1393+
Value: openapi3.NewJWTSecurityScheme(),
1394+
}
1395+
},
1396+
expectedError: expectedErrorForMissingSchema(apiKey),
1397+
},
1398+
{
1399+
name: "multiple security requirements with all matching security schemes",
1400+
setupOAS: func(oas *OAS) {
1401+
addMultipleSecurityReq(oas)
1402+
1403+
if oas.Components == nil {
1404+
oas.Components = &openapi3.Components{}
1405+
}
1406+
if oas.Components.SecuritySchemes == nil {
1407+
oas.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
1408+
}
1409+
oas.Components.SecuritySchemes[apiKey] = &openapi3.SecuritySchemeRef{
1410+
Value: openapi3.NewJWTSecurityScheme(),
1411+
}
1412+
oas.Components.SecuritySchemes[oauth2] = &openapi3.SecuritySchemeRef{
1413+
Value: openapi3.NewJWTSecurityScheme(),
1414+
}
1415+
},
1416+
expectedError: "",
1417+
},
1418+
{
1419+
name: "multiple security requirements with one missing security scheme",
1420+
setupOAS: func(oas *OAS) {
1421+
addMultipleSecurityReq(oas)
1422+
1423+
if oas.Components == nil {
1424+
oas.Components = &openapi3.Components{}
1425+
}
1426+
if oas.Components.SecuritySchemes == nil {
1427+
oas.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
1428+
}
1429+
oas.Components.SecuritySchemes[apiKey] = &openapi3.SecuritySchemeRef{}
1430+
},
1431+
expectedError: expectedErrorForMissingSchema(oauth2),
1432+
},
1433+
}
1434+
1435+
for _, tt := range tests {
1436+
t.Run(tt.name, func(t *testing.T) {
1437+
oas := &OAS{
1438+
T: openapi3.T{
1439+
OpenAPI: "3.0.3",
1440+
Info: &openapi3.Info{
1441+
Title: "Test API",
1442+
Version: "1.0.0",
1443+
},
1444+
Paths: openapi3.Paths{},
1445+
},
1446+
}
1447+
1448+
tt.setupOAS(oas)
1449+
1450+
err := oas.Validate(context.Background())
1451+
if tt.expectedError == "" {
1452+
assert.NoError(t, err)
1453+
} else {
1454+
assert.Error(t, err)
1455+
assert.Contains(t, err.Error(), tt.expectedError)
1456+
}
1457+
})
1458+
}
1459+
}
1460+
12971461
func TestYaml(t *testing.T) {
12981462
oasDoc := OAS{}
12991463
Fill(t, &oasDoc, 0)

apidef/streams/bento/schema/bento-config-schema.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,15 @@
11611161
"custom_delimiter": {
11621162
"type": "string"
11631163
},
1164+
"expected_headers": {
1165+
"items": {
1166+
"type": "string"
1167+
},
1168+
"type": "array"
1169+
},
1170+
"expected_number_of_fields": {
1171+
"type": "number"
1172+
},
11641173
"lazy_quotes": {
11651174
"type": "boolean"
11661175
},
@@ -1299,6 +1308,23 @@
12991308
}
13001309
},
13011310
"type": "object"
1311+
},
1312+
{
1313+
"properties": {
1314+
"xml_documents": {
1315+
"additionalProperties": false,
1316+
"properties": {
1317+
"cast": {
1318+
"type": "boolean"
1319+
},
1320+
"operator": {
1321+
"type": "string"
1322+
}
1323+
},
1324+
"type": "object"
1325+
}
1326+
},
1327+
"type": "object"
13021328
}
13031329
]
13041330
},
@@ -2852,6 +2878,9 @@
28522878
},
28532879
"type": "object"
28542880
},
2881+
"heartbeat": {
2882+
"type": "string"
2883+
},
28552884
"key_file": {
28562885
"type": "string"
28572886
},
@@ -2864,6 +2893,9 @@
28642893
"pong_wait": {
28652894
"type": "string"
28662895
},
2896+
"stream_format": {
2897+
"type": "string"
2898+
},
28672899
"stream_path": {
28682900
"type": "string"
28692901
},
@@ -2873,6 +2905,9 @@
28732905
"write_wait": {
28742906
"type": "string"
28752907
},
2908+
"ws_message_type": {
2909+
"type": "string"
2910+
},
28762911
"ws_path": {
28772912
"type": "string"
28782913
}

0 commit comments

Comments
 (0)