forked from TrueCloudLab/certificates
Add endpoint for listing ACME EAB keys
This commit is contained in:
parent
a1afbce50c
commit
f11c0cdc0c
5 changed files with 78 additions and 21 deletions
12
acme/db.go
12
acme/db.go
|
@ -21,6 +21,7 @@ type DB interface {
|
||||||
|
|
||||||
CreateExternalAccountKey(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
|
CreateExternalAccountKey(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
|
||||||
GetExternalAccountKey(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
|
GetExternalAccountKey(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
|
||||||
|
GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
|
||||||
DeleteExternalAccountKey(ctx context.Context, keyID string) error
|
DeleteExternalAccountKey(ctx context.Context, keyID string) error
|
||||||
UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ type MockDB struct {
|
||||||
|
|
||||||
MockCreateExternalAccountKey func(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
|
MockCreateExternalAccountKey func(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
|
||||||
MockGetExternalAccountKey func(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
|
MockGetExternalAccountKey func(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
|
||||||
|
MockGetExternalAccountKeys func(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
|
||||||
MockDeleteExternalAccountKey func(ctx context.Context, keyID string) error
|
MockDeleteExternalAccountKey func(ctx context.Context, keyID string) error
|
||||||
MockUpdateExternalAccountKey func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
MockUpdateExternalAccountKey func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
||||||
|
|
||||||
|
@ -140,6 +142,16 @@ func (m *MockDB) GetExternalAccountKey(ctx context.Context, provisionerName stri
|
||||||
return m.MockRet1.(*ExternalAccountKey), m.MockError
|
return m.MockRet1.(*ExternalAccountKey), m.MockError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExternalAccountKeys mock
|
||||||
|
func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error) {
|
||||||
|
if m.MockGetExternalAccountKeys != nil {
|
||||||
|
return m.MockGetExternalAccountKeys(ctx, provisionerName)
|
||||||
|
} else if m.MockError != nil {
|
||||||
|
return nil, m.MockError
|
||||||
|
}
|
||||||
|
return m.MockRet1.([]*ExternalAccountKey), m.MockError
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteExternalAccountKey mock
|
// DeleteExternalAccountKey mock
|
||||||
func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, keyID string) error {
|
func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, keyID string) error {
|
||||||
if m.MockDeleteExternalAccountKey != nil {
|
if m.MockDeleteExternalAccountKey != nil {
|
||||||
|
|
|
@ -233,6 +233,33 @@ func (db *DB) DeleteExternalAccountKey(ctx context.Context, keyID string) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExternalAccountKeys retrieves all External Account Binding keys for a provisioner
|
||||||
|
func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
|
||||||
|
entries, err := db.db.List(externalAccountKeyTable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]*acme.ExternalAccountKey, len(entries))
|
||||||
|
for i, entry := range entries {
|
||||||
|
dbeak := new(dbExternalAccountKey)
|
||||||
|
if err = json.Unmarshal(entry.Value, dbeak); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error unmarshaling external account key %s into dbExternalAccountKey", string(entry.Key))
|
||||||
|
}
|
||||||
|
keys[i] = &acme.ExternalAccountKey{
|
||||||
|
ID: dbeak.ID,
|
||||||
|
KeyBytes: dbeak.KeyBytes,
|
||||||
|
ProvisionerName: dbeak.ProvisionerName,
|
||||||
|
Name: dbeak.Name,
|
||||||
|
AccountID: dbeak.AccountID,
|
||||||
|
CreatedAt: dbeak.CreatedAt,
|
||||||
|
BoundAt: dbeak.BoundAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *acme.ExternalAccountKey) error {
|
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *acme.ExternalAccountKey) error {
|
||||||
old, err := db.getDBExternalAccountKey(ctx, eak.ID)
|
old, err := db.getDBExternalAccountKey(ctx, eak.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateExternalAccountKeyRequest is the type for POST /admin/acme/eab requests
|
// CreateExternalAccountKeyRequest is the type for POST /admin/acme/eab requests
|
||||||
|
@ -35,7 +36,7 @@ type GetExternalAccountKeysResponse struct {
|
||||||
// CreateExternalAccountKey creates a new External Account Binding key
|
// CreateExternalAccountKey creates a new External Account Binding key
|
||||||
func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
||||||
var body CreateExternalAccountKeyRequest
|
var body CreateExternalAccountKeyRequest
|
||||||
if err := api.ReadJSON(r.Body, &body); err != nil { // TODO: rewrite into protobuf json (likely)
|
if err := api.ReadJSON(r.Body, &body); err != nil {
|
||||||
api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -75,6 +76,9 @@ func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Reques
|
||||||
|
|
||||||
// GetExternalAccountKeys returns a segment of ACME EAB Keys.
|
// GetExternalAccountKeys returns a segment of ACME EAB Keys.
|
||||||
func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
|
||||||
|
prov := chi.URLParam(r, "prov")
|
||||||
|
|
||||||
|
// TODO: support paging properly? It'll probably leak to the DB layer, as we have to loop through all keys
|
||||||
// cursor, limit, err := api.ParseCursor(r)
|
// cursor, limit, err := api.ParseCursor(r)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
// api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||||
|
@ -82,13 +86,28 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// eaks, nextCursor, err := h.acmeDB.GetExternalAccountKeys(cursor, limit)
|
keys, err := h.acmeDB.GetExternalAccountKeys(r.Context(), prov)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// api.WriteError(w, admin.WrapErrorISE(err, "error retrieving paginated admins"))
|
api.WriteError(w, admin.WrapErrorISE(err, "error getting external account keys"))
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
// api.JSON(w, &GetExternalAccountKeysResponse{
|
|
||||||
// EAKs: eaks,
|
eaks := make([]*linkedca.EABKey, len(keys))
|
||||||
// NextCursor: nextCursor,
|
for i, k := range keys {
|
||||||
// })
|
eaks[i] = &linkedca.EABKey{
|
||||||
|
EabKid: k.ID,
|
||||||
|
EabHmacKey: []byte{},
|
||||||
|
ProvisionerName: k.ProvisionerName,
|
||||||
|
Name: k.Name,
|
||||||
|
Account: k.AccountID,
|
||||||
|
CreatedAt: timestamppb.New(k.CreatedAt),
|
||||||
|
BoundAt: timestamppb.New(k.BoundAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextCursor := ""
|
||||||
|
api.JSON(w, &GetExternalAccountKeysResponse{
|
||||||
|
EAKs: eaks,
|
||||||
|
NextCursor: nextCursor,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (h *Handler) Route(r api.Router) {
|
||||||
r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin))
|
r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin))
|
||||||
|
|
||||||
// ACME External Account Binding Keys
|
// ACME External Account Binding Keys
|
||||||
r.MethodFunc("GET", "/acme/eab", authnz(h.GetExternalAccountKeys))
|
r.MethodFunc("GET", "/acme/eab/{prov}", authnz(h.GetExternalAccountKeys))
|
||||||
r.MethodFunc("POST", "/acme/eab", authnz(h.CreateExternalAccountKey))
|
r.MethodFunc("POST", "/acme/eab", authnz(h.CreateExternalAccountKey))
|
||||||
r.MethodFunc("DELETE", "/acme/eab/{id}", authnz(h.DeleteExternalAccountKey))
|
r.MethodFunc("DELETE", "/acme/eab/{id}", authnz(h.DeleteExternalAccountKey))
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,14 +559,14 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExternalAccountKeysPaginate returns a page from the the GET /admin/acme/eab 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) {
|
func (c *AdminClient) GetExternalAccountKeysPaginate(provisionerName string, opts ...AdminOption) (*adminAPI.GetExternalAccountKeysResponse, error) {
|
||||||
var retried bool
|
var retried bool
|
||||||
o := new(adminOptions)
|
o := new(adminOptions)
|
||||||
if err := o.apply(opts); err != nil {
|
if err := o.apply(opts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
u := c.endpoint.ResolveReference(&url.URL{
|
u := c.endpoint.ResolveReference(&url.URL{
|
||||||
Path: "/admin/acme/eab",
|
Path: path.Join(adminURLPrefix, "acme/eab", provisionerName),
|
||||||
RawQuery: o.rawQuery(),
|
RawQuery: o.rawQuery(),
|
||||||
})
|
})
|
||||||
tok, err := c.generateAdminToken(u.Path)
|
tok, err := c.generateAdminToken(u.Path)
|
||||||
|
@ -590,12 +590,11 @@ retry:
|
||||||
}
|
}
|
||||||
return nil, readAdminError(resp.Body)
|
return nil, readAdminError(resp.Body)
|
||||||
}
|
}
|
||||||
// var body = new(GetExternalAccountKeysResponse)
|
var body = new(adminAPI.GetExternalAccountKeysResponse)
|
||||||
// if err := readJSON(resp.Body, body); err != nil {
|
if err := readJSON(resp.Body, body); err != nil {
|
||||||
// return nil, errors.Wrapf(err, "error reading %s", u)
|
return nil, errors.Wrapf(err, "error reading %s", u)
|
||||||
// }
|
}
|
||||||
// return body, nil
|
return body, nil
|
||||||
return nil, nil // TODO: fix correctly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateExternalAccountKey performs the POST /admin/acme/eab request to the CA.
|
// CreateExternalAccountKey performs the POST /admin/acme/eab request to the CA.
|
||||||
|
@ -663,13 +662,13 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExternalAccountKeys returns all ACME EAB Keys from the GET /admin/acme/eab request to the CA.
|
// GetExternalAccountKeys returns all ACME EAB Keys from the GET /admin/acme/eab request to the CA.
|
||||||
func (c *AdminClient) GetExternalAccountKeys(opts ...AdminOption) ([]*linkedca.EABKey, error) {
|
func (c *AdminClient) GetExternalAccountKeys(provisionerName string, opts ...AdminOption) ([]*linkedca.EABKey, error) {
|
||||||
var (
|
var (
|
||||||
cursor = ""
|
cursor = ""
|
||||||
eaks = []*linkedca.EABKey{}
|
eaks = []*linkedca.EABKey{}
|
||||||
)
|
)
|
||||||
for {
|
for {
|
||||||
resp, err := c.GetExternalAccountKeysPaginate(WithAdminCursor(cursor), WithAdminLimit(100))
|
resp, err := c.GetExternalAccountKeysPaginate(provisionerName, WithAdminCursor(cursor), WithAdminLimit(100))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue