Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi3filter: validate non-string headers #712

Merged
merged 6 commits into from Dec 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion openapi3/parameter.go
Expand Up @@ -90,7 +90,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt
}

// Parameter is specified by OpenAPI/Swagger 3.0 standard.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameterObject
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object
fenollp marked this conversation as resolved.
Show resolved Hide resolved
type Parameter struct {
ExtensionProps `json:"-" yaml:"-"`

Expand Down
2 changes: 1 addition & 1 deletion openapi3filter/issue201_test.go
Expand Up @@ -94,7 +94,7 @@ paths:
},

"invalid required header": {
err: `response header "X-Blup" doesn't match the schema: string "bluuuuuup" doesn't match the regular expression "^blup$"`,
err: `response header "X-Blup" doesn't match schema: string "bluuuuuup" doesn't match the regular expression "^blup$"`,
headers: map[string]string{
"X-Blip": "blip",
"x-blop": "blop",
Expand Down
70 changes: 55 additions & 15 deletions openapi3filter/validate_response.go
Expand Up @@ -81,21 +81,9 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error
for _, k := range headers {
s := response.Headers[k]
h := input.Header.Get(k)
if h == "" {
if s.Value.Required {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("response header %q missing", k),
}
}
continue
}
if err := s.Value.Schema.Value.VisitJSON(h, opts...); err != nil {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("response header %q doesn't match the schema", k),
Err: err,
}
err := validateResponseHeader(k, h, s, input, opts)
if err != nil {
return err
}
}

Expand Down Expand Up @@ -171,6 +159,58 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error
return nil
}

func validateResponseHeader(
headerName string,
headerVal string,
s *openapi3.HeaderRef,
input *ResponseValidationInput,
opts []openapi3.SchemaValidationOption,
) error {
if headerVal == "" && s.Value.Required {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("response header %q missing", headerName),
}
}

sm, err := s.Value.SerializationMethod()
if err != nil {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("unable to get header %q serialization method", headerName),
Err: err,
}
}

var decodedValue interface{}
dec := &headerParamDecoder{header: input.Header}
found := false
if decodedValue, found, err = decodeValue(dec, headerName, sm, s.Value.Schema, s.Value.Required); err != nil {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("unable to decode header %q value", headerName),
Err: err,
}
}

if found {
// If the value was found but not decoded it means there is no
// schema.Type defining how to interpret the header value
if decodedValue == nil {
// revert to the header's original value
decodedValue = headerVal
}
if err := s.Value.Schema.Value.VisitJSON(decodedValue, opts...); err != nil {
return &ResponseError{
Input: input,
Reason: fmt.Sprintf("response header %q doesn't match schema", headerName),
Err: err,
}
}
}
return nil
}

// getSchemaIdentifier gets something by which a schema could be identified.
// A schema by itself doesn't have a true identity field. This function makes
// a best effort to get a value that can fill that void.
Expand Down