certificates/acme/authority_test.go
max furman 704a510a2a Remove non-pending orders from the acme_orders_by_account index ...
- Each acme account has an index in this table. Before this change, the
index would grow unchecked as orders accumulate. This change removes
orders that have moved out of the 'PENDING' state.
2020-06-01 12:56:50 -07:00

1667 lines
46 KiB
Go

package acme
import (
"context"
"encoding/json"
"fmt"
"net/url"
"testing"
"time"
"github.com/pkg/errors"
"github.com/smallstep/assert"
"github.com/smallstep/certificates/db"
"github.com/smallstep/cli/jose"
"github.com/smallstep/nosql/database"
)
func TestAuthorityGetLink(t *testing.T) {
auth, err := NewAuthority(new(db.MockNoSQLDB), "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
prov := newProv()
provName := url.PathEscape(prov.GetName())
baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, baseURL)
type test struct {
auth *Authority
typ Link
abs bool
inputs []string
res string
}
tests := map[string]func(t *testing.T) test{
"ok/new-account/abs": func(t *testing.T) test {
return test{
auth: auth,
typ: NewAccountLink,
abs: true,
res: fmt.Sprintf("%s/acme/%s/new-account", baseURL.String(), provName),
}
},
"ok/new-account/no-abs": func(t *testing.T) test {
return test{
auth: auth,
typ: NewAccountLink,
abs: false,
res: fmt.Sprintf("/%s/new-account", provName),
}
},
"ok/order/abs": func(t *testing.T) test {
return test{
auth: auth,
typ: OrderLink,
abs: true,
inputs: []string{"foo"},
res: fmt.Sprintf("%s/acme/%s/order/foo", baseURL.String(), provName),
}
},
"ok/order/no-abs": func(t *testing.T) test {
return test{
auth: auth,
typ: OrderLink,
abs: false,
inputs: []string{"foo"},
res: fmt.Sprintf("/%s/order/foo", provName),
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
link := tc.auth.GetLink(ctx, tc.typ, tc.abs, tc.inputs...)
assert.Equals(t, tc.res, link)
})
}
}
func TestAuthorityGetDirectory(t *testing.T) {
auth, err := NewAuthority(new(db.MockNoSQLDB), "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
prov := newProv()
baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, baseURL)
type test struct {
ctx context.Context
err *Error
}
tests := map[string]func(t *testing.T) test{
"ok/empty-provisioner": func(t *testing.T) test {
return test{
ctx: context.Background(),
}
},
"ok/no-baseURL": func(t *testing.T) test {
return test{
ctx: context.WithValue(context.Background(), ProvisionerContextKey, prov),
}
},
"ok/baseURL": func(t *testing.T) test {
return test{
ctx: ctx,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if dir, err := auth.GetDirectory(tc.ctx); 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) {
bu := BaseURLFromContext(tc.ctx)
if bu == nil {
bu = &url.URL{Scheme: "https", Host: "ca.smallstep.com"}
}
var provName string
prov, err := ProvisionerFromContext(tc.ctx)
if err != nil {
provName = ""
} else {
provName = url.PathEscape(prov.GetName())
}
assert.Equals(t, dir.NewNonce, fmt.Sprintf("%s/acme/%s/new-nonce", bu.String(), provName))
assert.Equals(t, dir.NewAccount, fmt.Sprintf("%s/acme/%s/new-account", bu.String(), provName))
assert.Equals(t, dir.NewOrder, fmt.Sprintf("%s/acme/%s/new-order", bu.String(), provName))
assert.Equals(t, dir.RevokeCert, fmt.Sprintf("%s/acme/%s/revoke-cert", bu.String(), provName))
assert.Equals(t, dir.KeyChange, fmt.Sprintf("%s/acme/%s/key-change", bu.String(), provName))
}
}
})
}
}
func TestAuthorityNewNonce(t *testing.T) {
type test struct {
auth *Authority
res *string
err *Error
}
tests := map[string]func(t *testing.T) test{
"fail/newNonce-error": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
res: nil,
err: ServerInternalErr(errors.New("error storing nonce: force")),
}
},
"ok": func(t *testing.T) test {
var _res string
res := &_res
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
*res = string(key)
return nil, true, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
res: res,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if nonce, err := tc.auth.NewNonce(); 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, nonce, *tc.res)
}
}
})
}
}
func TestAuthorityUseNonce(t *testing.T) {
type test struct {
auth *Authority
err *Error
}
tests := map[string]func(t *testing.T) test{
"fail/newNonce-error": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{
MUpdate: func(tx *database.Tx) error {
return errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
err: ServerInternalErr(errors.New("error deleting nonce foo: force")),
}
},
"ok": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{
MUpdate: func(tx *database.Tx) error {
return nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if err := tc.auth.UseNonce("foo"); 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 TestAuthorityNewAccount(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
ops := AccountOptions{
Key: jwk, Contact: []string{"foo", "bar"},
}
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
ops AccountOptions
err *Error
acc **Account
}
tests := map[string]func(t *testing.T) test{
"fail/newAccount-error": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
ops: ops,
err: ServerInternalErr(errors.New("error setting key-id to account-id index: force")),
}
},
"ok": func(t *testing.T) test {
var (
_acmeacc = &Account{}
acmeacc = &_acmeacc
count = 0
dir = newDirectory("ca.smallstep.com", "acme")
)
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
if count == 1 {
var acc *account
assert.FatalError(t, json.Unmarshal(newval, &acc))
*acmeacc, err = acc.toACME(ctx, nil, dir)
return nil, true, nil
}
count++
return nil, true, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
ops: ops,
acc: acmeacc,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAcc, err := tc.auth.NewAccount(ctx, tc.ops); 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) {
gotb, err := json.Marshal(acmeAcc)
assert.FatalError(t, err)
expb, err := json.Marshal(*tc.acc)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetAccount(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id string
err *Error
acc *account
}
tests := map[string]func(t *testing.T) test{
"fail/getAccount-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.Errorf("error loading account %s: force", id)),
}
},
"ok": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: acc.ID,
acc: acc,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAcc, err := tc.auth.GetAccount(ctx, 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) {
gotb, err := json.Marshal(acmeAcc)
assert.FatalError(t, err)
acmeExp, err := tc.acc.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetAccountByKey(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
jwk *jose.JSONWebKey
err *Error
acc *account
}
tests := map[string]func(t *testing.T) test{
"fail/generate-thumbprint-error": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
jwk.Key = "foo"
auth, err := NewAuthority(new(db.MockNoSQLDB), "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
jwk: jwk,
err: ServerInternalErr(errors.New("error generating jwk thumbprint: square/go-jose: unknown key type 'string'")),
}
},
"fail/getAccount-error": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
kid, err := keyToID(jwk)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, accountByKeyIDTable)
assert.Equals(t, key, []byte(kid))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
jwk: jwk,
err: ServerInternalErr(errors.New("error loading key-account index: force")),
}
},
"ok": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
count := 0
kid, err := keyToID(acc.Key)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
var ret []byte
switch {
case count == 0:
assert.Equals(t, bucket, accountByKeyIDTable)
assert.Equals(t, key, []byte(kid))
ret = []byte(acc.ID)
case count == 1:
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(acc.ID))
ret = b
}
count++
return ret, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
jwk: acc.Key,
acc: acc,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAcc, err := tc.auth.GetAccountByKey(ctx, tc.jwk); 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) {
gotb, err := json.Marshal(acmeAcc)
assert.FatalError(t, err)
acmeExp, err := tc.acc.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetOrder(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id, accID string
err *Error
o *order
}
tests := map[string]func(t *testing.T) test{
"fail/getOrder-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.New("error loading order foo: force")),
}
},
"fail/order-not-owned-by-account": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
b, err := json.Marshal(o)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: "foo",
err: UnauthorizedErr(errors.New("account does not own order")),
}
},
"fail/updateStatus-error": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
b, err := json.Marshal(o)
assert.FatalError(t, err)
i := 0
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
switch {
case i == 0:
i++
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
default:
assert.Equals(t, bucket, authzTable)
assert.Equals(t, key, []byte(o.Authorizations[0]))
return nil, ServerInternalErr(errors.New("force"))
}
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: o.AccountID,
err: ServerInternalErr(errors.Errorf("error loading authz %s: force", o.Authorizations[0])),
}
},
"ok": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
o.Status = "valid"
b, err := json.Marshal(o)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: o.AccountID,
o: o,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeO, err := tc.auth.GetOrder(ctx, tc.accID, 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) {
gotb, err := json.Marshal(acmeO)
assert.FatalError(t, err)
acmeExp, err := tc.o.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetCertificate(t *testing.T) {
type test struct {
auth *Authority
id, accID string
err *Error
cert *certificate
}
tests := map[string]func(t *testing.T) test{
"fail/getCertificate-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, certTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.New("error loading certificate: force")),
}
},
"fail/certificate-not-owned-by-account": func(t *testing.T) test {
cert, err := newcert()
assert.FatalError(t, err)
b, err := json.Marshal(cert)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, certTable)
assert.Equals(t, key, []byte(cert.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: cert.ID,
accID: "foo",
err: UnauthorizedErr(errors.New("account does not own certificate")),
}
},
"ok": func(t *testing.T) test {
cert, err := newcert()
assert.FatalError(t, err)
b, err := json.Marshal(cert)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, certTable)
assert.Equals(t, key, []byte(cert.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: cert.ID,
accID: cert.AccountID,
cert: cert,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeCert, err := tc.auth.GetCertificate(tc.accID, 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) {
gotb, err := json.Marshal(acmeCert)
assert.FatalError(t, err)
acmeExp, err := tc.cert.toACME(nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetAuthz(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id, accID string
err *Error
acmeAz *Authz
}
tests := map[string]func(t *testing.T) test{
"fail/getAuthz-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, authzTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.Errorf("error loading authz %s: force", id)),
}
},
"fail/authz-not-owned-by-account": func(t *testing.T) test {
az, err := newAz()
assert.FatalError(t, err)
b, err := json.Marshal(az)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, authzTable)
assert.Equals(t, key, []byte(az.getID()))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: az.getID(),
accID: "foo",
err: UnauthorizedErr(errors.New("account does not own authz")),
}
},
"fail/update-status-error": func(t *testing.T) test {
az, err := newAz()
assert.FatalError(t, err)
b, err := json.Marshal(az)
assert.FatalError(t, err)
count := 0
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
var ret []byte
switch count {
case 0:
assert.Equals(t, bucket, authzTable)
assert.Equals(t, key, []byte(az.getID()))
ret = b
case 1:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(az.getChallenges()[0]))
return nil, errors.New("force")
}
count++
return ret, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: az.getID(),
accID: az.getAccountID(),
err: ServerInternalErr(errors.New("error updating authz status: error loading challenge")),
}
},
"ok": func(t *testing.T) test {
var ch1B, ch2B, ch3B = &[]byte{}, &[]byte{}, &[]byte{}
count := 0
mockdb := &db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
switch count {
case 0:
*ch1B = newval
case 1:
*ch2B = newval
case 2:
*ch3B = newval
}
count++
return nil, true, nil
},
}
az, err := newAuthz(mockdb, "1234", Identifier{
Type: "dns", Value: "acme.example.com",
})
assert.FatalError(t, err)
_az, ok := az.(*dnsAuthz)
assert.Fatal(t, ok)
_az.baseAuthz.Status = StatusValid
b, err := json.Marshal(az)
assert.FatalError(t, err)
ch1, err := unmarshalChallenge(*ch1B)
assert.FatalError(t, err)
ch2, err := unmarshalChallenge(*ch2B)
assert.FatalError(t, err)
ch3, err := unmarshalChallenge(*ch3B)
assert.FatalError(t, err)
count = 0
mockdb = &db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
var ret []byte
switch count {
case 0:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch1.getID()))
ret = *ch1B
case 1:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch2.getID()))
ret = *ch2B
case 2:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch3.getID()))
ret = *ch3B
}
count++
return ret, nil
},
}
acmeAz, err := az.toACME(ctx, mockdb, newDirectory("ca.smallstep.com", "acme"))
assert.FatalError(t, err)
count = 0
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
var ret []byte
switch count {
case 0:
assert.Equals(t, bucket, authzTable)
assert.Equals(t, key, []byte(az.getID()))
ret = b
case 1:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch1.getID()))
ret = *ch1B
case 2:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch2.getID()))
ret = *ch2B
case 3:
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch3.getID()))
ret = *ch3B
}
count++
return ret, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: az.getID(),
accID: az.getAccountID(),
acmeAz: acmeAz,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAz, err := tc.auth.GetAuthz(ctx, tc.accID, 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) {
gotb, err := json.Marshal(acmeAz)
assert.FatalError(t, err)
expb, err := json.Marshal(tc.acmeAz)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityNewOrder(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
ops OrderOptions
ctx context.Context
err *Error
o **Order
}
tests := map[string]func(t *testing.T) test{
"fail/no-provisioner": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
ops: defaultOrderOps(),
ctx: context.Background(),
err: ServerInternalErr(errors.New("provisioner expected in request context")),
}
},
"fail/newOrder-error": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
ops: defaultOrderOps(),
ctx: ctx,
err: ServerInternalErr(errors.New("error creating order: error creating http challenge: error saving acme challenge: force")),
}
},
"ok": func(t *testing.T) test {
var (
_acmeO = &Order{}
acmeO = &_acmeO
count = 0
dir = newDirectory("ca.smallstep.com", "acme")
err error
_accID string
accID = &_accID
)
auth, err := NewAuthority(&db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
switch count {
case 0:
assert.Equals(t, bucket, challengeTable)
case 1:
assert.Equals(t, bucket, challengeTable)
case 2:
assert.Equals(t, bucket, challengeTable)
case 3:
assert.Equals(t, bucket, authzTable)
case 4:
assert.Equals(t, bucket, challengeTable)
case 5:
assert.Equals(t, bucket, challengeTable)
case 6:
assert.Equals(t, bucket, challengeTable)
case 7:
assert.Equals(t, bucket, authzTable)
case 8:
assert.Equals(t, bucket, orderTable)
var o order
assert.FatalError(t, json.Unmarshal(newval, &o))
*acmeO, err = o.toACME(ctx, nil, dir)
assert.FatalError(t, err)
*accID = o.AccountID
case 9:
assert.Equals(t, bucket, ordersByAccountIDTable)
assert.Equals(t, string(key), *accID)
}
count++
return nil, true, nil
},
MGet: func(bucket, key []byte) ([]byte, error) {
return nil, database.ErrNotFound
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
ops: defaultOrderOps(),
ctx: ctx,
o: acmeO,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeO, err := tc.auth.NewOrder(tc.ctx, tc.ops); 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) {
gotb, err := json.Marshal(acmeO)
assert.FatalError(t, err)
expb, err := json.Marshal(*tc.o)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityGetOrdersByAccount(t *testing.T) {
prov := newProv()
provName := url.PathEscape(prov.GetName())
baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, baseURL)
type test struct {
auth *Authority
id string
err *Error
res []string
}
tests := map[string]func(t *testing.T) test{
"fail/getOrderIDsByAccount-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, ordersByAccountIDTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.New("error loading orderIDs for account foo: force")),
}
},
"fail/getOrder-error": func(t *testing.T) test {
var (
id = "zap"
oids = []string{"foo", "bar"}
count = 0
err error
)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
var ret []byte
switch count {
case 0:
assert.Equals(t, bucket, ordersByAccountIDTable)
assert.Equals(t, key, []byte(id))
ret, err = json.Marshal(oids)
assert.FatalError(t, err)
case 1:
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(oids[0]))
return nil, errors.New("force")
}
count++
return ret, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.New("error loading order foo for account zap: error loading order foo: force")),
}
},
"ok": func(t *testing.T) test {
accID := "zap"
foo, err := newO()
assert.FatalError(t, err)
bfoo, err := json.Marshal(foo)
assert.FatalError(t, err)
bar, err := newO()
assert.FatalError(t, err)
bar.Status = StatusInvalid
bbar, err := json.Marshal(bar)
assert.FatalError(t, err)
zap, err := newO()
assert.FatalError(t, err)
bzap, err := json.Marshal(zap)
assert.FatalError(t, err)
az, err := newAz()
assert.FatalError(t, err)
baz, err := json.Marshal(az)
assert.FatalError(t, err)
ch, err := newDNSCh()
assert.FatalError(t, err)
bch, err := json.Marshal(ch)
assert.FatalError(t, err)
dbGetOrder := 0
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
switch string(bucket) {
case string(orderTable):
dbGetOrder++
switch dbGetOrder {
case 1:
return bfoo, nil
case 2:
return bbar, nil
case 3:
return bzap, nil
}
case string(ordersByAccountIDTable):
assert.Equals(t, bucket, ordersByAccountIDTable)
assert.Equals(t, key, []byte(accID))
ret, err := json.Marshal([]string{foo.ID, bar.ID, zap.ID})
assert.FatalError(t, err)
return ret, nil
case string(challengeTable):
return bch, nil
case string(authzTable):
return baz, nil
}
return nil, errors.Errorf("should not be query db table %s", bucket)
},
MCmpAndSwap: func(bucket, key, old, newVal []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, ordersByAccountIDTable)
assert.Equals(t, string(key), accID)
return nil, true, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: accID,
res: []string{
fmt.Sprintf("%s/acme/%s/order/%s", baseURL.String(), provName, foo.ID),
fmt.Sprintf("%s/acme/%s/order/%s", baseURL.String(), provName, zap.ID),
},
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if orderLinks, err := tc.auth.GetOrdersByAccount(ctx, 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.res, orderLinks)
}
}
})
}
}
func TestAuthorityFinalizeOrder(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id, accID string
ctx context.Context
err *Error
o *order
}
tests := map[string]func(t *testing.T) test{
"fail/no-provisioner": func(t *testing.T) test {
auth, err := NewAuthority(&db.MockNoSQLDB{}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: "foo",
ctx: context.Background(),
err: ServerInternalErr(errors.New("provisioner expected in request context")),
}
},
"fail/getOrder-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
ctx: ctx,
err: ServerInternalErr(errors.New("error loading order foo: force")),
}
},
"fail/order-not-owned-by-account": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
b, err := json.Marshal(o)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: "foo",
ctx: ctx,
err: UnauthorizedErr(errors.New("account does not own order")),
}
},
"fail/finalize-error": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
o.Expires = time.Now().Add(-time.Minute)
b, err := json.Marshal(o)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: o.AccountID,
ctx: ctx,
err: ServerInternalErr(errors.New("error finalizing order: error storing order: force")),
}
},
"ok": func(t *testing.T) test {
o, err := newO()
assert.FatalError(t, err)
o.Status = StatusValid
o.Certificate = "certID"
b, err := json.Marshal(o)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, orderTable)
assert.Equals(t, key, []byte(o.ID))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: o.ID,
accID: o.AccountID,
ctx: ctx,
o: o,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeO, err := tc.auth.FinalizeOrder(tc.ctx, tc.accID, tc.id, nil); 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) {
gotb, err := json.Marshal(acmeO)
assert.FatalError(t, err)
acmeExp, err := tc.o.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityValidateChallenge(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id, accID string
err *Error
ch challenge
}
tests := map[string]func(t *testing.T) test{
"fail/getChallenge-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.Errorf("error loading challenge %s: force", id)),
}
},
"fail/challenge-not-owned-by-account": func(t *testing.T) test {
ch, err := newHTTPCh()
assert.FatalError(t, err)
b, err := json.Marshal(ch)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch.getID()))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: ch.getID(),
accID: "foo",
err: UnauthorizedErr(errors.New("account does not own challenge")),
}
},
"fail/validate-error": func(t *testing.T) test {
ch, err := newHTTPCh()
assert.FatalError(t, err)
b, err := json.Marshal(ch)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch.getID()))
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch.getID()))
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: ch.getID(),
accID: ch.getAccountID(),
err: ServerInternalErr(errors.New("error attempting challenge validation: error saving acme challenge: force")),
}
},
"ok": func(t *testing.T) test {
ch, err := newHTTPCh()
assert.FatalError(t, err)
_ch, ok := ch.(*http01Challenge)
assert.Fatal(t, ok)
_ch.baseChallenge.Status = StatusValid
_ch.baseChallenge.Validated = clock.Now()
b, err := json.Marshal(ch)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, key, []byte(ch.getID()))
return b, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: ch.getID(),
accID: ch.getAccountID(),
ch: ch,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeCh, err := tc.auth.ValidateChallenge(ctx, tc.accID, tc.id, nil); 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) {
gotb, err := json.Marshal(acmeCh)
assert.FatalError(t, err)
acmeExp, err := tc.ch.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityUpdateAccount(t *testing.T) {
contact := []string{"baz", "zap"}
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id string
contact []string
acc *account
err *Error
}
tests := map[string]func(t *testing.T) test{
"fail/getAccount-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
contact: contact,
err: ServerInternalErr(errors.Errorf("error loading account %s: force", id)),
}
},
"fail/update-error": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: acc.ID,
contact: contact,
err: ServerInternalErr(errors.New("error storing account: force")),
}
},
"ok": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
_acc := *acc
clone := &_acc
clone.Contact = contact
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(acc.ID))
return nil, true, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: acc.ID,
contact: contact,
acc: clone,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAcc, err := tc.auth.UpdateAccount(ctx, tc.id, tc.contact); 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) {
gotb, err := json.Marshal(acmeAcc)
assert.FatalError(t, err)
acmeExp, err := tc.acc.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}
func TestAuthorityDeactivateAccount(t *testing.T) {
prov := newProv()
ctx := context.WithValue(context.Background(), ProvisionerContextKey, prov)
ctx = context.WithValue(ctx, BaseURLContextKey, "https://test.ca.smallstep.com:8080")
type test struct {
auth *Authority
id string
acc *account
err *Error
}
tests := map[string]func(t *testing.T) test{
"fail/getAccount-error": func(t *testing.T) test {
id := "foo"
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(id))
return nil, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: id,
err: ServerInternalErr(errors.Errorf("error loading account %s: force", id)),
}
},
"fail/deactivate-error": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
return nil, false, errors.New("force")
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: acc.ID,
err: ServerInternalErr(errors.New("error storing account: force")),
}
},
"ok": func(t *testing.T) test {
acc, err := newAcc()
assert.FatalError(t, err)
b, err := json.Marshal(acc)
assert.FatalError(t, err)
_acc := *acc
clone := &_acc
clone.Status = StatusDeactivated
clone.Deactivated = clock.Now()
auth, err := NewAuthority(&db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
return b, nil
},
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, accountTable)
assert.Equals(t, key, []byte(acc.ID))
return nil, true, nil
},
}, "ca.smallstep.com", "acme", nil)
assert.FatalError(t, err)
return test{
auth: auth,
id: acc.ID,
acc: clone,
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
tc := run(t)
if acmeAcc, err := tc.auth.DeactivateAccount(ctx, 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) {
gotb, err := json.Marshal(acmeAcc)
assert.FatalError(t, err)
acmeExp, err := tc.acc.toACME(ctx, nil, tc.auth.dir)
assert.FatalError(t, err)
expb, err := json.Marshal(acmeExp)
assert.FatalError(t, err)
assert.Equals(t, expb, gotb)
}
}
})
}
}