add authorization and order unit tests
parent
a58466589f
commit
c0a9f24798
@ -0,0 +1,150 @@
|
|||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthorization_UpdateStatus(t *testing.T) {
|
||||||
|
type test struct {
|
||||||
|
az *Authorization
|
||||||
|
err *Error
|
||||||
|
db DB
|
||||||
|
}
|
||||||
|
tests := map[string]func(t *testing.T) test{
|
||||||
|
"ok/already-invalid": func(t *testing.T) test {
|
||||||
|
az := &Authorization{
|
||||||
|
Status: StatusInvalid,
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/already-valid": func(t *testing.T) test {
|
||||||
|
az := &Authorization{
|
||||||
|
Status: StatusInvalid,
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/error-unexpected-status": func(t *testing.T) test {
|
||||||
|
az := &Authorization{
|
||||||
|
Status: "foo",
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
err: NewErrorISE("unrecognized authorization status: %s", az.Status),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/expired": func(t *testing.T) test {
|
||||||
|
now := clock.Now()
|
||||||
|
az := &Authorization{
|
||||||
|
ID: "azID",
|
||||||
|
AccountID: "accID",
|
||||||
|
Status: StatusPending,
|
||||||
|
ExpiresAt: now.Add(-5 * time.Minute),
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
db: &MockDB{
|
||||||
|
MockUpdateAuthorization: func(ctx context.Context, updaz *Authorization) error {
|
||||||
|
assert.Equals(t, updaz.ID, az.ID)
|
||||||
|
assert.Equals(t, updaz.AccountID, az.AccountID)
|
||||||
|
assert.Equals(t, updaz.Status, StatusInvalid)
|
||||||
|
assert.Equals(t, updaz.ExpiresAt, az.ExpiresAt)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/db.UpdateAuthorization-error": func(t *testing.T) test {
|
||||||
|
now := clock.Now()
|
||||||
|
az := &Authorization{
|
||||||
|
ID: "azID",
|
||||||
|
AccountID: "accID",
|
||||||
|
Status: StatusPending,
|
||||||
|
ExpiresAt: now.Add(-5 * time.Minute),
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
db: &MockDB{
|
||||||
|
MockUpdateAuthorization: func(ctx context.Context, updaz *Authorization) error {
|
||||||
|
assert.Equals(t, updaz.ID, az.ID)
|
||||||
|
assert.Equals(t, updaz.AccountID, az.AccountID)
|
||||||
|
assert.Equals(t, updaz.Status, StatusInvalid)
|
||||||
|
assert.Equals(t, updaz.ExpiresAt, az.ExpiresAt)
|
||||||
|
return errors.New("force")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: NewErrorISE("error updating authorization: force"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/no-valid-challenges": func(t *testing.T) test {
|
||||||
|
now := clock.Now()
|
||||||
|
az := &Authorization{
|
||||||
|
ID: "azID",
|
||||||
|
AccountID: "accID",
|
||||||
|
Status: StatusPending,
|
||||||
|
ExpiresAt: now.Add(5 * time.Minute),
|
||||||
|
Challenges: []*Challenge{
|
||||||
|
{Status: StatusPending}, {Status: StatusPending}, {Status: StatusPending},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/valid": func(t *testing.T) test {
|
||||||
|
now := clock.Now()
|
||||||
|
az := &Authorization{
|
||||||
|
ID: "azID",
|
||||||
|
AccountID: "accID",
|
||||||
|
Status: StatusPending,
|
||||||
|
ExpiresAt: now.Add(5 * time.Minute),
|
||||||
|
Challenges: []*Challenge{
|
||||||
|
{Status: StatusPending}, {Status: StatusPending}, {Status: StatusValid},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
az: az,
|
||||||
|
db: &MockDB{
|
||||||
|
MockUpdateAuthorization: func(ctx context.Context, updaz *Authorization) error {
|
||||||
|
assert.Equals(t, updaz.ID, az.ID)
|
||||||
|
assert.Equals(t, updaz.AccountID, az.AccountID)
|
||||||
|
assert.Equals(t, updaz.Status, StatusValid)
|
||||||
|
assert.Equals(t, updaz.ExpiresAt, az.ExpiresAt)
|
||||||
|
assert.Equals(t, updaz.Error, nil)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, run := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
tc := run(t)
|
||||||
|
if err := tc.az.UpdateStatus(context.Background(), tc.db); err != nil {
|
||||||
|
if assert.NotNil(t, tc.err) {
|
||||||
|
switch k := err.(type) {
|
||||||
|
case *Error:
|
||||||
|
assert.Equals(t, k.Type, tc.err.Type)
|
||||||
|
assert.Equals(t, k.Detail, tc.err.Detail)
|
||||||
|
assert.Equals(t, k.Status, tc.err.Status)
|
||||||
|
assert.Equals(t, k.Err.Error(), tc.err.Err.Error())
|
||||||
|
assert.Equals(t, k.Detail, tc.err.Detail)
|
||||||
|
default:
|
||||||
|
assert.FatalError(t, errors.New("unexpected error type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.Nil(t, tc.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,824 +0,0 @@
|
|||||||
package acme
|
|
||||||
|
|
||||||
/*
|
|
||||||
func newAz() (*Authorization, error) {
|
|
||||||
mockdb := &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return []byte("foo"), true, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return newAuthz(mockdb, "1234", Identifier{
|
|
||||||
Type: "dns", Value: "acme.example.com",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAuthz(t *testing.T) {
|
|
||||||
type test struct {
|
|
||||||
id string
|
|
||||||
db nosql.DB
|
|
||||||
az authz
|
|
||||||
err *Error
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/not-found": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
id: az.getID(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
return nil, database.ErrNotFound
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: MalformedErr(errors.Errorf("authz %s not found: not found", az.getID())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/db-error": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
id: az.getID(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
return nil, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.Errorf("error loading authz %s: force", az.getID())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/unmarshal-error": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Identifier.Type = "foo"
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
id: az.getID(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, key, []byte(az.getID()))
|
|
||||||
return b, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("unexpected authz type foo")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
id: az.getID(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, key, []byte(az.getID()))
|
|
||||||
return b, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
tc := run(t)
|
|
||||||
if az, err := getAuthz(tc.db, tc.id); err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if assert.Nil(t, tc.err) {
|
|
||||||
assert.Equals(t, tc.az.getID(), az.getID())
|
|
||||||
assert.Equals(t, tc.az.getAccountID(), az.getAccountID())
|
|
||||||
assert.Equals(t, tc.az.getStatus(), az.getStatus())
|
|
||||||
assert.Equals(t, tc.az.getIdentifier(), az.getIdentifier())
|
|
||||||
assert.Equals(t, tc.az.getCreated(), az.getCreated())
|
|
||||||
assert.Equals(t, tc.az.getExpiry(), az.getExpiry())
|
|
||||||
assert.Equals(t, tc.az.getChallenges(), az.getChallenges())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthzClone(t *testing.T) {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
clone := az.clone()
|
|
||||||
|
|
||||||
assert.Equals(t, clone.getID(), az.getID())
|
|
||||||
assert.Equals(t, clone.getAccountID(), az.getAccountID())
|
|
||||||
assert.Equals(t, clone.getStatus(), az.getStatus())
|
|
||||||
assert.Equals(t, clone.getIdentifier(), az.getIdentifier())
|
|
||||||
assert.Equals(t, clone.getExpiry(), az.getExpiry())
|
|
||||||
assert.Equals(t, clone.getCreated(), az.getCreated())
|
|
||||||
assert.Equals(t, clone.getChallenges(), az.getChallenges())
|
|
||||||
|
|
||||||
clone.Status = StatusValid
|
|
||||||
|
|
||||||
assert.NotEquals(t, clone.getStatus(), az.getStatus())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewAuthz(t *testing.T) {
|
|
||||||
iden := Identifier{
|
|
||||||
Type: "dns", Value: "acme.example.com",
|
|
||||||
}
|
|
||||||
accID := "1234"
|
|
||||||
type test struct {
|
|
||||||
iden Identifier
|
|
||||||
db nosql.DB
|
|
||||||
err *Error
|
|
||||||
resChs *([]string)
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/unexpected-type": func(t *testing.T) test {
|
|
||||||
return test{
|
|
||||||
iden: Identifier{Type: "foo", Value: "acme.example.com"},
|
|
||||||
err: MalformedErr(errors.New("unexpected authz type foo")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/new-http-chall-error": func(t *testing.T) test {
|
|
||||||
return test{
|
|
||||||
iden: iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error creating http challenge: error saving acme challenge: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/new-tls-alpn-chall-error": func(t *testing.T) test {
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
iden: iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 1 {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error creating alpn challenge: error saving acme challenge: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/new-dns-chall-error": func(t *testing.T) test {
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
iden: iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 2 {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error creating dns challenge: error saving acme challenge: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/save-authz-error": func(t *testing.T) test {
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
iden: iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 3 {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error storing authz: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok": func(t *testing.T) test {
|
|
||||||
chs := &([]string{})
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
iden: iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 3 {
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, old, nil)
|
|
||||||
|
|
||||||
az, err := unmarshalAuthz(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
assert.Equals(t, az.getID(), string(key))
|
|
||||||
assert.Equals(t, az.getAccountID(), accID)
|
|
||||||
assert.Equals(t, az.getStatus(), StatusPending)
|
|
||||||
assert.Equals(t, az.getIdentifier(), iden)
|
|
||||||
assert.Equals(t, az.getWildcard(), false)
|
|
||||||
|
|
||||||
*chs = az.getChallenges()
|
|
||||||
|
|
||||||
assert.True(t, az.getCreated().Before(time.Now().UTC().Add(time.Minute)))
|
|
||||||
assert.True(t, az.getCreated().After(time.Now().UTC().Add(-1*time.Minute)))
|
|
||||||
|
|
||||||
expiry := az.getCreated().Add(defaultExpiryDuration)
|
|
||||||
assert.True(t, az.getExpiry().Before(expiry.Add(time.Minute)))
|
|
||||||
assert.True(t, az.getExpiry().After(expiry.Add(-1*time.Minute)))
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
resChs: chs,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/wildcard": func(t *testing.T) test {
|
|
||||||
chs := &([]string{})
|
|
||||||
count := 0
|
|
||||||
_iden := Identifier{Type: "dns", Value: "*.acme.example.com"}
|
|
||||||
return test{
|
|
||||||
iden: _iden,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 1 {
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, old, nil)
|
|
||||||
|
|
||||||
az, err := unmarshalAuthz(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
assert.Equals(t, az.getID(), string(key))
|
|
||||||
assert.Equals(t, az.getAccountID(), accID)
|
|
||||||
assert.Equals(t, az.getStatus(), StatusPending)
|
|
||||||
assert.Equals(t, az.getIdentifier(), iden)
|
|
||||||
assert.Equals(t, az.getWildcard(), true)
|
|
||||||
|
|
||||||
*chs = az.getChallenges()
|
|
||||||
// Verify that we only have 1 challenge instead of 2.
|
|
||||||
assert.True(t, len(*chs) == 1)
|
|
||||||
|
|
||||||
assert.True(t, az.getCreated().Before(time.Now().UTC().Add(time.Minute)))
|
|
||||||
assert.True(t, az.getCreated().After(time.Now().UTC().Add(-1*time.Minute)))
|
|
||||||
|
|
||||||
expiry := az.getCreated().Add(defaultExpiryDuration)
|
|
||||||
assert.True(t, az.getExpiry().Before(expiry.Add(time.Minute)))
|
|
||||||
assert.True(t, az.getExpiry().After(expiry.Add(-1*time.Minute)))
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
resChs: chs,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
tc := run(t)
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
az, err := newAuthz(tc.db, accID, tc.iden)
|
|
||||||
if err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if assert.Nil(t, tc.err) {
|
|
||||||
assert.Equals(t, az.getAccountID(), accID)
|
|
||||||
assert.Equals(t, az.getType(), "dns")
|
|
||||||
assert.Equals(t, az.getStatus(), StatusPending)
|
|
||||||
|
|
||||||
assert.True(t, az.getCreated().Before(time.Now().UTC().Add(time.Minute)))
|
|
||||||
assert.True(t, az.getCreated().After(time.Now().UTC().Add(-1*time.Minute)))
|
|
||||||
|
|
||||||
expiry := az.getCreated().Add(defaultExpiryDuration)
|
|
||||||
assert.True(t, az.getExpiry().Before(expiry.Add(time.Minute)))
|
|
||||||
assert.True(t, az.getExpiry().After(expiry.Add(-1*time.Minute)))
|
|
||||||
|
|
||||||
assert.Equals(t, az.getChallenges(), *(tc.resChs))
|
|
||||||
|
|
||||||
if strings.HasPrefix(tc.iden.Value, "*.") {
|
|
||||||
assert.True(t, az.getWildcard())
|
|
||||||
assert.Equals(t, az.getIdentifier().Value, strings.TrimPrefix(tc.iden.Value, "*."))
|
|
||||||
} else {
|
|
||||||
assert.False(t, az.getWildcard())
|
|
||||||
assert.Equals(t, az.getIdentifier().Value, tc.iden.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.True(t, az.getID() != "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthzToACME(t *testing.T) {
|
|
||||||
dir := newDirectory("ca.smallstep.com", "acme")
|
|
||||||
|
|
||||||
var (
|
|
||||||
ch1, ch2 challenge
|
|
||||||
ch1Bytes, ch2Bytes = &([]byte{}), &([]byte{})
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
mockdb := &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 0 {
|
|
||||||
*ch1Bytes = newval
|
|
||||||
ch1, err = unmarshalChallenge(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
} else if count == 1 {
|
|
||||||
*ch2Bytes = newval
|
|
||||||
ch2, err = unmarshalChallenge(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return []byte("foo"), true, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
iden := Identifier{
|
|
||||||
Type: "dns", Value: "acme.example.com",
|
|
||||||
}
|
|
||||||
az, err := newAuthz(mockdb, "1234", iden)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
prov := newProv()
|
|
||||||
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
|
|
||||||
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
|
|
||||||
|
|
||||||
type test struct {
|
|
||||||
db nosql.DB
|
|
||||||
err *Error
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/getChallenge1-error": func(t *testing.T) test {
|
|
||||||
return test{
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
return nil, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error loading challenge")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/getChallenge2-error": func(t *testing.T) test {
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
if count == 1 {
|
|
||||||
return nil, errors.New("force")
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return *ch1Bytes, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error loading challenge")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok": func(t *testing.T) test {
|
|
||||||
count := 0
|
|
||||||
return test{
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
if count == 0 {
|
|
||||||
count++
|
|
||||||
return *ch1Bytes, nil
|
|
||||||
}
|
|
||||||
return *ch2Bytes, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
tc := run(t)
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
acmeAz, err := az.toACME(ctx, tc.db, dir)
|
|
||||||
if err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if assert.Nil(t, tc.err) {
|
|
||||||
assert.Equals(t, acmeAz.ID, az.getID())
|
|
||||||
assert.Equals(t, acmeAz.Identifier, iden)
|
|
||||||
assert.Equals(t, acmeAz.Status, StatusPending)
|
|
||||||
|
|
||||||
acmeCh1, err := ch1.toACME(ctx, nil, dir)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
acmeCh2, err := ch2.toACME(ctx, nil, dir)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
assert.Equals(t, acmeAz.Challenges[0], acmeCh1)
|
|
||||||
assert.Equals(t, acmeAz.Challenges[1], acmeCh2)
|
|
||||||
|
|
||||||
expiry, err := time.Parse(time.RFC3339, acmeAz.Expires)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
assert.Equals(t, expiry.String(), az.getExpiry().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthzSave(t *testing.T) {
|
|
||||||
type test struct {
|
|
||||||
az, old authz
|
|
||||||
db nosql.DB
|
|
||||||
err *Error
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/old-nil/swap-error": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
old: nil,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error storing authz: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/old-nil/swap-false": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
old: nil,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return []byte("foo"), false, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error storing authz; value has changed since last read")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/old-nil": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
old: nil,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
assert.Equals(t, old, nil)
|
|
||||||
assert.Equals(t, b, newval)
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, []byte(az.getID()), key)
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/old-not-nil": func(t *testing.T) test {
|
|
||||||
oldAz, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
oldb, err := json.Marshal(oldAz)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
old: oldAz,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
assert.Equals(t, old, oldb)
|
|
||||||
assert.Equals(t, b, newval)
|
|
||||||
assert.Equals(t, bucket, authzTable)
|
|
||||||
assert.Equals(t, []byte(az.getID()), key)
|
|
||||||
return []byte("foo"), true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
tc := run(t)
|
|
||||||
if err := tc.az.save(tc.db, tc.old); err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert.Nil(t, tc.err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthzUnmarshal(t *testing.T) {
|
|
||||||
type test struct {
|
|
||||||
az authz
|
|
||||||
azb []byte
|
|
||||||
err *Error
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/nil": func(t *testing.T) test {
|
|
||||||
return test{
|
|
||||||
azb: nil,
|
|
||||||
err: ServerInternalErr(errors.New("error unmarshaling authz type: unexpected end of JSON input")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/unexpected-type": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Identifier.Type = "foo"
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
azb: b,
|
|
||||||
err: ServerInternalErr(errors.New("unexpected authz type foo")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/dns": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
azb: b,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
tc := run(t)
|
|
||||||
if az, err := unmarshalAuthz(tc.azb); err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if assert.Nil(t, tc.err) {
|
|
||||||
assert.Equals(t, tc.az.getID(), az.getID())
|
|
||||||
assert.Equals(t, tc.az.getAccountID(), az.getAccountID())
|
|
||||||
assert.Equals(t, tc.az.getStatus(), az.getStatus())
|
|
||||||
assert.Equals(t, tc.az.getCreated(), az.getCreated())
|
|
||||||
assert.Equals(t, tc.az.getExpiry(), az.getExpiry())
|
|
||||||
assert.Equals(t, tc.az.getWildcard(), az.getWildcard())
|
|
||||||
assert.Equals(t, tc.az.getChallenges(), az.getChallenges())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthzUpdateStatus(t *testing.T) {
|
|
||||||
type test struct {
|
|
||||||
az, res authz
|
|
||||||
err *Error
|
|
||||||
db nosql.DB
|
|
||||||
}
|
|
||||||
tests := map[string]func(t *testing.T) test{
|
|
||||||
"fail/already-invalid": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Status = StatusInvalid
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/already-valid": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Status = StatusValid
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/unexpected-status": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Status = StatusReady
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
err: ServerInternalErr(errors.New("unrecognized authz status: ready")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/save-error": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Expires = time.Now().UTC().Add(-time.Minute)
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error storing authz: force")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/expired": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Expires = time.Now().UTC().Add(-time.Minute)
|
|
||||||
|
|
||||||
clone := az.clone()
|
|
||||||
clone.Error = MalformedErr(errors.New("authz has expired"))
|
|
||||||
clone.Status = StatusInvalid
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: clone.parent(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/get-challenge-error": func(t *testing.T) test {
|
|
||||||
az, err := newAz()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
return nil, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error loading challenge")),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/valid": func(t *testing.T) test {
|
|
||||||
var (
|
|
||||||
ch3 challenge
|
|
||||||
ch2Bytes = &([]byte{})
|
|
||||||
ch1Bytes = &([]byte{})
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
mockdb := &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 0 {
|
|
||||||
*ch1Bytes = newval
|
|
||||||
} else if count == 1 {
|
|
||||||
*ch2Bytes = newval
|
|
||||||
} else if count == 2 {
|
|
||||||
ch3, err = unmarshalChallenge(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
iden := Identifier{
|
|
||||||
Type: "dns", Value: "acme.example.com",
|
|
||||||
}
|
|
||||||
az, err := newAuthz(mockdb, "1234", iden)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_az, ok := az.(*dnsAuthz)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_az.baseAuthz.Error = MalformedErr(nil)
|
|
||||||
|
|
||||||
_ch, ok := ch3.(*dns01Challenge)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_ch.baseChallenge.Status = StatusValid
|
|
||||||
chb, err := json.Marshal(ch3)
|
|
||||||
|
|
||||||
clone := az.clone()
|
|
||||||
clone.Status = StatusValid
|
|
||||||
clone.Error = nil
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: clone.parent(),
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
if count == 0 {
|
|
||||||
count++
|
|
||||||
return *ch1Bytes, nil
|
|
||||||
}
|
|
||||||
if count == 1 {
|
|
||||||
count++
|
|
||||||
return *ch2Bytes, nil
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return chb, nil
|
|
||||||
},
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/still-pending": func(t *testing.T) test {
|
|
||||||
var ch1Bytes, ch2Bytes = &([]byte{}), &([]byte{})
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
mockdb := &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
if count == 0 {
|
|
||||||
*ch1Bytes = newval
|
|
||||||
} else if count == 1 {
|
|
||||||
*ch2Bytes = newval
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
iden := Identifier{
|
|
||||||
Type: "dns", Value: "acme.example.com",
|
|
||||||
}
|
|
||||||
az, err := newAuthz(mockdb, "1234", iden)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
return test{
|
|
||||||
az: az,
|
|
||||||
res: az,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
||||||
if count == 0 {
|
|
||||||
count++
|
|
||||||
return *ch1Bytes, nil
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
return *ch2Bytes, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, run := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
tc := run(t)
|
|
||||||
az, err := tc.az.updateStatus(tc.db)
|
|
||||||
if err != nil {
|
|
||||||
if assert.NotNil(t, tc.err) {
|
|
||||||
ae, ok := err.(*Error)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.HasPrefix(t, ae.Error(), tc.err.Error())
|
|
||||||
assert.Equals(t, ae.StatusCode(), tc.err.StatusCode())
|
|
||||||
assert.Equals(t, ae.Type, tc.err.Type)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if assert.Nil(t, tc.err) {
|
|
||||||
expB, err := json.Marshal(tc.res)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(az)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
assert.Equals(t, expB, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
Loading…
Reference in New Issue