Пример #1
0
func (s *SpecValidator) validateDuplicatePropertyNames() *Result {
	// definition can't declare a property that's already defined by one of its ancestors
	res := new(Result)
	for k, sch := range s.spec.Spec().Definitions {
		if len(sch.AllOf) == 0 {
			continue
		}

		knownanc := map[string]struct{}{
			"#/definitions/" + k: struct{}{},
		}

		ancs := s.validateCircularAncestry(k, sch, knownanc)
		if len(ancs) > 0 {
			res.AddErrors(errors.New(422, "definition %q has circular ancestry: %v", k, ancs))
			return res
		}

		knowns := make(map[string]struct{})
		dups := s.validateSchemaPropertyNames(k, sch, knowns)
		if len(dups) > 0 {
			var pns []string
			for _, v := range dups {
				pns = append(pns, v.Definition+"."+v.Name)
			}
			res.AddErrors(errors.New(422, "definition %q contains duplicate properties: %v", k, pns))
		}

	}
	return res
}
Пример #2
0
func (s *SpecValidator) validatePathParamPresence(path string, fromPath, fromOperation []string) *Result {
	// Each defined operation path parameters must correspond to a named element in the API's path pattern.
	// (For example, you cannot have a path parameter named id for the following path /pets/{petId} but you must have a path parameter named petId.)
	res := new(Result)
	for _, l := range fromPath {
		var matched bool
		for _, r := range fromOperation {
			if l == "{"+r+"}" {
				matched = true
				break
			}
		}
		if !matched {
			res.Errors = append(res.Errors, errors.New(422, "path param %q has no parameter definition", l))
		}
	}

	for _, p := range fromOperation {
		var matched bool
		for _, r := range fromPath {
			if "{"+p+"}" == r {
				matched = true
				break
			}
		}
		if !matched {
			res.AddErrors(errors.New(422, "path param %q is not present in path %q", p, path))
		}
	}

	return res
}
Пример #3
0
func (s *SpecValidator) validateParameters() *Result {
	// each parameter should have a unique `name` and `type` combination
	// each operation should have only 1 parameter of type body
	// each api path should be non-verbatim (account for path param names) unique per method
	res := new(Result)
	for method, pi := range s.spec.Operations() {
		knownPaths := make(map[string]string)
		for path, op := range pi {
			segments, params := parsePath(path)
			knowns := make([]string, 0, len(segments))
			for _, s := range segments {
				knowns = append(knowns, s)
			}
			var fromPath []string
			for _, i := range params {
				fromPath = append(fromPath, knowns[i])
				knowns[i] = "!"
			}
			knownPath := strings.Join(knowns, "/")
			if orig, ok := knownPaths[knownPath]; ok {
				res.AddErrors(errors.New(422, "path %s overlaps with %s", path, orig))
			} else {
				knownPaths[knownPath] = path
			}

			ptypes := make(map[string]map[string]struct{})
			var firstBodyParam string

			var paramNames []string
			for _, pr := range op.Parameters {
				pnames, ok := ptypes[pr.In]
				if !ok {
					pnames = make(map[string]struct{})
					ptypes[pr.In] = pnames
				}

				_, ok = pnames[pr.Name]
				if ok {
					res.AddErrors(errors.New(422, "duplicate parameter name %q for %q in operation %q", pr.Name, pr.In, op.ID))
				}
				pnames[pr.Name] = struct{}{}
			}
			for _, pr := range s.spec.ParamsFor(method, path) {
				if pr.In == "body" {
					if firstBodyParam != "" {
						res.AddErrors(errors.New(422, "operation %q has more than 1 body param (accepted: %q, dropped: %q)", op.ID, firstBodyParam, pr.Name))
					}
					firstBodyParam = pr.Name
				}

				if pr.In == "path" {
					paramNames = append(paramNames, pr.Name)
				}
			}
			res.Merge(s.validatePathParamPresence(fromPath, paramNames))
		}
	}
	return res
}
Пример #4
0
// Respond renders the response after doing some content negotiation
func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) {

	offers := []string{c.api.DefaultProduces()}
	for _, mt := range produces {
		if mt != c.api.DefaultProduces() {
			offers = append(offers, mt)
		}
	}

	format := c.ResponseFormat(r, offers)
	rw.Header().Set(httpkit.HeaderContentType, format)

	if err, ok := data.(error); ok {
		if format == "" {
			rw.Header().Set(httpkit.HeaderContentType, httpkit.JSONMime)
		}
		if route == nil || route.Operation == nil {
			c.api.ServeErrorFor("")(rw, r, err)
			return
		}
		c.api.ServeErrorFor(route.Operation.ID)(rw, r, err)
		return
	}
	if route == nil || route.Operation == nil {
		rw.WriteHeader(200)
		if r.Method == "HEAD" {
			return
		}
		producers := c.api.ProducersFor(offers)
		prod, ok := producers[format]
		if !ok {
			panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format))
		}
		if err := prod.Produce(rw, data); err != nil {
			panic(err) // let the recovery middleware deal with this
		}
		return
	}

	if _, code, ok := route.Operation.SuccessResponse(); ok {
		rw.WriteHeader(code)
		if code == 201 || code == 204 || r.Method == "HEAD" {
			return
		}

		producers := route.Producers
		prod, ok := producers[format]
		if !ok {
			panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format))
		}
		if err := prod.Produce(rw, data); err != nil {
			panic(err) // let the recovery middleware deal with this
		}
		return
	}
	c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response"))
}
Пример #5
0
func (s *SpecValidator) validateItems() *Result {
	// validate parameter, items, schema and response objects for presence of item if type is array
	res := new(Result)

	// TODO: implement support for lookups of refs
	for method, pi := range s.spec.Operations() {
		for path, op := range pi {
			for _, param := range s.spec.ParamsFor(method, path) {
				if param.TypeName() == "array" && param.ItemsTypeName() == "" {
					res.AddErrors(errors.New(422, "param %q for %q is a collection without an element type", param.Name, op.ID))
					continue
				}
				if param.In != "body" {
					if param.Items != nil {
						items := param.Items
						for items.TypeName() == "array" {
							if items.ItemsTypeName() == "" {
								res.AddErrors(errors.New(422, "param %q for %q is a collection without an element type", param.Name, op.ID))
								break
							}
							items = items.Items
						}
					}
				} else {
					if err := s.validateSchemaItems(*param.Schema, fmt.Sprintf("body param %q", param.Name), op.ID); err != nil {
						res.AddErrors(err)
					}
				}
			}

			var responses []spec.Response
			if op.Responses != nil {
				if op.Responses.Default != nil {
					responses = append(responses, *op.Responses.Default)
				}
				for _, v := range op.Responses.StatusCodeResponses {
					responses = append(responses, v)
				}
			}

			for _, resp := range responses {
				for hn, hv := range resp.Headers {
					if hv.TypeName() == "array" && hv.ItemsTypeName() == "" {
						res.AddErrors(errors.New(422, "header %q for %q is a collection without an element type", hn, op.ID))
					}
				}
				if resp.Schema != nil {
					if err := s.validateSchemaItems(*resp.Schema, "response body", op.ID); err != nil {
						res.AddErrors(err)
					}
				}
			}
		}
	}
	return res
}
Пример #6
0
// APIKeyAuth creates an authenticator that uses a token for authorization.
// This token can be obtained from either a header or a query string
func APIKeyAuth(name, in string, authenticate TokenAuthentication) httpkit.Authenticator {
	inl := strings.ToLower(in)
	if inl != "query" && inl != "header" {
		// panic because this is most likely a typo
		panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\"."))
	}

	var getToken func(*http.Request) string
	switch inl {
	case "header":
		getToken = func(r *http.Request) string { return r.Header.Get(name) }
	case "query":
		getToken = func(r *http.Request) string { return r.URL.Query().Get(name) }
	}

	return httpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
		token := getToken(r)
		if token == "" {
			return false, nil, nil
		}

		p, err := authenticate(token)
		return true, p, err
	})
}
Пример #7
0
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls
func (o *UploadTaskFileParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
	var res []error
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		if err != http.ErrNotMultipart {
			return err
		} else if err := r.ParseForm(); err != nil {
			return err
		}
	}
	fds := httpkit.Values(r.Form)

	fdDescription, fdhkDescription, _ := fds.GetOK("description")
	if err := o.bindDescription(fdDescription, fdhkDescription, route.Formats); err != nil {
		res = append(res, err)
	}

	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		res = append(res, errors.New(400, "reading file %q failed: %v", "file", err))
	} else {
		o.File = httpkit.File{Data: file, Header: fileHeader}
	}

	rID, rhkID, _ := route.Params.GetOK("id")
	if err := o.bindID(rID, rhkID, route.Formats); err != nil {
		res = append(res, err)
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}
Пример #8
0
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls
func (o *UploadFileParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
	var res []error
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		return err
	}
	fds := httpkit.Values(r.Form)

	fdAdditionalMetadata, fdhkAdditionalMetadata, _ := fds.GetOK("additionalMetadata")
	if err := o.bindAdditionalMetadata(fdAdditionalMetadata, fdhkAdditionalMetadata, route.Formats); err != nil {
		res = append(res, err)
	}

	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		res = append(res, errors.New(400, "reading file %q failed: %v", "file", err))
	} else {
		o.File = httpkit.File{Data: file, Header: fileHeader}
	}

	rPetID, rhkPetID, _ := route.Params.GetOK("petId")
	if err := o.bindPetID(rPetID, rhkPetID, route.Formats); err != nil {
		res = append(res, err)
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}
Пример #9
0
func (s *SpecValidator) validateRequiredDefinitions() *Result {
	// Each definition property listed in the required array must be defined in the properties of the model
	res := new(Result)
	for d, v := range s.spec.Spec().Definitions {
	REQUIRED:
		for _, pn := range v.Required {
			if _, ok := v.Properties[pn]; ok {
				continue
			}

			for pp := range v.PatternProperties {
				re := regexp.MustCompile(pp)
				if re.MatchString(pn) {
					continue REQUIRED
				}
			}

			if v.AdditionalProperties != nil {
				if v.AdditionalProperties.Allows {
					continue
				}
				if v.AdditionalProperties.Schema != nil {
					continue
				}
			}

			res.AddErrors(errors.New(422, "%q is present in required but not defined as property in defintion %q", pn, d))
		}
	}
	return res
}
Пример #10
0
func (s *SpecValidator) validateReferencedDefinitions() *Result {
	// Each referenceable definition must have references.
	defs := s.spec.Spec().Definitions
	if len(defs) == 0 {
		return nil
	}

	expected := make(map[string]struct{})
	for k := range defs {
		expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{}
	}
	for _, k := range s.spec.AllDefinitionReferences() {
		if _, ok := expected[k]; ok {
			delete(expected, k)
		}
	}

	if len(expected) == 0 {
		return nil
	}
	var result Result
	for k := range expected {
		result.AddErrors(errors.New(422, "definition %q is not used anywhere", k))
	}
	return &result
}
Пример #11
0
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
	res := new(Result)
	for k := range s.spec.Spec().Paths.Paths {
		if strings.Contains(k, "{}") {
			res.AddErrors(errors.New(422, "%q contains an empty path parameter", k))
		}
	}
	return res
}
Пример #12
0
func (s *schemaSliceValidator) Validate(data interface{}) *Result {
	result := new(Result)
	if data == nil {
		return result
	}
	val := reflect.ValueOf(data)
	size := val.Len()

	if s.Items != nil && s.Items.Schema != nil {
		validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats)
		for i := 0; i < size; i++ {
			validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i))
			value := val.Index(i)
			result.Merge(validator.Validate(value.Interface()))
		}
	}

	itemsSize := int64(0)
	if s.Items != nil && len(s.Items.Schemas) > 0 {
		itemsSize = int64(len(s.Items.Schemas))
		for i := int64(0); i < itemsSize; i++ {
			validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats)
			result.Merge(validator.Validate(val.Index(int(i)).Interface()))
		}

	}
	if s.AdditionalItems != nil && itemsSize < int64(size) {
		if s.Items != nil && len(s.Items.Schemas) > 0 && !s.AdditionalItems.Allows {
			result.AddErrors(errors.New(422, "array doesn't allow for additional items"))
		}
		if s.AdditionalItems.Schema != nil {
			for i := itemsSize; i < (int64(size)-itemsSize)+1; i++ {
				validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats)
				result.Merge(validator.Validate(val.Index(int(i)).Interface()))
			}
		}
	}

	if s.MinItems != nil {
		if err := validate.MinItems(s.Path, s.In, int64(size), *s.MinItems); err != nil {
			result.AddErrors(err)
		}
	}
	if s.MaxItems != nil {
		if err := validate.MaxItems(s.Path, s.In, int64(size), *s.MaxItems); err != nil {
			result.AddErrors(err)
		}
	}
	if s.UniqueItems {
		if err := validate.UniqueItems(s.Path, s.In, val.Interface()); err != nil {
			result.AddErrors(err)
		}
	}
	result.Inc()
	return result
}
Пример #13
0
// Bind perform the databinding and validation
func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer httpkit.Consumer, data interface{}) error {
	val := reflect.Indirect(reflect.ValueOf(data))
	isMap := val.Kind() == reflect.Map
	var result []error

	for fieldName, param := range o.Parameters {
		binder := o.paramBinders[fieldName]

		var target reflect.Value
		if !isMap {
			binder.Name = fieldName
			target = val.FieldByName(fieldName)
		}

		if isMap {
			tpe := binder.Type()
			if tpe == nil {
				if param.Schema.Type.Contains("array") {
					tpe = reflect.TypeOf([]interface{}{})
				} else {
					tpe = reflect.TypeOf(map[string]interface{}{})
				}
			}
			target = reflect.Indirect(reflect.New(tpe))

		}

		if !target.IsValid() {
			result = append(result, errors.New(500, "parameter name %q is an unknown field", binder.Name))
			continue
		}

		if err := binder.Bind(request, routeParams, consumer, target); err != nil {
			result = append(result, err)
			continue
		}

		if binder.validator != nil {
			rr := binder.validator.Validate(target.Interface())
			if rr != nil && rr.HasErrors() {
				result = append(result, rr.AsError())
			}
		}

		if isMap {
			val.SetMapIndex(reflect.ValueOf(param.Name), target)
		}
	}

	if len(result) > 0 {
		return errors.CompositeValidationError(result...)
	}

	return nil
}
Пример #14
0
func addItem(item *models.Item) error {
	if item == nil {
		return errors.New(500, "item must be present")
	}

	itemsLock.Lock()
	defer itemsLock.Unlock()

	item.ID = newItemID()
	items[item.ID] = item

	return nil
}
Пример #15
0
func (s *SpecValidator) validateDuplicateOperationIDs() *Result {
	res := new(Result)
	known := make(map[string]int)
	for _, v := range s.spec.OperationIDs() {
		if v != "" {
			known[v]++
		}
	}
	for k, v := range known {
		if v > 1 {
			res.AddErrors(errors.New(422, "%q is defined %d times", k, v))
		}
	}
	return res
}
Пример #16
0
// Validate validates the swagger spec
func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) {
	var sd *spec.Document

	switch v := data.(type) {
	case *spec.Document:
		sd = v
	}
	if sd == nil {
		errs = sErr(errors.New(500, "spec validator can only validate spec.Document objects"))
		return
	}
	s.spec = sd

	errs = new(Result)
	warnings = new(Result)

	schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats)
	var obj interface{}
	if err := json.Unmarshal(sd.Raw(), &obj); err != nil {
		errs.AddErrors(err)
		return
	}
	errs.Merge(schv.Validate(obj)) // error -
	if errs.HasErrors() {
		return // no point in continuing
	}

	errs.Merge(s.validateReferencesValid()) // error -
	if errs.HasErrors() {
		return // no point in continuing
	}

	errs.Merge(s.validateDuplicateOperationIDs())
	errs.Merge(s.validateDuplicatePropertyNames())         // error -
	errs.Merge(s.validateParameters())                     // error -
	errs.Merge(s.validateItems())                          // error -
	errs.Merge(s.validateRequiredDefinitions())            // error -
	errs.Merge(s.validateDefaultValueValidAgainstSchema()) // error -
	errs.Merge(s.validateExamplesValidAgainstSchema())     // error -
	errs.Merge(s.validateNonEmptyPathParamNames())

	warnings.Merge(s.validateUniqueSecurityScopes())            // warning
	warnings.Merge(s.validateUniqueScopesSecurityDefinitions()) // warning
	warnings.Merge(s.validateReferenced())                      // warning

	return
}
Пример #17
0
func (s *SpecValidator) validateReferencesValid() *Result {
	// each reference must point to a valid object
	res := new(Result)
	for _, r := range s.spec.AllRefs() {
		if !r.IsValidURI() {
			res.AddErrors(errors.New(404, "invalid ref %q", r.String()))
		}
	}
	if !res.HasErrors() {
		exp, err := s.spec.Expanded()
		if err != nil {
			res.AddErrors(err)
		}
		s.expanded = exp
	}
	return res
}
Пример #18
0
// BindValidRequest binds a params object to a request but only when the request is valid
// if the request is not valid an error will be returned
func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, binder RequestBinder) error {
	var res []error

	requestContentType := "*/*"
	// check and validate content type, select consumer
	if httpkit.HasBody(request) {
		ct, _, err := httpkit.ContentType(request.Header)
		if err != nil {
			res = append(res, err)
		} else {
			if err := validateContentType(route.Consumes, ct); err != nil {
				res = append(res, err)
			}
			if len(res) == 0 {
				cons, ok := route.Consumers[ct]
				if !ok {
					res = append(res, errors.New(500, "no consumer registered for %s", ct))
				} else {
					route.Consumer = cons
					requestContentType = ct
				}
			}
		}
	}

	// check and validate the response format
	if len(res) == 0 && httpkit.HasBody(request) {
		if str := NegotiateContentType(request, route.Produces, requestContentType); str == "" {
			res = append(res, errors.InvalidResponseFormat(request.Header.Get(httpkit.HeaderAccept), route.Produces))
		}
	}

	// now bind the request with the provided binder
	// it's assumed the binder will also validate the request and return an error if the
	// request is invalid
	if binder != nil && len(res) == 0 {
		if err := binder.BindRequest(request, route); err != nil {
			return err
		}
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}
Пример #19
0
func updateItem(id int64, item *models.Item) error {
	if item == nil {
		return errors.New(500, "item must be present")
	}

	itemsLock.Lock()
	defer itemsLock.Unlock()

	_, exists := items[id]
	if !exists {
		return errors.NotFound("not found: item %d", id)
	}

	item.ID = id
	items[id] = item
	return nil
}
Пример #20
0
func unmarshalPricingComponent(data []byte, consumer httpkit.Consumer) (PricingComponent, error) {
	buf := bytes.NewBuffer(data)
	buf2 := bytes.NewBuffer(data)

	// the first time this is read is to fetch the value of the @type property.
	var getType struct {
		Type string `json:"@type"`
	}
	if err := consumer.Consume(buf, &getType); err != nil {
		return nil, err
	}

	if err := validate.RequiredString("@type", "body", getType.Type); err != nil {
		return nil, err
	}

	// The value of @type is used to determine which type to create and unmarshal the data into
	switch getType.Type {
	case "flatPricingComponent":
		var result FlatPricingComponent
		if err := consumer.Consume(buf2, &result); err != nil {
			return nil, err
		}
		return &result, nil

	case "tieredPricingComponent":
		var result TieredPricingComponent
		if err := consumer.Consume(buf2, &result); err != nil {
			return nil, err
		}
		return &result, nil

	case "tieredVolumePricingComponent":
		var result TieredVolumePricingComponent
		if err := consumer.Consume(buf2, &result); err != nil {
			return nil, err
		}
		return &result, nil

	}
	return nil, errors.New(422, "invalid @type value: %q", getType.Type)

}
Пример #21
0
func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) error {
	if !schema.Type.Contains("array") {
		return nil
	}

	if schema.Items == nil || schema.Items.Len() == 0 {
		return errors.New(422, "%s for %q is a collection without an element type", prefix, opID)
	}

	schemas := schema.Items.Schemas
	if schema.Items.Schema != nil {
		schemas = []spec.Schema{*schema.Items.Schema}
	}
	for _, sch := range schemas {
		if err := s.validateSchemaItems(sch, prefix, opID); err != nil {
			return err
		}
	}
	return nil
}
Пример #22
0
func (v *validation) contentType() {
	if len(v.result) == 0 && httpkit.HasBody(v.request) {
		ct, _, err := v.context.ContentType(v.request)
		if err != nil {
			v.result = append(v.result, err)
		}
		if len(v.result) == 0 {
			if err := validateContentType(v.route.Consumes, ct); err != nil {
				v.result = append(v.result, err)
			}
		}
		if ct != "" && v.route.Consumer == nil {
			cons, ok := v.route.Consumers[ct]
			if !ok {
				v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct))
			} else {
				v.route.Consumer = cons
			}
		}
	}
}
Пример #23
0
// Validate validates the swagger spec
func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) {
	var sd *spec.Document

	switch v := data.(type) {
	case *spec.Document:
		sd = v
	}
	if sd == nil {
		errs = sErr(errors.New(500, "spec validator can only validate spec.Document objects"))
		return
	}
	s.spec = sd

	errs = new(Result)
	warnings = new(Result)

	schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats)
	errs.Merge(schv.Validate(sd.Spec())) // error -
	if errs.HasErrors() {
		return // no point in continuing
	}
	errs.Merge(s.validateReferencesValid()) // error
	if errs.HasErrors() {
		return // no point in continuing
	}

	errs.Merge(s.validateParameters())                     // error -
	errs.Merge(s.validateItems())                          // error -
	errs.Merge(s.validateRequiredDefinitions())            // error -
	errs.Merge(s.validateDefaultValueValidAgainstSchema()) // error

	warnings.Merge(s.validateUniqueSecurityScopes())            // warning
	warnings.Merge(s.validateUniqueScopesSecurityDefinitions()) // warning
	warnings.Merge(s.validateReferenced())                      // warning

	return
}
Пример #24
0
func TestOperationExecutor(t *testing.T) {
	spec, api := petstore.NewAPI(t)
	api.RegisterOperation("get", "/pets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) {
		return []interface{}{
			map[string]interface{}{"id": 1, "name": "a dog"},
		}, nil
	}))

	context := NewContext(spec, api, nil)
	context.router = DefaultRouter(spec, context.api)
	mw := newOperationExecutor(context)

	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/pets", nil)
	request.Header.Add("Accept", "application/json")
	request.SetBasicAuth("admin", "admin")
	mw.ServeHTTP(recorder, request)
	assert.Equal(t, 200, recorder.Code)
	assert.Equal(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String())

	spec, api = petstore.NewAPI(t)
	api.RegisterOperation("get", "/pets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) {
		return nil, errors.New(422, "expected")
	}))

	context = NewContext(spec, api, nil)
	context.router = DefaultRouter(spec, context.api)
	mw = newOperationExecutor(context)

	recorder = httptest.NewRecorder()
	request, _ = http.NewRequest("GET", "/pets", nil)
	request.Header.Add("Accept", "application/json")
	request.SetBasicAuth("admin", "admin")
	mw.ServeHTTP(recorder, request)
	assert.Equal(t, 422, recorder.Code)
	assert.Equal(t, `{"code":422,"message":"expected"}`, recorder.Body.String())
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls
func (o *AddScreenshotParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
	var res []error
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		return err
	}
	//fds := httpkit.Values(r.Form)

	rID, rhkID, _ := route.Params.GetOK("id")
	if err := o.bindID(rID, rhkID, route.Formats); err != nil {
		res = append(res, err)
	}

	screenshot, screenshotHeader, err := r.FormFile("screenshot")
	if err != nil {
		res = append(res, errors.New(400, "reading file %q failed: %v", "screenshot", err))
	} else {
		o.Screenshot = httpkit.File{Data: screenshot, Header: screenshotHeader}
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}
Пример #26
0
func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams, consumer httpkit.Consumer, target reflect.Value) error {
	// fmt.Println("binding", p.name, "as", p.Type())
	switch p.parameter.In {
	case "query":
		data, custom, err := p.readValue(request.URL.Query(), target)
		if err != nil {
			return err
		}
		if custom {
			return nil
		}

		return p.bindValue(data, target)

	case "header":
		data, custom, err := p.readValue(request.Header, target)
		if err != nil {
			return err
		}
		if custom {
			return nil
		}
		return p.bindValue(data, target)

	case "path":
		data, custom, err := p.readValue(routeParams, target)
		if err != nil {
			return err
		}
		if custom {
			return nil
		}
		return p.bindValue(data, target)

	case "formData":
		var err error
		var mt string

		mt, _, e := httpkit.ContentType(request.Header)
		if e != nil {
			// because of the interface conversion go thinks the error is not nil
			// so we first check for nil and then set the err var if it's not nil
			err = e
		}

		if err != nil {
			return errors.InvalidContentType("", []string{"multipart/form-data", "application/x-www-form-urlencoded"})
		}

		if mt != "multipart/form-data" && mt != "application/x-www-form-urlencoded" {
			return errors.InvalidContentType(mt, []string{"multipart/form-data", "application/x-www-form-urlencoded"})
		}

		if mt == "multipart/form-data" {
			if err := request.ParseMultipartForm(defaultMaxMemory); err != nil {
				return errors.NewParseError(p.Name, p.parameter.In, "", err)
			}
		}

		if err := request.ParseForm(); err != nil {
			return errors.NewParseError(p.Name, p.parameter.In, "", err)
		}

		if p.parameter.Type == "file" {
			file, header, err := request.FormFile(p.parameter.Name)
			if err != nil {
				return errors.NewParseError(p.Name, p.parameter.In, "", err)
			}
			target.Set(reflect.ValueOf(httpkit.File{Data: file, Header: header}))
			return nil
		}

		if request.MultipartForm != nil {
			data, custom, err := p.readValue(url.Values(request.MultipartForm.Value), target)
			if err != nil {
				return err
			}
			if custom {
				return nil
			}
			return p.bindValue(data, target)
		}
		data, custom, err := p.readValue(url.Values(request.PostForm), target)
		if err != nil {
			return err
		}
		if custom {
			return nil
		}
		return p.bindValue(data, target)

	case "body":
		newValue := reflect.New(target.Type())
		if err := consumer.Consume(request.Body, newValue.Interface()); err != nil {
			if err == io.EOF && p.parameter.Default != nil {
				target.Set(reflect.ValueOf(p.parameter.Default))
				return nil
			}
			tpe := p.parameter.Type
			if p.parameter.Format != "" {
				tpe = p.parameter.Format
			}
			return errors.InvalidType(p.Name, p.parameter.In, tpe, nil)
		}
		target.Set(reflect.Indirect(newValue))
		return nil
	default:
		return errors.New(500, fmt.Sprintf("invalid parameter location %q", p.parameter.In))
	}
}
Пример #27
0
func (s *SpecValidator) validateDefaultValueValidAgainstSchema() *Result {
	// every default value that is specified must validate against the schema for that property
	// headers, items, parameters, schema

	res := new(Result)

	for method, pathItem := range s.spec.Operations() {
		for path, op := range pathItem {
			// parameters
			var hasForm, hasBody bool
		PARAMETERS:
			for _, pr := range s.spec.ParamsFor(method, path) {
				// expand ref is necessary
				param := pr
				for param.Ref.String() != "" {
					obj, _, err := param.Ref.GetPointer().Get(s.spec.Spec())
					if err != nil {
						res.AddErrors(err)
						break PARAMETERS
					}
					param = obj.(spec.Parameter)
				}
				if param.In == "formData" {
					if hasBody && !hasForm {
						res.AddErrors(errors.New(422, "operation %q has both formData and body parameters", op.ID))
					}
					hasForm = true
				}
				if param.In == "body" {
					if hasForm && !hasBody {
						res.AddErrors(errors.New(422, "operation %q has both body and formData parameters", op.ID))
					}
					hasBody = true
				}
				// check simple paramters first
				if param.Default != nil && param.Schema == nil {
					//fmt.Println(param.Name, "in", param.In, "has a default without a schema")
					// check param valid
					res.Merge(NewParamValidator(&param, s.KnownFormats).Validate(param.Default))
				}

				if param.Items != nil {
					res.Merge(s.validateDefaultValueItemsAgainstSchema(param.Name, param.In, &param, param.Items))
				}

				if param.Schema != nil {
					res.Merge(s.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema))
				}
			}

			if op.Responses.Default != nil {
				dr := op.Responses.Default
				for nm, h := range dr.Headers {
					if h.Default != nil {
						res.Merge(NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default))
					}
					if h.Items != nil {
						res.Merge(s.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items))
					}
				}
			}
			for _, r := range op.Responses.StatusCodeResponses {
				for nm, h := range r.Headers {
					if h.Default != nil {
						res.Merge(NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default))
					}
					if h.Items != nil {
						res.Merge(s.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items))
					}
				}
			}

		}
	}

	for nm, sch := range s.spec.Spec().Definitions {
		res.Merge(s.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch))
	}

	return res
}
func addLearningResource(lr *models.LearningResource) error {
	if lr == nil {
		return errors.New(500, "lr must be present")
	}
	return db.CreateLearningResource(lr)
}
Пример #29
0
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
	mainResult := new(Result)
	if len(s.anyOfValidators) > 0 {
		var bestFailures *Result
		succeededOnce := false
		for _, anyOfSchema := range s.anyOfValidators {
			result := anyOfSchema.Validate(data)
			if result.IsValid() {
				bestFailures = nil
				succeededOnce = true
				break
			}
			if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
				bestFailures = result
			}
		}

		if !succeededOnce {
			mainResult.AddErrors(errors.New(422, "must validate at least one schema (anyOf)"))
		}
		if bestFailures != nil {
			mainResult.Merge(bestFailures)
		}
	}

	if len(s.oneOfValidators) > 0 {
		var bestFailures *Result
		validated := 0

		for _, oneOfSchema := range s.oneOfValidators {
			result := oneOfSchema.Validate(data)
			if result.IsValid() {
				validated++
				bestFailures = nil
				continue
			}
			if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
				bestFailures = result
			}
		}

		if validated != 1 {
			mainResult.AddErrors(errors.New(422, "must validate one and only one schema (oneOf)"))
			if bestFailures != nil {
				mainResult.Merge(bestFailures)
			}
		}
	}

	if len(s.allOfValidators) > 0 {
		validated := 0

		for _, allOfSchema := range s.allOfValidators {
			result := allOfSchema.Validate(data)
			if result.IsValid() {
				validated++
			}
			mainResult.Merge(result)
		}

		if validated != len(s.allOfValidators) {
			mainResult.AddErrors(errors.New(422, "must validate all the schemas (allOf)"))
		}
	}

	if s.notValidator != nil {
		result := s.notValidator.Validate(data)
		if result.IsValid() {
			mainResult.AddErrors(errors.New(422, "must not validate the schema (not)"))
		}
	}

	if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map {
		val := data.(map[string]interface{})
		for key := range val {
			if dep, ok := s.Dependencies[key]; ok {

				if dep.Schema != nil {
					mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats).Validate(data))
					continue
				}

				if len(dep.Property) > 0 {
					for _, depKey := range dep.Property {
						if _, ok := val[depKey]; !ok {
							mainResult.AddErrors(errors.New(422, "has a dependency on %s", depKey))
						}
					}
				}
			}
		}
	}

	mainResult.Inc()
	return mainResult
}