forked from TrueCloudLab/certificates
085ae82163
Upgrade cli dependency.
423 lines
14 KiB
Go
423 lines
14 KiB
Go
package provisioner
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"net"
|
|
"net/url"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/assert"
|
|
"github.com/smallstep/cli/crypto/pemutil"
|
|
"github.com/smallstep/cli/crypto/x509util"
|
|
)
|
|
|
|
func Test_emailOnlyIdentity_Valid(t *testing.T) {
|
|
uri, err := url.Parse("https://example.com/1.0/getUser")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
e emailOnlyIdentity
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{"name@smallstep.com"}}}, false},
|
|
{"DNSNames", "name@smallstep.com", args{&x509.CertificateRequest{DNSNames: []string{"foo.bar.zar"}}}, true},
|
|
{"IPAddresses", "name@smallstep.com", args{&x509.CertificateRequest{IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}}}, true},
|
|
{"URIs", "name@smallstep.com", args{&x509.CertificateRequest{URIs: []*url.URL{uri}}}, true},
|
|
{"no-emails", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{}}}, true},
|
|
{"empty-email", "", args{&x509.CertificateRequest{EmailAddresses: []string{""}}}, true},
|
|
{"multiple-emails", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{"name@smallstep.com", "foo@smallstep.com"}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.e.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("emailOnlyIdentity.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_defaultPublicKeyValidator_Valid(t *testing.T) {
|
|
_shortRSA, err := pemutil.Read("./testdata/short-rsa.csr")
|
|
assert.FatalError(t, err)
|
|
shortRSA, ok := _shortRSA.(*x509.CertificateRequest)
|
|
assert.Fatal(t, ok)
|
|
|
|
_rsa, err := pemutil.Read("./testdata/rsa.csr")
|
|
assert.FatalError(t, err)
|
|
rsaCSR, ok := _rsa.(*x509.CertificateRequest)
|
|
assert.Fatal(t, ok)
|
|
|
|
_ecdsa, err := pemutil.Read("./testdata/ecdsa.csr")
|
|
assert.FatalError(t, err)
|
|
ecdsaCSR, ok := _ecdsa.(*x509.CertificateRequest)
|
|
assert.Fatal(t, ok)
|
|
|
|
_ed25519, err := pemutil.Read("./testdata/ed25519.csr")
|
|
assert.FatalError(t, err)
|
|
ed25519CSR, ok := _ed25519.(*x509.CertificateRequest)
|
|
assert.Fatal(t, ok)
|
|
|
|
v := defaultPublicKeyValidator{}
|
|
tests := []struct {
|
|
name string
|
|
csr *x509.CertificateRequest
|
|
err error
|
|
}{
|
|
{
|
|
"fail/unrecognized-key-type",
|
|
&x509.CertificateRequest{PublicKey: "foo"},
|
|
errors.New("unrecognized public key of type 'string' in CSR"),
|
|
},
|
|
{
|
|
"fail/rsa/too-short",
|
|
shortRSA,
|
|
errors.New("rsa key in CSR must be at least 2048 bits (256 bytes)"),
|
|
},
|
|
{
|
|
"ok/rsa",
|
|
rsaCSR,
|
|
nil,
|
|
},
|
|
{
|
|
"ok/ecdsa",
|
|
ecdsaCSR,
|
|
nil,
|
|
},
|
|
{
|
|
"ok/ed25519",
|
|
ed25519CSR,
|
|
nil,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := v.Valid(tt.csr); err != nil {
|
|
if assert.NotNil(t, tt.err) {
|
|
assert.HasPrefix(t, err.Error(), tt.err.Error())
|
|
}
|
|
} else {
|
|
assert.Nil(t, tt.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_commonNameValidator_Valid(t *testing.T) {
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v commonNameValidator
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok", "foo.bar.zar", args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: "foo.bar.zar"}}}, false},
|
|
{"empty", "", args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: ""}}}, true},
|
|
{"wrong", "foo.bar.zar", args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: "example.com"}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("commonNameValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_commonNameSliceValidator_Valid(t *testing.T) {
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v commonNameSliceValidator
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok", []string{"foo.bar.zar"}, args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: "foo.bar.zar"}}}, false},
|
|
{"ok", []string{"example.com", "foo.bar.zar"}, args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: "foo.bar.zar"}}}, false},
|
|
{"empty", []string{""}, args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: ""}}}, true},
|
|
{"wrong", []string{"foo.bar.zar"}, args{&x509.CertificateRequest{Subject: pkix.Name{CommonName: "example.com"}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("commonNameSliceValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_emailAddressesValidator_Valid(t *testing.T) {
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v emailAddressesValidator
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok0", []string{}, args{&x509.CertificateRequest{EmailAddresses: []string{}}}, false},
|
|
{"ok1", []string{"max@smallstep.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@smallstep.com"}}}, false},
|
|
{"ok2", []string{"max@step.com", "mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@step.com", "mike@step.com"}}}, false},
|
|
{"ok3", []string{"max@step.com", "mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"mike@step.com", "max@step.com"}}}, false},
|
|
{"fail1", []string{"max@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"mike@step.com"}}}, true},
|
|
{"fail2", []string{"mike@step.com"}, args{&x509.CertificateRequest{EmailAddresses: []string{"max@step.com", "mike@step.com"}}}, true},
|
|
{"fail3", []string{"mike@step.com", "max@step.com"}, args{&x509.CertificateRequest{DNSNames: []string{"mike@step.com", "mex@step.com"}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("dnsNamesValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_dnsNamesValidator_Valid(t *testing.T) {
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v dnsNamesValidator
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok0", []string{}, args{&x509.CertificateRequest{DNSNames: []string{}}}, false},
|
|
{"ok1", []string{"foo.bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"foo.bar.zar"}}}, false},
|
|
{"ok2", []string{"foo.bar.zar", "bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"foo.bar.zar", "bar.zar"}}}, false},
|
|
{"ok3", []string{"foo.bar.zar", "bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"bar.zar", "foo.bar.zar"}}}, false},
|
|
{"fail1", []string{"foo.bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"bar.zar"}}}, true},
|
|
{"fail2", []string{"foo.bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"bar.zar", "foo.bar.zar"}}}, true},
|
|
{"fail3", []string{"foo.bar.zar", "bar.zar"}, args{&x509.CertificateRequest{DNSNames: []string{"foo.bar.zar", "zar.bar"}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("dnsNamesValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_ipAddressesValidator_Valid(t *testing.T) {
|
|
ip1 := net.IPv4(10, 3, 2, 1)
|
|
ip2 := net.IPv4(10, 3, 2, 2)
|
|
ip3 := net.IPv4(10, 3, 2, 3)
|
|
|
|
type args struct {
|
|
req *x509.CertificateRequest
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v ipAddressesValidator
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{"ok0", []net.IP{}, args{&x509.CertificateRequest{IPAddresses: []net.IP{}}}, false},
|
|
{"ok1", []net.IP{ip1}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip1}}}, false},
|
|
{"ok2", []net.IP{ip1, ip2}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip1, ip2}}}, false},
|
|
{"ok3", []net.IP{ip1, ip2}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip2, ip1}}}, false},
|
|
{"fail1", []net.IP{ip1}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip2}}}, true},
|
|
{"fail2", []net.IP{ip1}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip2, ip1}}}, true},
|
|
{"fail3", []net.IP{ip1, ip2}, args{&x509.CertificateRequest{IPAddresses: []net.IP{ip1, ip3}}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.v.Valid(tt.args.req); (err != nil) != tt.wantErr {
|
|
t.Errorf("ipAddressesValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_validityValidator_Valid(t *testing.T) {
|
|
type fields struct {
|
|
min time.Duration
|
|
max time.Duration
|
|
}
|
|
type args struct {
|
|
crt *x509.Certificate
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
// TODO: Add test cases.
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
v := &validityValidator{
|
|
min: tt.fields.min,
|
|
max: tt.fields.max,
|
|
}
|
|
if err := v.Valid(tt.args.crt); (err != nil) != tt.wantErr {
|
|
t.Errorf("validityValidator.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_profileLimitDuration_Option(t *testing.T) {
|
|
n, fn := mockNow()
|
|
defer fn()
|
|
|
|
type test struct {
|
|
pld profileLimitDuration
|
|
so Options
|
|
cert *x509.Certificate
|
|
valid func(*x509.Certificate)
|
|
err error
|
|
}
|
|
tests := map[string]func() test{
|
|
"fail/notBefore-after-limit": func() test {
|
|
d, err := ParseTimeDuration("8h")
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{NotBefore: d},
|
|
cert: new(x509.Certificate),
|
|
err: errors.New("provisioning credential expiration ("),
|
|
}
|
|
},
|
|
"fail/requested-notAfter-after-limit": func() test {
|
|
d, err := ParseTimeDuration("4h")
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), NotAfter: d},
|
|
cert: new(x509.Certificate),
|
|
err: errors.New("provisioning credential expiration ("),
|
|
}
|
|
},
|
|
"ok/valid-notAfter-requested": func() test {
|
|
d, err := ParseTimeDuration("2h")
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), NotAfter: d, Backdate: 1 * time.Minute},
|
|
cert: new(x509.Certificate),
|
|
valid: func(cert *x509.Certificate) {
|
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
|
assert.Equals(t, cert.NotAfter, n.Add(5*time.Hour))
|
|
},
|
|
}
|
|
},
|
|
"ok/valid-notAfter-nil-limit-over-default": func() test {
|
|
return test{
|
|
pld: profileLimitDuration{def: 1 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), Backdate: 1 * time.Minute},
|
|
cert: new(x509.Certificate),
|
|
valid: func(cert *x509.Certificate) {
|
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
|
assert.Equals(t, cert.NotAfter, n.Add(4*time.Hour))
|
|
},
|
|
}
|
|
},
|
|
"ok/valid-notAfter-nil-limit-under-default": func() test {
|
|
return test{
|
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), Backdate: 1 * time.Minute},
|
|
cert: new(x509.Certificate),
|
|
valid: func(cert *x509.Certificate) {
|
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
|
assert.Equals(t, cert.NotAfter, n.Add(6*time.Hour))
|
|
},
|
|
}
|
|
},
|
|
"ok/over-limit-with-backdate": func() test {
|
|
return test{
|
|
pld: profileLimitDuration{def: 24 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
|
so: Options{Backdate: 1 * time.Minute},
|
|
cert: new(x509.Certificate),
|
|
valid: func(cert *x509.Certificate) {
|
|
assert.Equals(t, cert.NotBefore, n.Add(-time.Minute))
|
|
assert.Equals(t, cert.NotAfter, n.Add(6*time.Hour))
|
|
},
|
|
}
|
|
},
|
|
"ok/under-limit-with-backdate": func() test {
|
|
return test{
|
|
pld: profileLimitDuration{def: 24 * time.Hour, notAfter: n.Add(30 * time.Hour)},
|
|
so: Options{Backdate: 1 * time.Minute},
|
|
cert: new(x509.Certificate),
|
|
valid: func(cert *x509.Certificate) {
|
|
assert.Equals(t, cert.NotBefore, n.Add(-time.Minute))
|
|
assert.Equals(t, cert.NotAfter, n.Add(24*time.Hour))
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
tt := run()
|
|
prof := &x509util.Leaf{}
|
|
prof.SetSubject(tt.cert)
|
|
if err := tt.pld.Option(tt.so)(prof); err != nil {
|
|
if assert.NotNil(t, tt.err) {
|
|
assert.HasPrefix(t, err.Error(), tt.err.Error())
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tt.err) {
|
|
tt.valid(prof.Subject())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_profileDefaultDuration_Option(t *testing.T) {
|
|
tm, fn := mockNow()
|
|
defer fn()
|
|
|
|
v := profileDefaultDuration(24 * time.Hour)
|
|
type args struct {
|
|
so Options
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
v profileDefaultDuration
|
|
args args
|
|
want *x509.Certificate
|
|
}{
|
|
{"default", v, args{Options{}}, &x509.Certificate{NotBefore: tm, NotAfter: tm.Add(24 * time.Hour)}},
|
|
{"backdate", v, args{Options{Backdate: 1 * time.Minute}}, &x509.Certificate{NotBefore: tm.Add(-1 * time.Minute), NotAfter: tm.Add(24 * time.Hour)}},
|
|
{"notBefore", v, args{Options{NotBefore: NewTimeDuration(tm.Add(10 * time.Second))}}, &x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(24*time.Hour + 10*time.Second)}},
|
|
{"notAfter", v, args{Options{NotAfter: NewTimeDuration(tm.Add(1 * time.Hour))}}, &x509.Certificate{NotBefore: tm, NotAfter: tm.Add(1 * time.Hour)}},
|
|
{"notBefore and notAfter", v, args{Options{NotBefore: NewTimeDuration(tm.Add(10 * time.Second)), NotAfter: NewTimeDuration(tm.Add(1 * time.Hour))}},
|
|
&x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(1 * time.Hour)}},
|
|
{"notBefore and backdate", v, args{Options{Backdate: 1 * time.Minute, NotBefore: NewTimeDuration(tm.Add(10 * time.Second))}},
|
|
&x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(24*time.Hour + 10*time.Second)}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cert := &x509.Certificate{}
|
|
profile := &x509util.Leaf{}
|
|
profile.SetSubject(cert)
|
|
|
|
fn := tt.v.Option(tt.args.so)
|
|
if err := fn(profile); err != nil {
|
|
t.Errorf("profileDefaultDuration.Option() error = %v", err)
|
|
}
|
|
if !reflect.DeepEqual(cert, tt.want) {
|
|
t.Errorf("profileDefaultDuration.Option() = %v, \nwant %v", cert, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|