Skip to content

Commit 9d77937

Browse files
cpugopherbot
authored andcommitted
acme: include order problem in OrderError
If client.WaitOrder or client.CreateOrderCert return an acme.OrderError it's helpful to include the order's problem field (if available). This will often have detailed information about why a particular order became invalid that's invaluable for debugging (e.g. a challenge response was incorrect, a name couldn't be resolved, etc). While it's possible for a consumer to poll the order themselves as part of handling the order to extract a fresh Order.Error field value, it would take an extra round-trip network request. Since we have the underlying error in-hand when we produce the OrderError we might as well include it directly. Since this field is a structured object with a number of sub-fields the OrderError.Error() function isn't updated to include the order problem error in the String description. Interested callers should instead use errors.Is to extract the problem information directly. Resolves golang/go#74430 Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest Change-Id: I3158f064793bbfdc292dd6b5e1a6bfd7729bd980 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/681037 Auto-Submit: Daniel McCarney <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Ian Stapleton Cordasco <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 8f580de commit 9d77937

File tree

4 files changed

+28
-7
lines changed

4 files changed

+28
-7
lines changed

acme/pebble_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,12 @@ func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
382382
// Wait for the order to become ready for finalization.
383383
order, err = client.WaitOrder(ctx, order.URI)
384384
if err != nil {
385-
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
385+
var orderErr *acme.OrderError
386+
if errors.Is(err, orderErr) {
387+
t.Fatalf("failed to wait for order %s: %s: %s", orderURL, err, orderErr.Problem)
388+
} else {
389+
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
390+
}
386391
}
387392
if order.Status != acme.StatusReady {
388393
t.Fatalf("expected order %s status to be ready, got %v",

acme/rfc8555.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
272272
case err != nil:
273273
// Skip and retry.
274274
case o.Status == StatusInvalid:
275-
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
275+
return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
276276
case o.Status == StatusReady || o.Status == StatusValid:
277277
return o, nil
278278
}
@@ -369,7 +369,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
369369
}
370370
// The only acceptable status post finalize and WaitOrder is "valid".
371371
if o.Status != StatusValid {
372-
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
372+
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
373373
}
374374
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
375375
return crt, o.CertURL, err

acme/rfc8555_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -885,11 +885,17 @@ func TestRFC_WaitOrderError(t *testing.T) {
885885
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
886886
w.Header().Set("Location", s.url("/orders/1"))
887887
w.WriteHeader(http.StatusOK)
888-
s := StatusPending
889888
if count > 0 {
890-
s = StatusInvalid
889+
// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.3
890+
errorData := `{
891+
"type": "urn:ietf:params:acme:error:userActionRequired",
892+
"detail": "Terms of service have changed",
893+
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
894+
}`
895+
fmt.Fprintf(w, `{"status": %q, "error": %s}`, StatusInvalid, errorData)
896+
} else {
897+
fmt.Fprintf(w, `{"status": %q}`, StatusPending)
891898
}
892-
fmt.Fprintf(w, `{"status": %q}`, s)
893899
count++
894900
})
895901
s.start()
@@ -910,6 +916,13 @@ func TestRFC_WaitOrderError(t *testing.T) {
910916
if e.Status != StatusInvalid {
911917
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
912918
}
919+
if e.Problem == nil {
920+
t.Errorf("e.Problem = nil")
921+
}
922+
expectedProbType := "urn:ietf:params:acme:error:userActionRequired"
923+
if e.Problem.ProblemType != expectedProbType {
924+
t.Errorf("e.Problem.ProblemType = %q; want %q", e.Problem.ProblemType, expectedProbType)
925+
}
913926
}
914927

915928
func TestRFC_CreateOrderCert(t *testing.T) {

acme/types.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,16 @@ func (a *AuthorizationError) Error() string {
154154

155155
// OrderError is returned from Client's order related methods.
156156
// It indicates the order is unusable and the clients should start over with
157-
// AuthorizeOrder.
157+
// AuthorizeOrder. A Problem description may be provided with details on
158+
// what caused the order to become unusable.
158159
//
159160
// The clients can still fetch the order object from CA using GetOrder
160161
// to inspect its state.
161162
type OrderError struct {
162163
OrderURL string
163164
Status string
165+
// Problem is the error that occurred while processing the order.
166+
Problem *Error
164167
}
165168

166169
func (oe *OrderError) Error() string {

0 commit comments

Comments
 (0)