Skip to content

Commit 9c00203

Browse files
committed
Add lookup by reference and make reference optional
1 parent 02cd3b6 commit 9c00203

File tree

6 files changed

+88
-26
lines changed

6 files changed

+88
-26
lines changed

acme/db.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type DB interface {
2222
CreateExternalAccountKey(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
2323
GetExternalAccountKey(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
2424
GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
25+
GetExternalAccountKeyByReference(ctx context.Context, provisionerName string, reference string) (*ExternalAccountKey, error)
2526
DeleteExternalAccountKey(ctx context.Context, keyID string) error
2627
UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
2728

@@ -53,11 +54,12 @@ type MockDB struct {
5354
MockGetAccountByKeyID func(ctx context.Context, kid string) (*Account, error)
5455
MockUpdateAccount func(ctx context.Context, acc *Account) error
5556

56-
MockCreateExternalAccountKey func(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
57-
MockGetExternalAccountKey func(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
58-
MockGetExternalAccountKeys func(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
59-
MockDeleteExternalAccountKey func(ctx context.Context, keyID string) error
60-
MockUpdateExternalAccountKey func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
57+
MockCreateExternalAccountKey func(ctx context.Context, provisionerName string, name string) (*ExternalAccountKey, error)
58+
MockGetExternalAccountKey func(ctx context.Context, provisionerName string, keyID string) (*ExternalAccountKey, error)
59+
MockGetExternalAccountKeys func(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
60+
MockGetExternalAccountKeyByReference func(ctx context.Context, provisionerName string, reference string) (*ExternalAccountKey, error)
61+
MockDeleteExternalAccountKey func(ctx context.Context, keyID string) error
62+
MockUpdateExternalAccountKey func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
6163

6264
MockCreateNonce func(ctx context.Context) (Nonce, error)
6365
MockDeleteNonce func(ctx context.Context, nonce Nonce) error
@@ -152,6 +154,16 @@ func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName str
152154
return m.MockRet1.([]*ExternalAccountKey), m.MockError
153155
}
154156

157+
// GetExtrnalAccountKeyByReference mock
158+
func (m *MockDB) GetExternalAccountKeyByReference(ctx context.Context, provisionerName string, reference string) (*ExternalAccountKey, error) {
159+
if m.MockGetExternalAccountKeys != nil {
160+
return m.GetExternalAccountKeyByReference(ctx, provisionerName, reference)
161+
} else if m.MockError != nil {
162+
return nil, m.MockError
163+
}
164+
return m.MockRet1.(*ExternalAccountKey), m.MockError
165+
}
166+
155167
// DeleteExternalAccountKey mock
156168
func (m *MockDB) DeleteExternalAccountKey(ctx context.Context, keyID string) error {
157169
if m.MockDeleteExternalAccountKey != nil {

acme/db/nosql/account.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/pkg/errors"
1010
"github.com/smallstep/certificates/acme"
11+
"github.com/smallstep/nosql"
1112
nosqlDB "github.com/smallstep/nosql"
1213
"go.step.sm/crypto/jose"
1314
)
@@ -37,6 +38,11 @@ type dbExternalAccountKey struct {
3738
BoundAt time.Time `json:"boundAt"`
3839
}
3940

41+
type dbExternalAccountKeyReference struct {
42+
Reference string `json:"reference"`
43+
ExternalAccountKeyID string `json:"externalAccountKeyID"`
44+
}
45+
4046
func (db *DB) getAccountIDByKeyID(ctx context.Context, kid string) (string, error) {
4147
id, err := db.db.Get(accountByKeyIDTable, []byte(kid))
4248
if err != nil {
@@ -188,6 +194,17 @@ func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerName stri
188194
if err = db.save(ctx, keyID, dbeak, nil, "external_account_key", externalAccountKeyTable); err != nil {
189195
return nil, err
190196
}
197+
198+
if dbeak.Reference != "" {
199+
dbExternalAccountKeyReference := &dbExternalAccountKeyReference{
200+
Reference: dbeak.Reference,
201+
ExternalAccountKeyID: dbeak.ID,
202+
}
203+
if err = db.save(ctx, dbeak.Reference, dbExternalAccountKeyReference, nil, "external_account_key_reference", externalAccountKeysByReferenceTable); err != nil {
204+
return nil, err
205+
}
206+
}
207+
191208
return &acme.ExternalAccountKey{
192209
ID: dbeak.ID,
193210
Provisioner: dbeak.Provisioner,
@@ -263,6 +280,21 @@ func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName string
263280
return keys, nil
264281
}
265282

283+
// GetExternalAccountKeyByReference retrieves an External Account Binding key with unique reference
284+
func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerName string, reference string) (*acme.ExternalAccountKey, error) {
285+
k, err := db.db.Get(externalAccountKeysByReferenceTable, []byte(reference))
286+
if nosql.IsErrNotFound(err) {
287+
return nil, errors.Errorf("ACME EAB key for reference %s not found", reference)
288+
} else if err != nil {
289+
return nil, errors.Wrapf(err, "error loading ACME EAB key for reference %s", reference)
290+
}
291+
dbExternalAccountKeyReference := new(dbExternalAccountKeyReference)
292+
if err := json.Unmarshal(k, dbExternalAccountKeyReference); err != nil {
293+
return nil, errors.Wrapf(err, "error unmarshaling ACME EAB key for reference %s", reference)
294+
}
295+
return db.GetExternalAccountKey(ctx, provisionerName, dbExternalAccountKeyReference.ExternalAccountKeyID)
296+
}
297+
266298
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *acme.ExternalAccountKey) error {
267299
old, err := db.getDBExternalAccountKey(ctx, eak.ID)
268300
if err != nil {

acme/db/nosql/nosql.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ import (
1111
)
1212

1313
var (
14-
accountTable = []byte("acme_accounts")
15-
accountByKeyIDTable = []byte("acme_keyID_accountID_index")
16-
authzTable = []byte("acme_authzs")
17-
challengeTable = []byte("acme_challenges")
18-
nonceTable = []byte("nonces")
19-
orderTable = []byte("acme_orders")
20-
ordersByAccountIDTable = []byte("acme_account_orders_index")
21-
certTable = []byte("acme_certs")
22-
externalAccountKeyTable = []byte("acme_external_account_keys")
14+
accountTable = []byte("acme_accounts")
15+
accountByKeyIDTable = []byte("acme_keyID_accountID_index")
16+
authzTable = []byte("acme_authzs")
17+
challengeTable = []byte("acme_challenges")
18+
nonceTable = []byte("nonces")
19+
orderTable = []byte("acme_orders")
20+
ordersByAccountIDTable = []byte("acme_account_orders_index")
21+
certTable = []byte("acme_certs")
22+
externalAccountKeyTable = []byte("acme_external_account_keys")
23+
externalAccountKeysByReferenceTable = []byte("acme_external_account_key_reference_index")
2324
)
2425

2526
// DB is a struct that implements the AcmeDB interface.
@@ -30,7 +31,7 @@ type DB struct {
3031
// New configures and returns a new ACME DB backend implemented using a nosql DB.
3132
func New(db nosqlDB.DB) (*DB, error) {
3233
tables := [][]byte{accountTable, accountByKeyIDTable, authzTable,
33-
challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable, externalAccountKeyTable}
34+
challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable, externalAccountKeyTable, externalAccountKeysByReferenceTable}
3435
for _, b := range tables {
3536
if err := db.CreateTable(b); err != nil {
3637
return nil, errors.Wrapf(err, "error creating table %s",

authority/admin/api/acme.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66

77
"github.com/go-chi/chi"
8+
"github.com/smallstep/certificates/acme"
89
"github.com/smallstep/certificates/api"
910
"github.com/smallstep/certificates/authority/admin"
1011
"github.com/smallstep/certificates/authority/provisioner"
@@ -23,9 +24,6 @@ func (r *CreateExternalAccountKeyRequest) Validate() error {
2324
if r.Provisioner == "" {
2425
return admin.NewError(admin.ErrorBadRequestType, "provisioner name cannot be empty")
2526
}
26-
if r.Reference == "" {
27-
return admin.NewError(admin.ErrorBadRequestType, "reference cannot be empty")
28-
}
2927
return nil
3028
}
3129

@@ -124,6 +122,7 @@ func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Reques
124122
// GetExternalAccountKeys returns a segment of ACME EAB Keys.
125123
func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
126124
prov := chi.URLParam(r, "prov")
125+
reference := chi.URLParam(r, "ref")
127126

128127
eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), prov)
129128
if err != nil {
@@ -144,10 +143,23 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
144143
// return
145144
// }
146145

147-
keys, err := h.acmeDB.GetExternalAccountKeys(r.Context(), prov)
148-
if err != nil {
149-
api.WriteError(w, admin.WrapErrorISE(err, "error getting external account keys"))
150-
return
146+
var (
147+
key *acme.ExternalAccountKey
148+
keys []*acme.ExternalAccountKey
149+
)
150+
if reference != "" {
151+
key, err = h.acmeDB.GetExternalAccountKeyByReference(r.Context(), prov, reference)
152+
if err != nil {
153+
api.WriteError(w, admin.WrapErrorISE(err, "error getting external account key with reference %s", reference))
154+
return
155+
}
156+
keys = []*acme.ExternalAccountKey{key}
157+
} else {
158+
keys, err = h.acmeDB.GetExternalAccountKeys(r.Context(), prov)
159+
if err != nil {
160+
api.WriteError(w, admin.WrapErrorISE(err, "error getting external account keys"))
161+
return
162+
}
151163
}
152164

153165
eaks := make([]*linkedca.EABKey, len(keys))

authority/admin/api/handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func (h *Handler) Route(r api.Router) {
4444
r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin))
4545

4646
// ACME External Account Binding Keys
47+
r.MethodFunc("GET", "/acme/eab/{prov}/{ref}", authnz(h.GetExternalAccountKeys))
4748
r.MethodFunc("GET", "/acme/eab/{prov}", authnz(h.GetExternalAccountKeys))
4849
r.MethodFunc("POST", "/acme/eab", authnz(h.CreateExternalAccountKey))
4950
r.MethodFunc("DELETE", "/acme/eab/{id}", authnz(h.DeleteExternalAccountKey))

ca/adminClient.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,14 +559,18 @@ retry:
559559
}
560560

561561
// GetExternalAccountKeysPaginate returns a page from the the GET /admin/acme/eab request to the CA.
562-
func (c *AdminClient) GetExternalAccountKeysPaginate(provisionerName string, opts ...AdminOption) (*adminAPI.GetExternalAccountKeysResponse, error) {
562+
func (c *AdminClient) GetExternalAccountKeysPaginate(provisionerName string, reference string, opts ...AdminOption) (*adminAPI.GetExternalAccountKeysResponse, error) {
563563
var retried bool
564564
o := new(adminOptions)
565565
if err := o.apply(opts); err != nil {
566566
return nil, err
567567
}
568+
p := path.Join(adminURLPrefix, "acme/eab", provisionerName)
569+
if reference != "" {
570+
p = path.Join(p, "/", reference)
571+
}
568572
u := c.endpoint.ResolveReference(&url.URL{
569-
Path: path.Join(adminURLPrefix, "acme/eab", provisionerName),
573+
Path: p,
570574
RawQuery: o.rawQuery(),
571575
})
572576
tok, err := c.generateAdminToken(u.Path)
@@ -662,13 +666,13 @@ retry:
662666
}
663667

664668
// GetExternalAccountKeys returns all ACME EAB Keys from the GET /admin/acme/eab request to the CA.
665-
func (c *AdminClient) GetExternalAccountKeys(provisionerName string, opts ...AdminOption) ([]*linkedca.EABKey, error) {
669+
func (c *AdminClient) GetExternalAccountKeys(provisionerName string, reference string, opts ...AdminOption) ([]*linkedca.EABKey, error) {
666670
var (
667671
cursor = ""
668672
eaks = []*linkedca.EABKey{}
669673
)
670674
for {
671-
resp, err := c.GetExternalAccountKeysPaginate(provisionerName, WithAdminCursor(cursor), WithAdminLimit(100))
675+
resp, err := c.GetExternalAccountKeysPaginate(provisionerName, reference, WithAdminCursor(cursor), WithAdminLimit(100))
672676
if err != nil {
673677
return nil, err
674678
}

0 commit comments

Comments
 (0)