Skip to content

Commit 6f7dac9

Browse files
jason-bakergopherbot
authored andcommitted
acme: DeactivateReg fix panic
Currently discover is not called which results in a panic if just a key is added to an ACME client and then deactivation is attempted. This patch adds a discover call as well as missing unit tests for the API. Change-Id: I0719e5376eb2fccf62182e5f91e5b5eaa7bdd518 GitHub-Last-Rev: 501d7c6 GitHub-Pull-Request: #217 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/406734 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Run-TryBot: Roland Shoemaker <roland@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com>
1 parent 85d78b3 commit 6f7dac9

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

acme/rfc8555.go

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import (
2424
//
2525
// It only works with CAs implementing RFC 8555.
2626
func (c *Client) DeactivateReg(ctx context.Context) error {
27+
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
28+
return err
29+
}
2730
url := string(c.accountKID(ctx))
2831
if url == "" {
2932
return ErrNoAccount

acme/rfc8555_test.go

+103
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ import (
1515
"encoding/base64"
1616
"encoding/json"
1717
"encoding/pem"
18+
"errors"
1819
"fmt"
1920
"io/ioutil"
2021
"math/big"
2122
"net/http"
2223
"net/http/httptest"
2324
"reflect"
25+
"strings"
2426
"sync"
2527
"testing"
2628
"time"
@@ -644,6 +646,107 @@ func TestRFC_AccountKeyRollover(t *testing.T) {
644646
}
645647
}
646648

649+
func TestRFC_DeactivateReg(t *testing.T) {
650+
const email = "mailto:user@example.org"
651+
curStatus := StatusValid
652+
653+
type account struct {
654+
Status string `json:"status"`
655+
Contact []string `json:"contact"`
656+
AcceptTOS bool `json:"termsOfServiceAgreed"`
657+
Orders string `json:"orders"`
658+
}
659+
660+
s := newACMEServer()
661+
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
662+
w.Header().Set("Location", s.url("/accounts/1"))
663+
w.WriteHeader(http.StatusOK) // 200 means existing account
664+
json.NewEncoder(w).Encode(account{
665+
Status: curStatus,
666+
Contact: []string{email},
667+
AcceptTOS: true,
668+
Orders: s.url("/accounts/1/orders"),
669+
})
670+
671+
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
672+
head, err := decodeJWSHead(bytes.NewReader(b))
673+
if err != nil {
674+
t.Errorf("decodeJWSHead: %v", err)
675+
return
676+
}
677+
if len(head.JWK) == 0 {
678+
t.Error("head.JWK is empty")
679+
}
680+
681+
var req struct {
682+
Status string `json:"status"`
683+
Contact []string `json:"contact"`
684+
AcceptTOS bool `json:"termsOfServiceAgreed"`
685+
OnlyExisting bool `json:"onlyReturnExisting"`
686+
}
687+
decodeJWSRequest(t, &req, bytes.NewReader(b))
688+
if !req.OnlyExisting {
689+
t.Errorf("req.OnlyReturnExisting = %t; want = %t", req.OnlyExisting, true)
690+
}
691+
})
692+
s.handle("/accounts/1", func(w http.ResponseWriter, r *http.Request) {
693+
if curStatus == StatusValid {
694+
curStatus = StatusDeactivated
695+
w.WriteHeader(http.StatusOK)
696+
} else {
697+
s.error(w, &wireError{
698+
Status: http.StatusUnauthorized,
699+
Type: "urn:ietf:params:acme:error:unauthorized",
700+
})
701+
}
702+
var req account
703+
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
704+
head, err := decodeJWSHead(bytes.NewReader(b))
705+
if err != nil {
706+
t.Errorf("decodeJWSHead: %v", err)
707+
return
708+
}
709+
if len(head.JWK) != 0 {
710+
t.Error("head.JWK is not empty")
711+
}
712+
if !strings.HasSuffix(head.KID, "/accounts/1") {
713+
t.Errorf("head.KID = %q; want suffix /accounts/1", head.KID)
714+
}
715+
716+
decodeJWSRequest(t, &req, bytes.NewReader(b))
717+
if req.Status != StatusDeactivated {
718+
t.Errorf("req.Status = %q; want = %q", req.Status, StatusDeactivated)
719+
}
720+
})
721+
s.start()
722+
defer s.close()
723+
724+
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
725+
if err := cl.DeactivateReg(context.Background()); err != nil {
726+
t.Errorf("DeactivateReg: %v, wanted no error", err)
727+
}
728+
if err := cl.DeactivateReg(context.Background()); err == nil {
729+
t.Errorf("DeactivateReg: %v, wanted error for unauthorized", err)
730+
}
731+
}
732+
733+
func TestRF_DeactivateRegNoAccount(t *testing.T) {
734+
s := newACMEServer()
735+
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
736+
s.error(w, &wireError{
737+
Status: http.StatusBadRequest,
738+
Type: "urn:ietf:params:acme:error:accountDoesNotExist",
739+
})
740+
})
741+
s.start()
742+
defer s.close()
743+
744+
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
745+
if err := cl.DeactivateReg(context.Background()); !errors.Is(err, ErrNoAccount) {
746+
t.Errorf("DeactivateReg: %v, wanted ErrNoAccount", err)
747+
}
748+
}
749+
647750
func TestRFC_AuthorizeOrder(t *testing.T) {
648751
s := newACMEServer()
649752
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)