forked from TrueCloudLab/certificates
[acme db interface] more unit tests
This commit is contained in:
parent
20b9785d20
commit
8d2ebcfd49
5 changed files with 316 additions and 121 deletions
|
@ -27,16 +27,6 @@ func (a *Account) ToLog() (interface{}, error) {
|
|||
return string(b), nil
|
||||
}
|
||||
|
||||
// GetID returns the account ID.
|
||||
func (a *Account) GetID() string {
|
||||
return a.ID
|
||||
}
|
||||
|
||||
// GetKey returns the JWK associated with the account.
|
||||
func (a *Account) GetKey() *jose.JSONWebKey {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// IsValid returns true if the Account is valid.
|
||||
func (a *Account) IsValid() bool {
|
||||
return Status(a.Status) == StatusValid
|
||||
|
|
|
@ -153,17 +153,19 @@ func (h *Handler) GetUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
|||
api.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
// If neither the status nor the contacts are being updated then ignore
|
||||
// the updates and return 200. This conforms with the behavior detailed
|
||||
// in the ACME spec (https://tools.ietf.org/html/rfc8555#section-7.3.2).
|
||||
if len(uar.Status) > 0 || len(uar.Contact) > 0 {
|
||||
if len(uar.Status) > 0 {
|
||||
acc.Status = uar.Status
|
||||
} else if len(uar.Contact) > 0 {
|
||||
acc.Contact = uar.Contact
|
||||
if err = h.db.UpdateAccount(ctx, acc); err != nil {
|
||||
}
|
||||
|
||||
if err := h.db.UpdateAccount(ctx, acc); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error updating account"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h.linker.LinkAccount(ctx, acc)
|
||||
|
||||
|
|
|
@ -168,16 +168,22 @@ func TestUpdateAccountRequest_Validate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
||||
oids := []string{
|
||||
"https://ca.smallstep.com/acme/order/foo",
|
||||
"https://ca.smallstep.com/acme/order/bar",
|
||||
oids := []string{"foo", "bar"}
|
||||
oidURLs := []string{
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/order/foo",
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/order/bar",
|
||||
}
|
||||
accID := "account-id"
|
||||
|
||||
// Request with chi context
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("accID", accID)
|
||||
url := fmt.Sprintf("http://ca.smallstep.com/acme/account/%s/orders", accID)
|
||||
|
||||
prov := newProv()
|
||||
provName := url.PathEscape(prov.GetName())
|
||||
baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
|
||||
|
||||
url := fmt.Sprintf("http://ca.smallstep.com/acme/%s/account/%s/orders", provName, accID)
|
||||
|
||||
type test struct {
|
||||
db acme.DB
|
||||
|
@ -189,15 +195,15 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
|||
"fail/no-account": func(t *testing.T) test {
|
||||
return test{
|
||||
db: &acme.MockDB{},
|
||||
ctx: context.Background(),
|
||||
statusCode: 400,
|
||||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/nil-account": func(t *testing.T) test {
|
||||
ctx := context.WithValue(context.Background(), accContextKey, nil)
|
||||
return test{
|
||||
db: &acme.MockDB{},
|
||||
ctx: ctx,
|
||||
ctx: context.WithValue(context.Background(), accContextKey, nil),
|
||||
statusCode: 400,
|
||||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
}
|
||||
|
@ -213,7 +219,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
|||
err: acme.NewError(acme.ErrorUnauthorizedType, "account ID does not match url param"),
|
||||
}
|
||||
},
|
||||
"fail/getOrdersByAccount-error": func(t *testing.T) test {
|
||||
"fail/db.GetOrdersByAccountID-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: accID}
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
|
@ -230,6 +236,8 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
|||
acc := &acme.Account{ID: accID}
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
ctx = context.WithValue(ctx, baseURLContextKey, baseURL)
|
||||
ctx = context.WithValue(ctx, provisionerContextKey, prov)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrdersByAccountID: func(ctx context.Context, id string) ([]string, error) {
|
||||
|
@ -245,7 +253,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
|||
for name, run := range tests {
|
||||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &Handler{db: tc.db}
|
||||
h := &Handler{db: tc.db, linker: NewLinker("dns", "acme")}
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
req = req.WithContext(tc.ctx)
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -268,7 +276,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
|
|||
assert.Equals(t, ae.Subproblems, tc.err.Subproblems)
|
||||
assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"})
|
||||
} else {
|
||||
expB, err := json.Marshal(oids)
|
||||
expB, err := json.Marshal(oidURLs)
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, bytes.TrimSpace(body), expB)
|
||||
assert.Equals(t, res.Header["Content-Type"], []string{"application/json"})
|
||||
|
@ -558,7 +566,7 @@ func TestHandler_GetUpdateAccount(t *testing.T) {
|
|||
err: acme.NewError(acme.ErrorMalformedType, "contact cannot be empty string"),
|
||||
}
|
||||
},
|
||||
"fail/update-error": func(t *testing.T) test {
|
||||
"fail/db.UpdateAccount-error": func(t *testing.T) test {
|
||||
uar := &UpdateAccountRequest{
|
||||
Status: "deactivated",
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"go.step.sm/crypto/pemutil"
|
||||
)
|
||||
|
||||
func TestNewOrderRequestValidate(t *testing.T) {
|
||||
func TestNewOrderRequest_Validate(t *testing.T) {
|
||||
type test struct {
|
||||
nor *NewOrderRequest
|
||||
nbf, naf time.Time
|
||||
|
@ -148,12 +148,12 @@ func TestFinalizeRequestValidate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHandler_GetOrder(t *testing.T) {
|
||||
expiry := time.Now().UTC().Add(6 * time.Hour)
|
||||
nbf := time.Now().UTC()
|
||||
naf := time.Now().UTC().Add(24 * time.Hour)
|
||||
now := clock.Now()
|
||||
nbf := now
|
||||
naf := now.Add(24 * time.Hour)
|
||||
expiry := now.Add(-time.Hour)
|
||||
o := acme.Order{
|
||||
ID: "orderID",
|
||||
Expires: expiry,
|
||||
NotBefore: nbf,
|
||||
NotAfter: naf,
|
||||
Identifiers: []acme.Identifier{
|
||||
|
@ -166,8 +166,15 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
Value: "*.smallstep.com",
|
||||
},
|
||||
},
|
||||
Status: "pending",
|
||||
AuthorizationURLs: []string{"foo", "bar"},
|
||||
Expires: expiry,
|
||||
Status: acme.StatusInvalid,
|
||||
Error: acme.NewError(acme.ErrorMalformedType, "order has expired"),
|
||||
AuthorizationURLs: []string{
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/foo",
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/bar",
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/baz",
|
||||
},
|
||||
FinalizeURL: "https://test.ca.smallstep.com/acme/test@acme-provisioner.com/order/orderID/finalize",
|
||||
}
|
||||
|
||||
// Request with chi context
|
||||
|
@ -181,7 +188,6 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
|
||||
type test struct {
|
||||
db acme.DB
|
||||
linker Linker
|
||||
ctx context.Context
|
||||
statusCode int
|
||||
err *acme.Error
|
||||
|
@ -203,8 +209,27 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/getOrder-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
"fail/no-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("provisioner does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/nil-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, nil)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("provisioner does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/db.GetOrder-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
|
@ -217,8 +242,64 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
err: acme.NewErrorISE("force"),
|
||||
}
|
||||
},
|
||||
"fail/account-id-mismatch": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{AccountID: "foo"}, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 401,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType, "account id mismatch"),
|
||||
}
|
||||
},
|
||||
"fail/provisioner-id-mismatch": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{AccountID: "accountID", ProvisionerID: "bar"}, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 401,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType, "provisioner id mismatch"),
|
||||
}
|
||||
},
|
||||
"fail/order-update-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{
|
||||
AccountID: "accountID",
|
||||
ProvisionerID: "acme/test@acme-provisioner.com",
|
||||
Expires: clock.Now().Add(-time.Hour),
|
||||
Status: acme.StatusReady,
|
||||
}, nil
|
||||
},
|
||||
MockUpdateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
return acme.NewErrorISE("force")
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("force"),
|
||||
}
|
||||
},
|
||||
"ok": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
|
@ -226,11 +307,31 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
assert.Equals(t, id, o.ID)
|
||||
return &o, nil
|
||||
return &acme.Order{
|
||||
ID: "orderID",
|
||||
AccountID: "accountID",
|
||||
ProvisionerID: "acme/test@acme-provisioner.com",
|
||||
Expires: expiry,
|
||||
Status: acme.StatusReady,
|
||||
AuthorizationIDs: []string{"foo", "bar", "baz"},
|
||||
NotBefore: nbf,
|
||||
NotAfter: naf,
|
||||
Identifiers: []acme.Identifier{
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "example.com",
|
||||
},
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "*.smallstep.com",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
MockUpdateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
linker: NewLinker("dns", "acme"),
|
||||
ctx: ctx,
|
||||
statusCode: 200,
|
||||
}
|
||||
|
@ -239,7 +340,7 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
for name, run := range tests {
|
||||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &Handler{linker: tc.linker, db: tc.db}
|
||||
h := &Handler{linker: NewLinker("dns", "acme"), db: tc.db}
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
req = req.WithContext(tc.ctx)
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -264,6 +365,7 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
} else {
|
||||
expB, err := json.Marshal(o)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
assert.Equals(t, bytes.TrimSpace(body), expB)
|
||||
assert.Equals(t, res.Header["Location"], []string{url})
|
||||
assert.Equals(t, res.Header["Content-Type"], []string{"application/json"})
|
||||
|
@ -272,7 +374,7 @@ func TestHandler_GetOrder(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandlerNewOrder(t *testing.T) {
|
||||
func TestHandler_NewOrder(t *testing.T) {
|
||||
expiry := time.Now().UTC().Add(6 * time.Hour)
|
||||
nbf := time.Now().UTC().Add(5 * time.Hour)
|
||||
naf := nbf.Add(17 * time.Hour)
|
||||
|
@ -297,7 +399,6 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
|
||||
type test struct {
|
||||
db acme.DB
|
||||
linker Linker
|
||||
ctx context.Context
|
||||
statusCode int
|
||||
err *acme.Error
|
||||
|
@ -319,14 +420,23 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/no-payload": func(t *testing.T) test {
|
||||
"fail/no-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("provisioner expected in request context"),
|
||||
}
|
||||
},
|
||||
"fail/nil-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, nil)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("payload expected in request context"),
|
||||
err: acme.NewErrorISE("provisioner expected in request context"),
|
||||
}
|
||||
},
|
||||
"fail/nil-payload": func(t *testing.T) test {
|
||||
|
@ -408,10 +518,10 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockCreateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
o.ID = "orderID"
|
||||
return nil
|
||||
},
|
||||
},
|
||||
linker: NewLinker("dns", "acme"),
|
||||
ctx: ctx,
|
||||
statusCode: 201,
|
||||
}
|
||||
|
@ -436,7 +546,6 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
return nil
|
||||
},
|
||||
},
|
||||
linker: NewLinker("dns", "acme"),
|
||||
ctx: ctx,
|
||||
statusCode: 201,
|
||||
}
|
||||
|
@ -445,7 +554,7 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
for name, run := range tests {
|
||||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &Handler{linker: tc.linker, db: tc.db}
|
||||
h := &Handler{linker: NewLinker("dns", "prefix"), db: tc.db}
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
req = req.WithContext(tc.ctx)
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -481,26 +590,33 @@ func TestHandlerNewOrder(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHandler_FinalizeOrder(t *testing.T) {
|
||||
expiry := time.Now().UTC().Add(6 * time.Hour)
|
||||
nbf := time.Now().UTC().Add(5 * time.Hour)
|
||||
naf := nbf.Add(17 * time.Hour)
|
||||
now := clock.Now()
|
||||
nbf := now
|
||||
naf := now.Add(24 * time.Hour)
|
||||
o := acme.Order{
|
||||
ID: "orderID",
|
||||
Expires: expiry,
|
||||
NotBefore: nbf,
|
||||
NotAfter: naf,
|
||||
Identifiers: []acme.Identifier{
|
||||
{Type: "dns", Value: "example.com"},
|
||||
{Type: "dns", Value: "bar.com"},
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "example.com",
|
||||
},
|
||||
Status: "valid",
|
||||
AuthorizationURLs: []string{"foo", "bar"},
|
||||
CertificateURL: "https://ca.smallstep.com/acme/certificate/certID",
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "*.smallstep.com",
|
||||
},
|
||||
},
|
||||
Expires: naf,
|
||||
Status: acme.StatusValid,
|
||||
AuthorizationURLs: []string{
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/foo",
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/bar",
|
||||
"https://test.ca.smallstep.com/acme/test@acme-provisioner.com/authz/baz",
|
||||
},
|
||||
FinalizeURL: "https://test.ca.smallstep.com/acme/test@acme-provisioner.com/order/orderID/finalize",
|
||||
CertificateURL: "https://test.ca.smallstep.com/acme/test@acme-provisioner.com/certificate/certID",
|
||||
}
|
||||
_csr, err := pemutil.Read("../../authority/testdata/certs/foo.csr")
|
||||
assert.FatalError(t, err)
|
||||
csr, ok := _csr.(*x509.CertificateRequest)
|
||||
assert.Fatal(t, ok)
|
||||
|
||||
// Request with chi context
|
||||
chiCtx := chi.NewRouteContext()
|
||||
|
@ -508,12 +624,22 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
prov := newProv()
|
||||
provName := url.PathEscape(prov.GetName())
|
||||
baseURL := &url.URL{Scheme: "https", Host: "test.ca.smallstep.com"}
|
||||
url := fmt.Sprintf("%s/acme/%s/order/%s/finalize",
|
||||
url := fmt.Sprintf("%s/acme/%s/order/%s",
|
||||
baseURL.String(), provName, o.ID)
|
||||
|
||||
_csr, err := pemutil.Read("../../authority/testdata/certs/foo.csr")
|
||||
assert.FatalError(t, err)
|
||||
csr, ok := _csr.(*x509.CertificateRequest)
|
||||
assert.Fatal(t, ok)
|
||||
|
||||
nor := &FinalizeRequest{
|
||||
CSR: base64.RawURLEncoding.EncodeToString(csr.Raw),
|
||||
}
|
||||
payloadBytes, err := json.Marshal(nor)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
type test struct {
|
||||
db acme.DB
|
||||
linker Linker
|
||||
ctx context.Context
|
||||
statusCode int
|
||||
err *acme.Error
|
||||
|
@ -521,7 +647,6 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
var tests = map[string]func(t *testing.T) test{
|
||||
"fail/no-account": func(t *testing.T) test {
|
||||
return test{
|
||||
db: &acme.MockDB{},
|
||||
ctx: context.WithValue(context.Background(), provisionerContextKey, prov),
|
||||
statusCode: 400,
|
||||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
|
@ -531,31 +656,49 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, nil)
|
||||
return test{
|
||||
db: &acme.MockDB{},
|
||||
ctx: ctx,
|
||||
statusCode: 400,
|
||||
err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/no-payload": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
"fail/no-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("provisioner does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/nil-provisioner": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, nil)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("payload expected in request context"),
|
||||
err: acme.NewErrorISE("provisioner does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/no-payload": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, provisionerContextKey, prov)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("payload does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/nil-payload": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, nil)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("payload expected in request context"),
|
||||
err: acme.NewErrorISE("paylod does not exist"),
|
||||
}
|
||||
},
|
||||
"fail/unmarshal-payload-error": func(t *testing.T) test {
|
||||
|
@ -583,62 +726,112 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
err: acme.NewError(acme.ErrorMalformedType, "unable to parse csr: asn1: syntax error: sequence truncated"),
|
||||
}
|
||||
},
|
||||
"fail/FinalizeOrder-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
nor := &FinalizeRequest{
|
||||
CSR: base64.RawURLEncoding.EncodeToString(csr.Raw),
|
||||
}
|
||||
b, err := json.Marshal(nor)
|
||||
assert.FatalError(t, err)
|
||||
"fail/db.GetOrder-error": func(t *testing.T) test {
|
||||
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes})
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockUpdateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
/*
|
||||
p, err := acme.ProvisionerFromContext(ctx)
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, p, prov)
|
||||
assert.Equals(t, accID, acc.ID)
|
||||
assert.Equals(t, id, o.ID)
|
||||
assert.Equals(t, incsr.Raw, csr.Raw)
|
||||
*/
|
||||
return acme.NewError(acme.ErrorMalformedType, "force")
|
||||
MockError: acme.NewErrorISE("force"),
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("force"),
|
||||
}
|
||||
},
|
||||
"fail/account-id-mismatch": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes})
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{AccountID: "foo"}, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 400,
|
||||
err: acme.NewError(acme.ErrorMalformedType, "force"),
|
||||
statusCode: 401,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType, "account id mismatch"),
|
||||
}
|
||||
},
|
||||
"fail/provisioner-id-mismatch": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes})
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{AccountID: "accountID", ProvisionerID: "bar"}, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 401,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType, "provisioner id mismatch"),
|
||||
}
|
||||
},
|
||||
"fail/order-finalize-error": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes})
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
db: &acme.MockDB{
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{
|
||||
AccountID: "accountID",
|
||||
ProvisionerID: "acme/test@acme-provisioner.com",
|
||||
Expires: clock.Now().Add(-time.Hour),
|
||||
Status: acme.StatusReady,
|
||||
}, nil
|
||||
},
|
||||
MockUpdateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
return acme.NewErrorISE("force")
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: acme.NewErrorISE("force"),
|
||||
}
|
||||
},
|
||||
"ok": func(t *testing.T) test {
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
nor := &FinalizeRequest{
|
||||
CSR: base64.RawURLEncoding.EncodeToString(csr.Raw),
|
||||
}
|
||||
b, err := json.Marshal(nor)
|
||||
assert.FatalError(t, err)
|
||||
acc := &acme.Account{ID: "accountID"}
|
||||
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes})
|
||||
ctx = context.WithValue(ctx, baseURLContextKey, baseURL)
|
||||
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
linker: NewLinker("dns", "acme"),
|
||||
db: &acme.MockDB{
|
||||
MockUpdateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
/*
|
||||
p, err := acme.ProvisionerFromContext(ctx)
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, p, prov)
|
||||
assert.Equals(t, accID, acc.ID)
|
||||
assert.Equals(t, id, o.ID)
|
||||
assert.Equals(t, incsr.Raw, csr.Raw)
|
||||
return &o, nil
|
||||
*/
|
||||
return nil
|
||||
MockGetOrder: func(ctx context.Context, id string) (*acme.Order, error) {
|
||||
return &acme.Order{
|
||||
ID: "orderID",
|
||||
AccountID: "accountID",
|
||||
ProvisionerID: "acme/test@acme-provisioner.com",
|
||||
Expires: naf,
|
||||
Status: acme.StatusValid,
|
||||
AuthorizationIDs: []string{"foo", "bar", "baz"},
|
||||
NotBefore: nbf,
|
||||
NotAfter: naf,
|
||||
Identifiers: []acme.Identifier{
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "example.com",
|
||||
},
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "*.smallstep.com",
|
||||
},
|
||||
},
|
||||
CertificateID: "certID",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
|
@ -649,7 +842,7 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
for name, run := range tests {
|
||||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &Handler{linker: tc.linker, db: tc.db}
|
||||
h := &Handler{linker: NewLinker("dns", "acme"), db: tc.db}
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
req = req.WithContext(tc.ctx)
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -674,10 +867,12 @@ func TestHandler_FinalizeOrder(t *testing.T) {
|
|||
} else {
|
||||
expB, err := json.Marshal(o)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
ro := new(acme.Order)
|
||||
err = json.Unmarshal(body, ro)
|
||||
|
||||
assert.Equals(t, bytes.TrimSpace(body), expB)
|
||||
assert.Equals(t, res.Header["Location"],
|
||||
[]string{fmt.Sprintf("%s/acme/%s/order/%s",
|
||||
baseURL, provName, o.ID)})
|
||||
assert.Equals(t, res.Header["Location"], []string{url})
|
||||
assert.Equals(t, res.Header["Content-Type"], []string{"application/json"})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -20,6 +20,7 @@ type Identifier struct {
|
|||
|
||||
// Order contains order metadata for the ACME protocol order type.
|
||||
type Order struct {
|
||||
ID string `json:"id"`
|
||||
Status Status `json:"status"`
|
||||
Expires time.Time `json:"expires,omitempty"`
|
||||
Identifiers []Identifier `json:"identifiers"`
|
||||
|
@ -31,7 +32,6 @@ type Order struct {
|
|||
FinalizeURL string `json:"finalize"`
|
||||
CertificateID string `json:"-"`
|
||||
CertificateURL string `json:"certificate,omitempty"`
|
||||
ID string `json:"-"`
|
||||
AccountID string `json:"-"`
|
||||
ProvisionerID string `json:"-"`
|
||||
DefaultDuration time.Duration `json:"-"`
|
||||
|
@ -50,7 +50,7 @@ func (o *Order) ToLog() (interface{}, error) {
|
|||
// UpdateStatus updates the ACME Order Status if necessary.
|
||||
// Changes to the order are saved using the database interface.
|
||||
func (o *Order) UpdateStatus(ctx context.Context, db DB) error {
|
||||
now := time.Now().UTC()
|
||||
now := clock.Now()
|
||||
|
||||
switch o.Status {
|
||||
case StatusInvalid:
|
||||
|
|
Loading…
Reference in a new issue