diff --git a/config/api.go b/config/api.go index 0d34f0c33..ccbeb7a44 100644 --- a/config/api.go +++ b/config/api.go @@ -1,28 +1,77 @@ package config +import ( + "github.com/qri-io/jsonschema" +) + // DefaultAPIPort is local the port webapp serves on by default var DefaultAPIPort = "2503" // API holds configuration for the qri JSON api type API struct { - Enabled bool + Enabled bool `json:"enabled"` // APIPort specifies the port to listen for JSON API calls - Port string + Port string `json:"port"` // read-only mode - ReadOnly bool + ReadOnly bool `json:"readonly"` // URLRoot is the base url for this server - URLRoot string + URLRoot string `json:"urlroot"` // TLS enables https via letsEyncrypt - TLS bool + TLS bool `json:"tls"` // if true, requests that have X-Forwarded-Proto: http will be redirected // to their https variant - ProxyForceHTTPS bool + ProxyForceHTTPS bool `json:"proxyforcehttps"` // support CORS signing from a list of origins - AllowedOrigins []string + AllowedOrigins []string `json:"allowedorigins"` +} + +// Validate validates all fields of api returning all errors found. +func (a API) Validate() error { + schema := jsonschema.Must(`{ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "api", + "description": "Config for the api", + "type": "object", + "required": ["enabled", "port", "readonly", "urlroot", "tls", "proxyforcehttps", "allowedorigins"], + "properties": { + "enabled": { + "description": "When false, the api port does not listen for calls", + "type": "boolean" + }, + "port": { + "description": "The port that listens for JSON API calls", + "type": "string" + }, + "readonly": { + "description": "When true, api port limits the accepted calls to certain GET requests", + "type": "boolean" + }, + "urlroot": { + "description": "The base url for this server", + "type": "string" + }, + "tls": { + "description": "Enables https via letsEncrypt", + "type": "boolean" + }, + "proxyforcehttps": { + "description": "When true, requests that have X-Forwarded-Proto: http will be redirected to their https variant", + "type": "boolean" + }, + "allowedorigins": { + "description": "Support CORS signing from a list of origins", + "type": "array", + "items": { + "type": "string" + } + } + } + }`) + return validate(schema, &a) } -// Default returns the default configuration details -func (API) Default() *API { +// DefaultAPI returns the default configuration details +func DefaultAPI() *API { return &API{ Enabled: true, Port: DefaultAPIPort, diff --git a/config/api_test.go b/config/api_test.go new file mode 100644 index 000000000..0465b7565 --- /dev/null +++ b/config/api_test.go @@ -0,0 +1,21 @@ +package config + +import ( + "testing" +) + +func TestAPIValidate(t *testing.T) { + apiDefault := API{ + Enabled: true, + Port: "2503", + ReadOnly: false, + URLRoot: "", + TLS: false, + ProxyForceHTTPS: false, + AllowedOrigins: []string{"http://localhost:2505"}, + } + err := apiDefault.Validate() + if err != nil { + t.Errorf("error validating apiDefault: %s", err) + } +} diff --git a/config/config.go b/config/config.go index c7390f912..03758c5ed 100644 --- a/config/config.go +++ b/config/config.go @@ -3,12 +3,14 @@ package config import ( + "encoding/json" "fmt" "io/ioutil" "reflect" "strconv" "strings" + "github.com/qri-io/jsonschema" "gopkg.in/yaml.v2" ) @@ -34,7 +36,7 @@ func (Config) Default() *Config { Store: Store{}.Default(), CLI: CLI{}.Default(), - API: API{}.Default(), + API: DefaultAPI(), P2P: P2P{}.Default(), Webapp: Webapp{}.Default(), RPC: RPC{}.Default(), @@ -163,3 +165,18 @@ func (cfg Config) path(path string) (elem reflect.Value, err error) { return elem, nil } + +// valiate is a helper function that wraps json.Marshal an ValidateBytes +// it is used by each struct that is in a Config field (eg API, Profile, etc) +func validate(rs *jsonschema.RootSchema, s interface{}) error { + strct, err := json.Marshal(s) + if err != nil { + return fmt.Errorf("error marshaling profile to json: %s", err) + } + if errors, err := rs.ValidateBytes(strct); len(errors) > 0 { + return fmt.Errorf("%s", errors[0]) + } else if err != nil { + return err + } + return nil +}