mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 17:02:20 +08:00

* Move to new repository * Rename it to dual writer * Rename the function * Rename the methods * Rename to exportResource * Clean up logic in migrate and add TODOs * Add TODOs * Use generic client for unprovisioned * ForEachResource * More consolidation * Refactor more around client * Consolidate constants * ForEachFolder * More use of constants * Add FIXME notes * Use more constant * Remove Dashboard * Pass tree to folder manager * Replicate tree * Reduce export complexity * More refactoring * Use the ForEach for loading users * Limit in-memory folders * Isolate the object * Improve the export function * Move resources to resources package * Move delete operation * Move more logic * More consolidation * More renaming * Fix more issues * Ensure path exists when created a resource * Simply append error * Fix receiver lint issue * Fix cyclomatic complexity * Fix linting * Remove folder path creation
71 lines
2.1 KiB
Go
71 lines
2.1 KiB
Go
package provisioning
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// contentTypeJSON is the Content-Type for JSON requests as go standard library does not provide one
|
|
contentTypeJSON = "application/json"
|
|
// defaultMaxBodySize is the default max size for request bodies (10KB)
|
|
defaultMaxBodySize = 10 * 1024
|
|
// errMsgRequestTooLarge is the error message for request bodies that are too large
|
|
errMsgRequestTooLarge = "request body too large"
|
|
)
|
|
|
|
// readBody reads the request body and limits the size
|
|
func readBody(r *http.Request, maxSize int64) ([]byte, error) {
|
|
limitedBody := http.MaxBytesReader(nil, r.Body, maxSize)
|
|
body, err := io.ReadAll(limitedBody)
|
|
if err != nil {
|
|
var maxBytesError *http.MaxBytesError
|
|
if errors.As(err, &maxBytesError) {
|
|
return nil, fmt.Errorf("%s: max size %d bytes", errMsgRequestTooLarge, maxSize)
|
|
}
|
|
return nil, fmt.Errorf("error reading request body: %w", err)
|
|
}
|
|
defer func() { _ = limitedBody.Close() }()
|
|
|
|
return body, nil
|
|
}
|
|
|
|
// isJSONContentType checks if the request has the JSON Content-Type
|
|
func isJSONContentType(r *http.Request) bool {
|
|
contentType := r.Header.Get("Content-Type")
|
|
return strings.HasPrefix(contentType, contentTypeJSON)
|
|
}
|
|
|
|
// unmarshalJSON unmarshals the request body into the provided interface
|
|
// it also checks the Content-Type and limits the size of the request body
|
|
func unmarshalJSON(r *http.Request, maxSize int64, v interface{}) error {
|
|
if !isJSONContentType(r) {
|
|
return fmt.Errorf("content type is not JSON: %s", r.Header.Get("Content-Type"))
|
|
}
|
|
|
|
r.Body = http.MaxBytesReader(nil, r.Body, maxSize)
|
|
decoder := json.NewDecoder(r.Body)
|
|
decoder.DisallowUnknownFields()
|
|
|
|
if err := decoder.Decode(v); err != nil {
|
|
var maxBytesError *http.MaxBytesError
|
|
if errors.As(err, &maxBytesError) {
|
|
return fmt.Errorf("%s: max size %d bytes", errMsgRequestTooLarge, maxSize)
|
|
}
|
|
if err == io.EOF {
|
|
return fmt.Errorf("empty request body")
|
|
}
|
|
return fmt.Errorf("error decoding JSON: %w", err)
|
|
}
|
|
|
|
if decoder.More() {
|
|
return fmt.Errorf("multiple JSON objects not allowed")
|
|
}
|
|
|
|
return nil
|
|
}
|