From c6a4c4ecba3d59445fce0b020b64d21cc4364c76 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 23 Jul 2021 15:16:11 +0200 Subject: [PATCH] Change ACME EAB endpoint --- authority/admin/api/eak.go | 45 ----------------------- authority/admin/api/handler.go | 5 +-- authority/admin/eak/eak.go | 12 ------- ca/adminClient.go | 65 ++++++++++++++++++++++++++++++++-- 4 files changed, 65 insertions(+), 62 deletions(-) delete mode 100644 authority/admin/api/eak.go delete mode 100644 authority/admin/eak/eak.go diff --git a/authority/admin/api/eak.go b/authority/admin/api/eak.go deleted file mode 100644 index 8ea83309..00000000 --- a/authority/admin/api/eak.go +++ /dev/null @@ -1,45 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/admin" -) - -// CreateExternalAccountKeyRequest is the type for POST /admin/eak requests -type CreateExternalAccountKeyRequest struct { - Name string `json:"name"` -} - -// CreateExternalAccountKeyResponse is the type for POST /admin/eak responses -type CreateExternalAccountKeyResponse struct { - KeyID string `json:"keyID"` - Name string `json:"name"` - Key []byte `json:"key"` -} - -// CreateExternalAccountKey creates a new External Account Binding key -func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Request) { - var body CreateExternalAccountKeyRequest - if err := api.ReadJSON(r.Body, &body); err != nil { // TODO: rewrite into protobuf json (likely) - api.WriteError(w, err) - return - } - - // TODO: Validate input - - eak, err := h.acmeDB.CreateExternalAccountKey(r.Context(), body.Name) - if err != nil { - api.WriteError(w, admin.WrapErrorISE(err, "error creating external account key %s", body.Name)) - return - } - - eakResponse := CreateExternalAccountKeyResponse{ - KeyID: eak.ID, - Name: eak.Name, - Key: eak.KeyBytes, - } - - api.JSONStatus(w, eakResponse, http.StatusCreated) // TODO: rewrite into protobuf json (likely) -} diff --git a/authority/admin/api/handler.go b/authority/admin/api/handler.go index b20e29ab..0e49dfd9 100644 --- a/authority/admin/api/handler.go +++ b/authority/admin/api/handler.go @@ -43,6 +43,7 @@ func (h *Handler) Route(r api.Router) { r.MethodFunc("PATCH", "/admins/{id}", authnz(h.UpdateAdmin)) r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin)) - // External Account Binding Keys - r.MethodFunc("POST", "/eak", authnz(h.CreateExternalAccountKey)) + // ACME External Account Binding Keys + r.MethodFunc("GET", "/acme/eab", authnz(h.GetExternalAccountKeys)) + r.MethodFunc("POST", "/acme/eab", authnz(h.CreateExternalAccountKey)) } diff --git a/authority/admin/eak/eak.go b/authority/admin/eak/eak.go deleted file mode 100644 index b2eaa157..00000000 --- a/authority/admin/eak/eak.go +++ /dev/null @@ -1,12 +0,0 @@ -package eak - -import "time" - -type ExternalAccountKey struct { - ID string `json:"id"` - Name string `json:"name"` - AccountID string `json:"-"` - KeyBytes []byte `json:"-"` - CreatedAt time.Time `json:"createdAt"` - BoundAt time.Time `json:"boundAt,omitempty"` -} diff --git a/ca/adminClient.go b/ca/adminClient.go index 4aa03db2..7f24efa9 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -559,15 +559,54 @@ retry: return nil } -// CreateExternalAccountKey performs the POST /admin/eak request to the CA. +// GetExternalAccountKeysPaginate returns a page from the the GET /admin/acme/eab request to the CA. +func (c *AdminClient) GetExternalAccountKeysPaginate(opts ...AdminOption) (*adminAPI.GetExternalAccountKeysResponse, error) { + var retried bool + o := new(adminOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + u := c.endpoint.ResolveReference(&url.URL{ + Path: "/admin/acme/eab", + RawQuery: o.rawQuery(), + }) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create GET %s request failed", u) + } + req.Header.Add("Authorization", tok) +retry: + resp, err := c.client.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "client GET %s failed", u) + } + if resp.StatusCode >= 400 { + if !retried && c.retryOnError(resp) { + retried = true + goto retry + } + return nil, readAdminError(resp.Body) + } + // var body = new(GetExternalAccountKeysResponse) + // if err := readJSON(resp.Body, body); err != nil { + // return nil, errors.Wrapf(err, "error reading %s", u) + // } + // return body, nil + return nil, nil // TODO: fix correctly +} + +// CreateExternalAccountKey performs the POST /admin/acme/eab request to the CA. func (c *AdminClient) CreateExternalAccountKey(eakRequest *adminAPI.CreateExternalAccountKeyRequest) (*adminAPI.CreateExternalAccountKeyResponse, error) { var retried bool - //body, err := protojson.Marshal(req) body, err := json.Marshal(eakRequest) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "eak")}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "acme/eab")}) tok, err := c.generateAdminToken(u.Path) if err != nil { return nil, errors.Wrapf(err, "error generating admin token") @@ -596,7 +635,27 @@ retry: return eakResp, nil } +// GetExternalAccountKeys returns all ACME EAB Keys from the GET /admin/acme/eab request to the CA. +func (c *AdminClient) GetExternalAccountKeys(opts ...AdminOption) ([]*adminAPI.CreateExternalAccountKeyResponse, error) { + var ( + cursor = "" + eaks = []*adminAPI.CreateExternalAccountKeyResponse{} + ) + for { + resp, err := c.GetExternalAccountKeysPaginate(WithAdminCursor(cursor), WithAdminLimit(100)) + if err != nil { + return nil, err + } + eaks = append(eaks, resp.EAKs...) + if resp.NextCursor == "" { + return eaks, nil + } + cursor = resp.NextCursor + } +} + func readAdminError(r io.ReadCloser) error { + // TODO: not all errors can be read (i.e. 404); seems to be a bigger issue defer r.Close() adminErr := new(admin.Error) if err := json.NewDecoder(r).Decode(adminErr); err != nil {