From f1dc00c810ae4dd79aa2bdc56fdee64e9466762d Mon Sep 17 00:00:00 2001 From: max furman Date: Mon, 8 Oct 2018 23:25:18 -0700 Subject: [PATCH] add Provisioner config validation --- authority/config.go | 39 ++++++++++++++++++------ authority/config_test.go | 66 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 13 deletions(-) diff --git a/authority/config.go b/authority/config.go index 98485fbf..4abdd9a0 100644 --- a/authority/config.go +++ b/authority/config.go @@ -59,6 +59,22 @@ type Provisioner struct { EncryptedKey string `json:"encryptedKey,omitempty"` } +// Validate validates a provisioner. +func (p *Provisioner) Validate() error { + switch { + case p.Issuer == "": + return errors.New("provisioner issuer cannot be empty") + + case p.Type == "": + return errors.New("provisioner type cannot be empty") + + case p.Key == nil: + return errors.New("provisioner key cannot be empty") + } + + return nil +} + // Config represents the CA configuration and it's mapped to a JSON object. type Config struct { Root string `json:"root"` @@ -83,17 +99,22 @@ type AuthConfig struct { // Validate validates the authority configuration. func (c *AuthConfig) Validate() error { - switch { - case c == nil: + if c == nil { return errors.New("authority cannot be undefined") - case len(c.Provisioners) == 0: - return errors.New("authority.provisioners cannot be empty") - default: - if c.Template == nil { - c.Template = &x509util.ASN1DN{} - } - return nil } + if len(c.Provisioners) == 0 { + return errors.New("authority.provisioners cannot be empty") + } + for _, p := range c.Provisioners { + err := p.Validate() + if err != nil { + return err + } + } + if c.Template == nil { + c.Template = &x509util.ASN1DN{} + } + return nil } // LoadConfiguration parses the given filename in JSON format and returns the diff --git a/authority/config_test.go b/authority/config_test.go index cf4554c8..64315fac 100644 --- a/authority/config_test.go +++ b/authority/config_test.go @@ -8,8 +8,55 @@ import ( "github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/x509util" stepJOSE "github.com/smallstep/cli/jose" + jose "gopkg.in/square/go-jose.v2" ) +func TestProvisionerValidate(t *testing.T) { + type ProvisionerValidateTest struct { + p *Provisioner + err error + } + tests := map[string]func(*testing.T) ProvisionerValidateTest{ + "fail-empty-issuer": func(t *testing.T) ProvisionerValidateTest { + return ProvisionerValidateTest{ + p: &Provisioner{}, + err: errors.New("provisioner issuer cannot be empty"), + } + }, + "fail-empty-type": func(t *testing.T) ProvisionerValidateTest { + return ProvisionerValidateTest{ + p: &Provisioner{Issuer: "foo"}, + err: errors.New("provisioner type cannot be empty"), + } + }, + "fail-empty-key": func(t *testing.T) ProvisionerValidateTest { + return ProvisionerValidateTest{ + p: &Provisioner{Issuer: "foo", Type: "bar"}, + err: errors.New("provisioner key cannot be empty"), + } + }, + "ok": func(t *testing.T) ProvisionerValidateTest { + return ProvisionerValidateTest{ + p: &Provisioner{Issuer: "foo", Type: "bar", Key: &jose.JSONWebKey{}}, + } + }, + } + + for name, get := range tests { + t.Run(name, func(t *testing.T) { + tc := get(t) + err := tc.p.Validate() + if err != nil { + if assert.NotNil(t, tc.err) { + assert.Equals(t, tc.err.Error(), err.Error()) + } + } else { + assert.Nil(t, tc.err) + } + }) + } +} + func TestConfigValidate(t *testing.T) { maxjwk, err := stepJOSE.ParseKey("testdata/secrets/max_pub.jwk") assert.FatalError(t, err) @@ -233,19 +280,30 @@ func TestAuthConfigValidate(t *testing.T) { err error } tests := map[string]func(*testing.T) AuthConfigValidateTest{ - "nil": func(t *testing.T) AuthConfigValidateTest { + "fail-nil-authconfig": func(t *testing.T) AuthConfigValidateTest { return AuthConfigValidateTest{ ac: nil, err: errors.New("authority cannot be undefined"), } }, - "empty-provisioners": func(t *testing.T) AuthConfigValidateTest { + "fail-empty-provisioners": func(t *testing.T) AuthConfigValidateTest { return AuthConfigValidateTest{ ac: &AuthConfig{}, err: errors.New("authority.provisioners cannot be empty"), } }, - "empty-asn1dn-template": func(t *testing.T) AuthConfigValidateTest { + "fail-invalid-provisioners": func(t *testing.T) AuthConfigValidateTest { + return AuthConfigValidateTest{ + ac: &AuthConfig{ + Provisioners: []*Provisioner{ + &Provisioner{Issuer: "foo", Type: "bar", Key: &jose.JSONWebKey{}}, + &Provisioner{Issuer: "foo", Key: &jose.JSONWebKey{}}, + }, + }, + err: errors.New("provisioner type cannot be empty"), + } + }, + "ok-empty-asn1dn-template": func(t *testing.T) AuthConfigValidateTest { return AuthConfigValidateTest{ ac: &AuthConfig{ Provisioners: p, @@ -253,7 +311,7 @@ func TestAuthConfigValidate(t *testing.T) { asn1dn: x509util.ASN1DN{}, } }, - "custom-asn1dn": func(t *testing.T) AuthConfigValidateTest { + "ok-custom-asn1dn": func(t *testing.T) AuthConfigValidateTest { return AuthConfigValidateTest{ ac: &AuthConfig{ Provisioners: p,