Merge pull request #1439 from sks/feature/fail_on_invalid_config
Return config validation errors in one go
This commit is contained in:
commit
e2ddefff31
3 changed files with 76 additions and 22 deletions
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
|
@ -48,6 +49,38 @@ type Config struct {
|
|||
StaticPasswords []password `json:"staticPasswords"`
|
||||
}
|
||||
|
||||
//Validate the configuration
|
||||
func (c Config) Validate() error {
|
||||
// Fast checks. Perform these first for a more responsive CLI.
|
||||
checks := []struct {
|
||||
bad bool
|
||||
errMsg string
|
||||
}{
|
||||
{c.Issuer == "", "no issuer specified in config file"},
|
||||
{!c.EnablePasswordDB && len(c.StaticPasswords) != 0, "cannot specify static passwords without enabling password db"},
|
||||
{c.Storage.Config == nil, "no storage supplied in config file"},
|
||||
{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS address to listen on"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
|
||||
{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
|
||||
{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
|
||||
{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
|
||||
{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
|
||||
}
|
||||
|
||||
var checkErrors []string
|
||||
|
||||
for _, check := range checks {
|
||||
if check.bad {
|
||||
checkErrors = append(checkErrors, check.errMsg)
|
||||
}
|
||||
}
|
||||
if len(checkErrors) != 0 {
|
||||
return fmt.Errorf("invalid Config:\n\t-\t%s", strings.Join(checkErrors, "\n\t-\t"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type password storage.Password
|
||||
|
||||
func (p *password) UnmarshalJSON(b []byte) error {
|
||||
|
|
|
@ -14,6 +14,47 @@ import (
|
|||
|
||||
var _ = yaml.YAMLToJSON
|
||||
|
||||
func TestValidConfiguration(t *testing.T) {
|
||||
configuration := Config{
|
||||
Issuer: "http://127.0.0.1:5556/dex",
|
||||
Storage: Storage{
|
||||
Type: "sqlite3",
|
||||
Config: &sql.SQLite3{
|
||||
File: "examples/dex.db",
|
||||
},
|
||||
},
|
||||
Web: Web{
|
||||
HTTP: "127.0.0.1:5556",
|
||||
},
|
||||
StaticConnectors: []Connector{
|
||||
{
|
||||
Type: "mockCallback",
|
||||
ID: "mock",
|
||||
Name: "Example",
|
||||
Config: &mock.CallbackConfig{},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := configuration.Validate(); err != nil {
|
||||
t.Fatalf("this configuration should have been valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidConfiguration(t *testing.T) {
|
||||
configuration := Config{}
|
||||
err := configuration.Validate()
|
||||
if err == nil {
|
||||
t.Fatal("this configuration should be invalid")
|
||||
}
|
||||
got := err.Error()
|
||||
wanted := `invalid Config:
|
||||
- no issuer specified in config file
|
||||
- no storage supplied in config file
|
||||
- must supply a HTTP/HTTPS address to listen on`
|
||||
if got != wanted {
|
||||
t.Fatalf("Expected error message to be %q, got %q", wanted, got)
|
||||
}
|
||||
}
|
||||
func TestUnmarshalConfig(t *testing.T) {
|
||||
rawConfig := []byte(`
|
||||
issuer: http://127.0.0.1:5556/dex
|
||||
|
|
|
@ -71,28 +71,8 @@ func serve(cmd *cobra.Command, args []string) error {
|
|||
if c.Logger.Level != "" {
|
||||
logger.Infof("config using log level: %s", c.Logger.Level)
|
||||
}
|
||||
|
||||
// Fast checks. Perform these first for a more responsive CLI.
|
||||
checks := []struct {
|
||||
bad bool
|
||||
errMsg string
|
||||
}{
|
||||
{c.Issuer == "", "no issuer specified in config file"},
|
||||
{!c.EnablePasswordDB && len(c.StaticPasswords) != 0, "cannot specify static passwords without enabling password db"},
|
||||
{c.Storage.Config == nil, "no storage supplied in config file"},
|
||||
{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS address to listen on"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
|
||||
{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
|
||||
{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
|
||||
{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
|
||||
{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
if check.bad {
|
||||
return fmt.Errorf("invalid config: %s", check.errMsg)
|
||||
}
|
||||
if err := c.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("config issuer: %s", c.Issuer)
|
||||
|
|
Reference in a new issue