mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 11:44:26 -06:00
605d056136
* * Teams: Appropriately apply user id filter in /api/teams/:id and /api/teams/search * Teams: Ensure that users searching for teams are only able see teams they have access to * Teams: Require teamGuardian admin privileges to list team members * Teams: Prevent org viewers from administering teams * Teams: Add org_id condition to team count query * Teams: clarify permission requirements in teams api docs * Teams: expand scenarios for team search tests * Teams: mock teamGuardian in tests Co-authored-by: Dan Cech <dcech@grafana.com> * remove duplicate WHERE statement * Fix for CVE-2022-21702 (cherry picked from commit 202d7c190082c094bc1dc13f7fe9464746c37f9e) * Lint and test fixes (cherry picked from commit 3e6b67d5504abf4a1d7b8d621f04d062c048e981) * check content type properly (cherry picked from commit 70b4458892bf2f776302720c10d24c9ff34edd98) * basic csrf origin check (cherry picked from commit 3adaa5ff39832364f6390881fb5b42ad47df92e1) * compare origin to host (cherry picked from commit 5443892699e8ed42836bb2b9a44744ff3e970f42) * simplify url parsing (cherry picked from commit b2ffbc9513fed75468628370a48b929d30af2b1d) * check csrf for GET requests, only compare origin (cherry picked from commit 8b81dc12d8f8a1f07852809c5b4d44f0f0b1d709) * parse content type properly (cherry picked from commit 16f76f4902e6f2188bea9606c68b551af186bdc0) * mentioned get in the comment (cherry picked from commit a7e61811ef8ae558ce721e2e3fed04ce7a5a5345) * add content-type: application/json to test HTTP requests * fix pluginproxy test * Fix linter when comparing errors Co-authored-by: Kevin Minehart <kmineh0151@gmail.com> Co-authored-by: Dan Cech <dcech@grafana.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Serge Zaitsev <serge.zaitsev@grafana.com> Co-authored-by: Vardan Torosyan <vardants@gmail.com>
98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package web
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
"reflect"
|
|
)
|
|
|
|
// Bind deserializes JSON payload from the request
|
|
func Bind(req *http.Request, v interface{}) error {
|
|
if req.Body != nil {
|
|
m, _, err := mime.ParseMediaType(req.Header.Get("Content-type"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if m != "application/json" {
|
|
return errors.New("bad content type")
|
|
}
|
|
defer func() { _ = req.Body.Close() }()
|
|
err = json.NewDecoder(req.Body).Decode(v)
|
|
if err != nil && !errors.Is(err, io.EOF) {
|
|
return err
|
|
}
|
|
}
|
|
return validate(v)
|
|
}
|
|
|
|
type Validator interface {
|
|
Validate() error
|
|
}
|
|
|
|
func validate(obj interface{}) error {
|
|
// First check if obj is nil, because we cannot validate those.
|
|
if obj == nil {
|
|
return nil
|
|
}
|
|
|
|
// Second, check if obj has a nil interface value.
|
|
// This is to prevent panics when obj is an instance of uninitialised struct pointer / interface.
|
|
t := reflect.TypeOf(obj)
|
|
v := reflect.ValueOf(obj)
|
|
|
|
if v.Kind() == reflect.Ptr && v.IsNil() {
|
|
return nil
|
|
}
|
|
|
|
// If type has a Validate() method - use that
|
|
if validator, ok := obj.(Validator); ok {
|
|
return validator.Validate()
|
|
}
|
|
|
|
// Otherwise, use reflection to match `binding:"Required"` struct field tags.
|
|
// Resolve all pointers and interfaces, until we get a concrete type.
|
|
for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr {
|
|
t = t.Elem()
|
|
v = v.Elem()
|
|
}
|
|
switch v.Kind() {
|
|
// For arrays and slices - iterate over each element and validate it recursively
|
|
case reflect.Slice, reflect.Array:
|
|
for i := 0; i < v.Len(); i++ {
|
|
e := v.Index(i).Interface()
|
|
if err := validate(e); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// For structs - iterate over each field, check for the "Required" constraint (Macaron legacy), then validate it recursively
|
|
case reflect.Struct:
|
|
for i := 0; i < v.NumField(); i++ {
|
|
field := t.Field(i)
|
|
value := v.Field(i)
|
|
rule := field.Tag.Get("binding")
|
|
if !value.CanInterface() {
|
|
continue
|
|
}
|
|
if rule == "Required" {
|
|
zero := reflect.Zero(field.Type).Interface()
|
|
if value.Kind() == reflect.Slice {
|
|
if value.Len() == 0 {
|
|
return fmt.Errorf("required slice %s must not be empty", field.Name)
|
|
}
|
|
} else if reflect.DeepEqual(zero, value.Interface()) {
|
|
return fmt.Errorf("required value %s must not be empty", field.Name)
|
|
}
|
|
}
|
|
if err := validate(value.Interface()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default: // ignore
|
|
}
|
|
return nil
|
|
}
|