Merge pull request #1439 from sks/feature/fail_on_invalid_config
Return config validation errors in one go
This commit is contained in:
		@@ -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 New Issue
	
	Block a user