|
|
@ -3,9 +3,11 @@ package provisioner
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509"
|
|
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
"errors"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
@ -14,13 +16,19 @@ import (
|
|
|
|
"go.step.sm/crypto/randutil"
|
|
|
|
"go.step.sm/crypto/randutil"
|
|
|
|
"go.step.sm/linkedca"
|
|
|
|
"go.step.sm/linkedca"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/smallstep/assert"
|
|
|
|
|
|
|
|
"github.com/smallstep/certificates/api/render"
|
|
|
|
"github.com/smallstep/certificates/api/render"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func assertHasPrefix(t *testing.T, s, p string) bool {
|
|
|
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
return assert.True(t, strings.HasPrefix(s, p), "%q is not a prefix of %q", p, s)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestX5C_Getters(t *testing.T) {
|
|
|
|
func TestX5C_Getters(t *testing.T) {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
id := "x5c/" + p.Name
|
|
|
|
id := "x5c/" + p.Name
|
|
|
|
if got := p.GetID(); got != id {
|
|
|
|
if got := p.GetID(); got != id {
|
|
|
|
t.Errorf("X5C.GetID() = %v, want %v:%v", got, p.Name, id)
|
|
|
|
t.Errorf("X5C.GetID() = %v, want %v:%v", got, p.Name, id)
|
|
|
@ -79,7 +87,7 @@ func TestX5C_Init(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/invalid-duration": func(t *testing.T) ProvisionerValidateTest {
|
|
|
|
"fail/invalid-duration": func(t *testing.T) ProvisionerValidateTest {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
p.Claims = &Claims{DefaultTLSDur: &Duration{0}}
|
|
|
|
p.Claims = &Claims{DefaultTLSDur: &Duration{0}}
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
@ -88,7 +96,7 @@ func TestX5C_Init(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok": func(t *testing.T) ProvisionerValidateTest {
|
|
|
|
"ok": func(t *testing.T) ProvisionerValidateTest {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -117,7 +125,7 @@ VR0RBA0wC4IJcm9vdC10ZXN0MAoGCCqGSM49BAMCA0kAMEYCIQC2vgqwla0u8LHH
|
|
|
|
1MHob14qvS5o76HautbIBW7fcHzz5gIhAIx5A2+wkJYX4026kqaZCk/1sAwTxSGY
|
|
|
|
1MHob14qvS5o76HautbIBW7fcHzz5gIhAIx5A2+wkJYX4026kqaZCk/1sAwTxSGY
|
|
|
|
M46l92gdOozT
|
|
|
|
M46l92gdOozT
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
return ProvisionerValidateTest{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
extraValid: func(p *X5C) error {
|
|
|
|
extraValid: func(p *X5C) error {
|
|
|
@ -143,11 +151,11 @@ M46l92gdOozT
|
|
|
|
err := tc.p.Init(config)
|
|
|
|
err := tc.p.Init(config)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
assert.Equals(t, tc.err.Error(), err.Error())
|
|
|
|
assert.EqualError(t, tc.err, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
assert.Equals(t, *tc.p.ctl.Audiences, config.Audiences.WithFragment(tc.p.GetID()))
|
|
|
|
assert.Equal(t, *tc.p.ctl.Audiences, config.Audiences.WithFragment(tc.p.GetID()))
|
|
|
|
if tc.extraValid != nil {
|
|
|
|
if tc.extraValid != nil {
|
|
|
|
assert.Nil(t, tc.extraValid(tc.p))
|
|
|
|
assert.Nil(t, tc.extraValid(tc.p))
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -159,9 +167,9 @@ M46l92gdOozT
|
|
|
|
|
|
|
|
|
|
|
|
func TestX5C_authorizeToken(t *testing.T) {
|
|
|
|
func TestX5C_authorizeToken(t *testing.T) {
|
|
|
|
x5cCerts, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
x5cCerts, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
x5cJWK, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
x5cJWK, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
type test struct {
|
|
|
|
type test struct {
|
|
|
|
p *X5C
|
|
|
|
p *X5C
|
|
|
@ -172,7 +180,7 @@ func TestX5C_authorizeToken(t *testing.T) {
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
"fail/bad-token": func(t *testing.T) test {
|
|
|
|
"fail/bad-token": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: "foo",
|
|
|
|
token: "foo",
|
|
|
@ -192,15 +200,15 @@ DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFNLJ
|
|
|
|
P9K7MAoGCCqGSM49BAMCA0gAMEUCIQC5c1ldDcesDb31GlO5cEJvOcRrIrNtkk8m
|
|
|
|
P9K7MAoGCCqGSM49BAMCA0gAMEUCIQC5c1ldDcesDb31GlO5cEJvOcRrIrNtkk8m
|
|
|
|
a5wpg+9s6QIgHIW6L60F8klQX+EO3o0SBqLeNcaskA4oSZsKjEdpSGo=
|
|
|
|
a5wpg+9s6QIgHIW6L60F8klQX+EO3o0SBqLeNcaskA4oSZsKjEdpSGo=
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -231,15 +239,15 @@ BgNVHREECTAHggVsZWFmMjAKBggqhkjOPQQDAgNIADBFAiB7gMRy3t81HpcnoRAS
|
|
|
|
ELZmDFaEnoLCsVfbmanFykazQQIhAI0sZjoE9t6gvzQp7XQp6CoxzCc3Jv3FwZ8G
|
|
|
|
ELZmDFaEnoLCsVfbmanFykazQQIhAI0sZjoE9t6gvzQp7XQp6CoxzCc3Jv3FwZ8G
|
|
|
|
EXAHTA9L
|
|
|
|
EXAHTA9L
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -272,16 +280,16 @@ E4IRaW50ZXJtZWRpYXRlLXRlc3QwCgYIKoZIzj0EAwIDSAAwRQIgII8XpQ8ezDO1
|
|
|
|
2xdq3hShf155C5X/5jO8qr0VyEJgzlkCIQCTqph1Gwu/dmuf6dYLCfQqJyb371LC
|
|
|
|
2xdq3hShf155C5X/5jO8qr0VyEJgzlkCIQCTqph1Gwu/dmuf6dYLCfQqJyb371LC
|
|
|
|
lgsqsR63is+0YQ==
|
|
|
|
lgsqsR63is+0YQ==
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", p.Name, testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -314,15 +322,15 @@ E4IRaW50ZXJtZWRpYXRlLXRlc3QwCgYIKoZIzj0EAwIDSAAwRQIgII8XpQ8ezDO1
|
|
|
|
2xdq3hShf155C5X/5jO8qr0VyEJgzlkCIQCTqph1Gwu/dmuf6dYLCfQqJyb371LC
|
|
|
|
2xdq3hShf155C5X/5jO8qr0VyEJgzlkCIQCTqph1Gwu/dmuf6dYLCfQqJyb371LC
|
|
|
|
lgsqsR63is+0YQ==
|
|
|
|
lgsqsR63is+0YQ==
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
-----END CERTIFICATE-----`))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", "foobar", testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", "foobar", testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -332,11 +340,11 @@ lgsqsR63is+0YQ==
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/invalid-issuer": func(t *testing.T) test {
|
|
|
|
"fail/invalid-issuer": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", "foobar", testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", "foobar", testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -346,11 +354,11 @@ lgsqsR63is+0YQ==
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/invalid-audience": func(t *testing.T) test {
|
|
|
|
"fail/invalid-audience": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", p.GetName(), "foobar", "",
|
|
|
|
tok, err := generateToken("", p.GetName(), "foobar", "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -360,11 +368,11 @@ lgsqsR63is+0YQ==
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/empty-subject": func(t *testing.T) test {
|
|
|
|
"fail/empty-subject": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -374,11 +382,11 @@ lgsqsR63is+0YQ==
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -392,12 +400,12 @@ lgsqsR63is+0YQ==
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
assert.Equals(t, sc.StatusCode(), tc.code)
|
|
|
|
assert.Equal(t, tc.code, sc.StatusCode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
assertHasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
if assert.NoError(t, tc.err) {
|
|
|
|
assert.NotNil(t, claims)
|
|
|
|
assert.NotNil(t, claims)
|
|
|
|
assert.NotNil(t, claims.chains)
|
|
|
|
assert.NotNil(t, claims.chains)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -408,21 +416,22 @@ lgsqsR63is+0YQ==
|
|
|
|
|
|
|
|
|
|
|
|
func TestX5C_AuthorizeSign(t *testing.T) {
|
|
|
|
func TestX5C_AuthorizeSign(t *testing.T) {
|
|
|
|
certs, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
certs, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
jwk, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
type test struct {
|
|
|
|
type test struct {
|
|
|
|
p *X5C
|
|
|
|
p *X5C
|
|
|
|
token string
|
|
|
|
token string
|
|
|
|
code int
|
|
|
|
code int
|
|
|
|
err error
|
|
|
|
err error
|
|
|
|
sans []string
|
|
|
|
sans []string
|
|
|
|
|
|
|
|
fingerprint string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: "foo",
|
|
|
|
token: "foo",
|
|
|
@ -432,11 +441,11 @@ func TestX5C_AuthorizeSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok/empty-sans": func(t *testing.T) test {
|
|
|
|
"ok/empty-sans": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
[]string{}, time.Now(), jwk,
|
|
|
|
[]string{}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -445,65 +454,90 @@ func TestX5C_AuthorizeSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok/multi-sans": func(t *testing.T) test {
|
|
|
|
"ok/multi-sans": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Sign[0], "",
|
|
|
|
[]string{"127.0.0.1", "foo", "max@smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"127.0.0.1", "foo", "max@smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
|
sans: []string{"127.0.0.1", "foo", "max@smallstep.com"},
|
|
|
|
sans: []string{"127.0.0.1", "foo", "max@smallstep.com"},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"ok/cnf": func(t *testing.T) test {
|
|
|
|
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
x5c := make([]string, len(certs))
|
|
|
|
|
|
|
|
for i, cert := range certs {
|
|
|
|
|
|
|
|
x5c[i] = base64.StdEncoding.EncodeToString(cert.Raw)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extraHeaders := map[string]any{"x5c": x5c}
|
|
|
|
|
|
|
|
extraClaims := map[string]any{
|
|
|
|
|
|
|
|
"sans": []string{"127.0.0.1", "foo", "max@smallstep.com"},
|
|
|
|
|
|
|
|
"cnf": map[string]any{"x5rt#S256": "fingerprint"},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tok, err := generateCustomToken("foo", p.GetName(), testAudiences.Sign[0], jwk, extraHeaders, extraClaims)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
return test{
|
|
|
|
|
|
|
|
p: p,
|
|
|
|
|
|
|
|
token: tok,
|
|
|
|
|
|
|
|
sans: []string{"127.0.0.1", "foo", "max@smallstep.com"},
|
|
|
|
|
|
|
|
fingerprint: "fingerprint",
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for name, tt := range tests {
|
|
|
|
for name, tt := range tests {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
tc := tt(t)
|
|
|
|
tc := tt(t)
|
|
|
|
ctx := NewContextWithMethod(context.Background(), SignIdentityMethod)
|
|
|
|
ctx := NewContextWithMethod(context.Background(), SignIdentityMethod)
|
|
|
|
if opts, err := tc.p.AuthorizeSign(ctx, tc.token); err != nil {
|
|
|
|
if opts, err := tc.p.AuthorizeSign(ctx, tc.token); err != nil {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err, err.Error()) {
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
assert.Equals(t, sc.StatusCode(), tc.code)
|
|
|
|
assert.Equal(t, tc.code, sc.StatusCode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
assertHasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, opts) {
|
|
|
|
if assert.NotNil(t, opts) {
|
|
|
|
assert.Equals(t, 10, len(opts))
|
|
|
|
assert.Len(t, opts, 11)
|
|
|
|
for _, o := range opts {
|
|
|
|
for _, o := range opts {
|
|
|
|
switch v := o.(type) {
|
|
|
|
switch v := o.(type) {
|
|
|
|
case *X5C:
|
|
|
|
case *X5C:
|
|
|
|
case certificateOptionsFunc:
|
|
|
|
case certificateOptionsFunc:
|
|
|
|
case *provisionerExtensionOption:
|
|
|
|
case *provisionerExtensionOption:
|
|
|
|
assert.Equals(t, v.Type, TypeX5C)
|
|
|
|
assert.Equal(t, TypeX5C, v.Type)
|
|
|
|
assert.Equals(t, v.Name, tc.p.GetName())
|
|
|
|
assert.Equal(t, tc.p.GetName(), v.Name)
|
|
|
|
assert.Equals(t, v.CredentialID, "")
|
|
|
|
assert.Equal(t, "", v.CredentialID)
|
|
|
|
assert.Len(t, 0, v.KeyValuePairs)
|
|
|
|
assert.Len(t, v.KeyValuePairs, 0)
|
|
|
|
case profileLimitDuration:
|
|
|
|
case profileLimitDuration:
|
|
|
|
assert.Equals(t, v.def, tc.p.ctl.Claimer.DefaultTLSCertDuration())
|
|
|
|
assert.Equal(t, tc.p.ctl.Claimer.DefaultTLSCertDuration(), v.def)
|
|
|
|
claims, err := tc.p.authorizeToken(tc.token, tc.p.ctl.Audiences.Sign)
|
|
|
|
claims, err := tc.p.authorizeToken(tc.token, tc.p.ctl.Audiences.Sign)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equals(t, v.notAfter, claims.chains[0][0].NotAfter)
|
|
|
|
assert.Equal(t, claims.chains[0][0].NotAfter, v.notAfter)
|
|
|
|
case commonNameValidator:
|
|
|
|
case commonNameValidator:
|
|
|
|
assert.Equals(t, string(v), "foo")
|
|
|
|
assert.Equal(t, "foo", string(v))
|
|
|
|
case defaultPublicKeyValidator:
|
|
|
|
case defaultPublicKeyValidator:
|
|
|
|
case *defaultSANsValidator:
|
|
|
|
case *defaultSANsValidator:
|
|
|
|
assert.Equals(t, v.sans, tc.sans)
|
|
|
|
assert.Equal(t, tc.sans, v.sans)
|
|
|
|
assert.Equals(t, MethodFromContext(v.ctx), SignIdentityMethod)
|
|
|
|
assert.Equal(t, SignIdentityMethod, MethodFromContext(v.ctx))
|
|
|
|
case *validityValidator:
|
|
|
|
case *validityValidator:
|
|
|
|
assert.Equals(t, v.min, tc.p.ctl.Claimer.MinTLSCertDuration())
|
|
|
|
assert.Equal(t, tc.p.ctl.Claimer.MinTLSCertDuration(), v.min)
|
|
|
|
assert.Equals(t, v.max, tc.p.ctl.Claimer.MaxTLSCertDuration())
|
|
|
|
assert.Equal(t, tc.p.ctl.Claimer.MaxTLSCertDuration(), v.max)
|
|
|
|
case *x509NamePolicyValidator:
|
|
|
|
case *x509NamePolicyValidator:
|
|
|
|
assert.Equals(t, nil, v.policyEngine)
|
|
|
|
assert.Equal(t, nil, v.policyEngine)
|
|
|
|
case *WebhookController:
|
|
|
|
case *WebhookController:
|
|
|
|
assert.Len(t, 0, v.webhooks)
|
|
|
|
assert.Len(t, v.webhooks, 0)
|
|
|
|
assert.Equals(t, linkedca.Webhook_X509, v.certType)
|
|
|
|
assert.Equal(t, linkedca.Webhook_X509, v.certType)
|
|
|
|
assert.Len(t, 2, v.options)
|
|
|
|
assert.Len(t, v.options, 2)
|
|
|
|
|
|
|
|
case csrFingerprintValidator:
|
|
|
|
|
|
|
|
assert.Equal(t, tc.fingerprint, string(v))
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
|
|
|
require.NoError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -523,7 +557,7 @@ func TestX5C_AuthorizeRevoke(t *testing.T) {
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: "foo",
|
|
|
|
token: "foo",
|
|
|
@ -533,16 +567,16 @@ func TestX5C_AuthorizeRevoke(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
certs, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
certs, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwk, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
jwk, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Revoke[0], "",
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.Revoke[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), jwk,
|
|
|
|
withX5CHdr(certs))
|
|
|
|
withX5CHdr(certs))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -556,9 +590,9 @@ func TestX5C_AuthorizeRevoke(t *testing.T) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
assert.Equals(t, sc.StatusCode(), tc.code)
|
|
|
|
assert.Equal(t, tc.code, sc.StatusCode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
assertHasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
assert.Nil(t, tc.err)
|
|
|
|
assert.Nil(t, tc.err)
|
|
|
@ -577,12 +611,12 @@ func TestX5C_AuthorizeRenew(t *testing.T) {
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
"fail/renew-disabled": func(t *testing.T) test {
|
|
|
|
"fail/renew-disabled": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// disable renewal
|
|
|
|
// disable renewal
|
|
|
|
disable := true
|
|
|
|
disable := true
|
|
|
|
p.Claims = &Claims{DisableRenewal: &disable}
|
|
|
|
p.Claims = &Claims{DisableRenewal: &disable}
|
|
|
|
p.ctl.Claimer, err = NewClaimer(p.Claims, globalProvisionerClaims)
|
|
|
|
p.ctl.Claimer, err = NewClaimer(p.Claims, globalProvisionerClaims)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
code: http.StatusUnauthorized,
|
|
|
|
code: http.StatusUnauthorized,
|
|
|
@ -591,7 +625,7 @@ func TestX5C_AuthorizeRenew(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
"ok": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -607,9 +641,9 @@ func TestX5C_AuthorizeRenew(t *testing.T) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
assert.Equals(t, sc.StatusCode(), tc.code)
|
|
|
|
assert.Equal(t, tc.code, sc.StatusCode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
assertHasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
assert.Nil(t, tc.err)
|
|
|
|
assert.Nil(t, tc.err)
|
|
|
@ -620,28 +654,30 @@ func TestX5C_AuthorizeRenew(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
|
|
func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
x5cCerts, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
x5cCerts, err := pemutil.ReadCertificateBundle("./testdata/certs/x5c-leaf.crt")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
x5cJWK, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
x5cJWK, err := jose.ReadKey("./testdata/secrets/x5c-leaf.key")
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
_, fn := mockNow()
|
|
|
|
_, fn := mockNow()
|
|
|
|
defer fn()
|
|
|
|
defer fn()
|
|
|
|
type test struct {
|
|
|
|
type test struct {
|
|
|
|
p *X5C
|
|
|
|
p *X5C
|
|
|
|
token string
|
|
|
|
token string
|
|
|
|
claims *x5cPayload
|
|
|
|
claims *x5cPayload
|
|
|
|
code int
|
|
|
|
fingerprint string
|
|
|
|
err error
|
|
|
|
count int
|
|
|
|
|
|
|
|
code int
|
|
|
|
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
tests := map[string]func(*testing.T) test{
|
|
|
|
"fail/sshCA-disabled": func(t *testing.T) test {
|
|
|
|
"fail/sshCA-disabled": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// disable sshCA
|
|
|
|
// disable sshCA
|
|
|
|
enable := false
|
|
|
|
enable := false
|
|
|
|
p.Claims = &Claims{EnableSSHCA: &enable}
|
|
|
|
p.Claims = &Claims{EnableSSHCA: &enable}
|
|
|
|
p.ctl.Claimer, err = NewClaimer(p.Claims, globalProvisionerClaims)
|
|
|
|
p.ctl.Claimer, err = NewClaimer(p.Claims, globalProvisionerClaims)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: "foo",
|
|
|
|
token: "foo",
|
|
|
@ -651,7 +687,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
"fail/invalid-token": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: "foo",
|
|
|
|
token: "foo",
|
|
|
@ -661,11 +697,11 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/no-Step-claim": func(t *testing.T) test {
|
|
|
|
"fail/no-Step-claim": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.SSHSign[0], "",
|
|
|
|
tok, err := generateToken("foo", p.GetName(), testAudiences.SSHSign[0], "",
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
[]string{"test.smallstep.com"}, time.Now(), x5cJWK,
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -675,10 +711,10 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"fail/no-SSH-subattribute-in-claims": func(t *testing.T) test {
|
|
|
|
"fail/no-SSH-subattribute-in-claims": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now()
|
|
|
|
now := time.Now()
|
|
|
|
claims := &x5cPayload{
|
|
|
|
claims := &x5cPayload{
|
|
|
|
Claims: jose.Claims{
|
|
|
|
Claims: jose.Claims{
|
|
|
@ -693,7 +729,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
Step: &stepPayload{},
|
|
|
|
Step: &stepPayload{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
@ -703,10 +739,10 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok/with-claims": func(t *testing.T) test {
|
|
|
|
"ok/with-claims": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now()
|
|
|
|
now := time.Now()
|
|
|
|
claims := &x5cPayload{
|
|
|
|
claims := &x5cPayload{
|
|
|
|
Claims: jose.Claims{
|
|
|
|
Claims: jose.Claims{
|
|
|
@ -719,7 +755,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
Audience: []string{testAudiences.SSHSign[0]},
|
|
|
|
Audience: []string{testAudiences.SSHSign[0]},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Step: &stepPayload{SSH: &SignSSHOptions{
|
|
|
|
Step: &stepPayload{SSH: &SignSSHOptions{
|
|
|
|
CertType: SSHHostCert,
|
|
|
|
CertType: SSHUserCert,
|
|
|
|
KeyID: "foo",
|
|
|
|
KeyID: "foo",
|
|
|
|
Principals: []string{"max", "mariano", "alan"},
|
|
|
|
Principals: []string{"max", "mariano", "alan"},
|
|
|
|
ValidAfter: TimeDuration{d: 5 * time.Minute},
|
|
|
|
ValidAfter: TimeDuration{d: 5 * time.Minute},
|
|
|
@ -727,19 +763,20 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
}},
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
claims: claims,
|
|
|
|
claims: claims,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
|
|
|
|
|
count: 12,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ok/without-claims": func(t *testing.T) test {
|
|
|
|
"ok/without-claims": func(t *testing.T) test {
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now()
|
|
|
|
now := time.Now()
|
|
|
|
claims := &x5cPayload{
|
|
|
|
claims := &x5cPayload{
|
|
|
|
Claims: jose.Claims{
|
|
|
|
Claims: jose.Claims{
|
|
|
@ -754,11 +791,47 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
Step: &stepPayload{SSH: &SignSSHOptions{}},
|
|
|
|
Step: &stepPayload{SSH: &SignSSHOptions{}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
assert.FatalError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return test{
|
|
|
|
return test{
|
|
|
|
p: p,
|
|
|
|
p: p,
|
|
|
|
claims: claims,
|
|
|
|
claims: claims,
|
|
|
|
token: tok,
|
|
|
|
token: tok,
|
|
|
|
|
|
|
|
count: 10,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"ok/cnf": func(t *testing.T) test {
|
|
|
|
|
|
|
|
p, err := generateX5C(nil)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
id, err := randutil.ASCII(64)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
claims := &x5cPayload{
|
|
|
|
|
|
|
|
Claims: jose.Claims{
|
|
|
|
|
|
|
|
ID: id,
|
|
|
|
|
|
|
|
Subject: "foo",
|
|
|
|
|
|
|
|
Issuer: p.GetName(),
|
|
|
|
|
|
|
|
IssuedAt: jose.NewNumericDate(now),
|
|
|
|
|
|
|
|
NotBefore: jose.NewNumericDate(now),
|
|
|
|
|
|
|
|
Expiry: jose.NewNumericDate(now.Add(5 * time.Minute)),
|
|
|
|
|
|
|
|
Audience: []string{testAudiences.SSHSign[0]},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Step: &stepPayload{SSH: &SignSSHOptions{
|
|
|
|
|
|
|
|
CertType: SSHHostCert,
|
|
|
|
|
|
|
|
Principals: []string{"host.smallstep.com"},
|
|
|
|
|
|
|
|
}},
|
|
|
|
|
|
|
|
Confirmation: &cnfPayload{
|
|
|
|
|
|
|
|
Fingerprint: "fingerprint",
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
tok, err := generateX5CSSHToken(x5cJWK, claims, withX5CHdr(x5cCerts))
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
return test{
|
|
|
|
|
|
|
|
p: p,
|
|
|
|
|
|
|
|
claims: claims,
|
|
|
|
|
|
|
|
token: tok,
|
|
|
|
|
|
|
|
fingerprint: "fingerprint",
|
|
|
|
|
|
|
|
count: 10,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -769,9 +842,9 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
if assert.NotNil(t, tc.err) {
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
var sc render.StatusCodedError
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
|
|
|
assert.Equals(t, sc.StatusCode(), tc.code)
|
|
|
|
assert.Equal(t, tc.code, sc.StatusCode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
assertHasPrefix(t, err.Error(), tc.err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
|
if assert.Nil(t, tc.err) {
|
|
|
@ -786,38 +859,34 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
|
|
|
tc.claims.Step.SSH.ValidAfter.t = time.Time{}
|
|
|
|
tc.claims.Step.SSH.ValidAfter.t = time.Time{}
|
|
|
|
tc.claims.Step.SSH.ValidBefore.t = time.Time{}
|
|
|
|
tc.claims.Step.SSH.ValidBefore.t = time.Time{}
|
|
|
|
if firstValidator {
|
|
|
|
if firstValidator {
|
|
|
|
assert.Equals(t, SignSSHOptions(v), *tc.claims.Step.SSH)
|
|
|
|
assert.Equal(t, *tc.claims.Step.SSH, SignSSHOptions(v))
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
assert.Equals(t, SignSSHOptions(v), SignSSHOptions{KeyID: tc.claims.Subject})
|
|
|
|
assert.Equal(t, SignSSHOptions{KeyID: tc.claims.Subject}, SignSSHOptions(v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
firstValidator = false
|
|
|
|
firstValidator = false
|
|
|
|
case sshCertValidAfterModifier:
|
|
|
|
case sshCertValidAfterModifier:
|
|
|
|
assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidAfter.RelativeTime(nw).Unix())
|
|
|
|
assert.Equal(t, tc.claims.Step.SSH.ValidAfter.RelativeTime(nw).Unix(), int64(v))
|
|
|
|
case sshCertValidBeforeModifier:
|
|
|
|
case sshCertValidBeforeModifier:
|
|
|
|
assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidBefore.RelativeTime(nw).Unix())
|
|
|
|
assert.Equal(t, tc.claims.Step.SSH.ValidBefore.RelativeTime(nw).Unix(), int64(v))
|
|
|
|
case *sshLimitDuration:
|
|
|
|
case *sshLimitDuration:
|
|
|
|
assert.Equals(t, v.Claimer, tc.p.ctl.Claimer)
|
|
|
|
assert.Equal(t, tc.p.ctl.Claimer, v.Claimer)
|
|
|
|
assert.Equals(t, v.NotAfter, x5cCerts[0].NotAfter)
|
|
|
|
assert.Equal(t, x5cCerts[0].NotAfter, v.NotAfter)
|
|
|
|
case *sshCertValidityValidator:
|
|
|
|
case *sshCertValidityValidator:
|
|
|
|
assert.Equals(t, v.Claimer, tc.p.ctl.Claimer)
|
|
|
|
assert.Equal(t, tc.p.ctl.Claimer, v.Claimer)
|
|
|
|
case *sshNamePolicyValidator:
|
|
|
|
case *sshNamePolicyValidator:
|
|
|
|
assert.Equals(t, nil, v.userPolicyEngine)
|
|
|
|
assert.Nil(t, v.userPolicyEngine)
|
|
|
|
assert.Equals(t, nil, v.hostPolicyEngine)
|
|
|
|
assert.Nil(t, v.hostPolicyEngine)
|
|
|
|
case *sshDefaultPublicKeyValidator, *sshCertDefaultValidator, sshCertificateOptionsFunc:
|
|
|
|
case *sshDefaultPublicKeyValidator, *sshCertDefaultValidator, sshCertificateOptionsFunc:
|
|
|
|
case *WebhookController:
|
|
|
|
case *WebhookController:
|
|
|
|
assert.Len(t, 0, v.webhooks)
|
|
|
|
assert.Len(t, v.webhooks, 0)
|
|
|
|
assert.Equals(t, linkedca.Webhook_SSH, v.certType)
|
|
|
|
assert.Equal(t, linkedca.Webhook_SSH, v.certType)
|
|
|
|
assert.Len(t, 2, v.options)
|
|
|
|
assert.Len(t, v.options, 2)
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
|
|
|
require.NoError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tot++
|
|
|
|
tot++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if tc.claims.Step.SSH.CertType != "" {
|
|
|
|
assert.Equal(t, tc.count, tot)
|
|
|
|
assert.Equals(t, tot, 12)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
assert.Equals(t, tot, 10)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|