Increase test coverage for additional indexes
This commit is contained in:
parent
8838961b68
commit
868cc4ad7f
6 changed files with 2131 additions and 1380 deletions
|
@ -2,7 +2,6 @@ package nosql
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -172,284 +171,3 @@ func (db *DB) UpdateAccount(ctx context.Context, acc *acme.Account) error {
|
|||
|
||||
return db.save(ctx, old.ID, nu, old, "account", accountTable)
|
||||
}
|
||||
|
||||
// CreateExternalAccountKey creates a new External Account Binding key with a name
|
||||
func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
|
||||
keyID, err := randID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
random := make([]byte, 32)
|
||||
_, err = rand.Read(random)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbeak := &dbExternalAccountKey{
|
||||
ID: keyID,
|
||||
ProvisionerID: provisionerID,
|
||||
Reference: reference,
|
||||
KeyBytes: random,
|
||||
CreatedAt: clock.Now(),
|
||||
}
|
||||
|
||||
if err := db.save(ctx, keyID, dbeak, nil, "external_account_key", externalAccountKeyTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := db.addEAKID(ctx, provisionerID, dbeak.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dbeak.Reference != "" {
|
||||
dbExternalAccountKeyReference := &dbExternalAccountKeyReference{
|
||||
Reference: dbeak.Reference,
|
||||
ExternalAccountKeyID: dbeak.ID,
|
||||
}
|
||||
if err := db.save(ctx, referenceKey(provisionerID, dbeak.Reference), dbExternalAccountKeyReference, nil, "external_account_key_reference", externalAccountKeysByReferenceTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &acme.ExternalAccountKey{
|
||||
ID: dbeak.ID,
|
||||
ProvisionerID: dbeak.ProvisionerID,
|
||||
Reference: dbeak.Reference,
|
||||
AccountID: dbeak.AccountID,
|
||||
KeyBytes: dbeak.KeyBytes,
|
||||
CreatedAt: dbeak.CreatedAt,
|
||||
BoundAt: dbeak.BoundAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKey retrieves an External Account Binding key by KeyID
|
||||
func (db *DB) GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*acme.ExternalAccountKey, error) {
|
||||
dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dbeak.ProvisionerID != provisionerID {
|
||||
return nil, acme.NewError(acme.ErrorUnauthorizedType, "provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
return &acme.ExternalAccountKey{
|
||||
ID: dbeak.ID,
|
||||
ProvisionerID: dbeak.ProvisionerID,
|
||||
Reference: dbeak.Reference,
|
||||
AccountID: dbeak.AccountID,
|
||||
KeyBytes: dbeak.KeyBytes,
|
||||
CreatedAt: dbeak.CreatedAt,
|
||||
BoundAt: dbeak.BoundAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *DB) DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error {
|
||||
dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error loading ACME EAB Key with Key ID %s", keyID)
|
||||
}
|
||||
|
||||
if dbeak.ProvisionerID != provisionerID {
|
||||
return errors.New("provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
if dbeak.Reference != "" {
|
||||
if err := db.db.Del(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, dbeak.Reference))); err != nil {
|
||||
return errors.Wrapf(err, "error deleting ACME EAB Key reference with Key ID %s and reference %s", keyID, dbeak.Reference)
|
||||
}
|
||||
}
|
||||
if err := db.db.Del(externalAccountKeyTable, []byte(keyID)); err != nil {
|
||||
return errors.Wrapf(err, "error deleting ACME EAB Key with Key ID %s", keyID)
|
||||
}
|
||||
if err := db.deleteEAKID(ctx, provisionerID, keyID); err != nil {
|
||||
return errors.Wrapf(err, "error removing ACME EAB Key ID %s", keyID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeys retrieves all External Account Binding keys for a provisioner
|
||||
func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerID string) ([]*acme.ExternalAccountKey, error) {
|
||||
|
||||
// TODO: mutex?
|
||||
|
||||
var eakIDs []string
|
||||
r, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "error loading ACME EAB Key IDs for provisioner %s", provisionerID)
|
||||
}
|
||||
} else {
|
||||
if err := json.Unmarshal(r, &eakIDs); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling ACME EAB Key IDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
keys := []*acme.ExternalAccountKey{}
|
||||
for _, eakID := range eakIDs {
|
||||
if eakID == "" {
|
||||
continue // shouldn't happen; just in case
|
||||
}
|
||||
eak, err := db.getDBExternalAccountKey(ctx, eakID)
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "error retrieving ACME EAB Key for provisioner %s and keyID %s", provisionerID, eakID)
|
||||
}
|
||||
}
|
||||
keys = append(keys, &acme.ExternalAccountKey{
|
||||
ID: eak.ID,
|
||||
KeyBytes: eak.KeyBytes,
|
||||
ProvisionerID: eak.ProvisionerID,
|
||||
Reference: eak.Reference,
|
||||
AccountID: eak.AccountID,
|
||||
CreatedAt: eak.CreatedAt,
|
||||
BoundAt: eak.BoundAt,
|
||||
})
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeyByReference retrieves an External Account Binding key with unique reference
|
||||
func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
|
||||
if reference == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
k, err := db.db.Get(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, reference)))
|
||||
if nosqlDB.IsErrNotFound(err) {
|
||||
return nil, acme.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading ACME EAB key for reference %s", reference)
|
||||
}
|
||||
dbExternalAccountKeyReference := new(dbExternalAccountKeyReference)
|
||||
if err := json.Unmarshal(k, dbExternalAccountKeyReference); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling ACME EAB key for reference %s", reference)
|
||||
}
|
||||
|
||||
return db.GetExternalAccountKey(ctx, provisionerID, dbExternalAccountKeyReference.ExternalAccountKeyID)
|
||||
}
|
||||
|
||||
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *acme.ExternalAccountKey) error {
|
||||
old, err := db.getDBExternalAccountKey(ctx, eak.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if old.ProvisionerID != provisionerID {
|
||||
return errors.New("provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
if old.ProvisionerID != eak.ProvisionerID {
|
||||
return errors.New("cannot change provisioner for an existing ACME EAB Key")
|
||||
}
|
||||
|
||||
if old.Reference != eak.Reference {
|
||||
return errors.New("cannot change reference for an existing ACME EAB Key")
|
||||
}
|
||||
|
||||
nu := dbExternalAccountKey{
|
||||
ID: eak.ID,
|
||||
ProvisionerID: eak.ProvisionerID,
|
||||
Reference: eak.Reference,
|
||||
AccountID: eak.AccountID,
|
||||
KeyBytes: eak.KeyBytes,
|
||||
CreatedAt: eak.CreatedAt,
|
||||
BoundAt: eak.BoundAt,
|
||||
}
|
||||
|
||||
return db.save(ctx, nu.ID, nu, old, "external_account_key", externalAccountKeyTable)
|
||||
}
|
||||
|
||||
func (db *DB) addEAKID(ctx context.Context, provisionerID, eakID string) error {
|
||||
referencesByProvisionerIndexMux.Lock()
|
||||
defer referencesByProvisionerIndexMux.Unlock()
|
||||
|
||||
var eakIDs []string
|
||||
b, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return errors.Wrapf(err, "error loading eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
} else {
|
||||
if err := json.Unmarshal(b, &eakIDs); err != nil {
|
||||
return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
var newEAKIDs []string
|
||||
newEAKIDs = append(newEAKIDs, eakIDs...)
|
||||
newEAKIDs = append(newEAKIDs, eakID)
|
||||
var (
|
||||
_old interface{} = eakIDs
|
||||
_new interface{} = newEAKIDs
|
||||
)
|
||||
|
||||
if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeysByProvisionerID", externalAccountKeysByProvisionerIDTable); err != nil {
|
||||
return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) deleteEAKID(ctx context.Context, provisionerID, eakID string) error {
|
||||
referencesByProvisionerIndexMux.Lock()
|
||||
defer referencesByProvisionerIndexMux.Unlock()
|
||||
|
||||
var eakIDs []string
|
||||
b, err := db.db.Get(externalAccountKeysByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return errors.Wrapf(err, "error loading reference IDs for provisioner %s", provisionerID)
|
||||
}
|
||||
} else {
|
||||
if err := json.Unmarshal(b, &eakIDs); err != nil {
|
||||
return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
newEAKIDs := removeElement(eakIDs, eakID)
|
||||
var (
|
||||
_old interface{} = eakIDs
|
||||
_new interface{} = newEAKIDs
|
||||
)
|
||||
|
||||
if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeysByProvisionerID", externalAccountKeysByProvisionerIDTable); err != nil {
|
||||
return errors.Wrapf(err, "error saving referenceIDs index for provisioner %s", provisionerID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// referenceKey returns a unique key for a reference per provisioner
|
||||
func referenceKey(provisionerID, reference string) string {
|
||||
return provisionerID + "." + reference
|
||||
}
|
||||
|
||||
// sliceIndex finds the index of item in slice
|
||||
func sliceIndex(slice []string, item string) int {
|
||||
for i := range slice {
|
||||
if slice[i] == item {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// removeElement deletes the item if it exists in the
|
||||
// slice. It returns a new slice, keeping the old one intact.
|
||||
func removeElement(slice []string, item string) []string {
|
||||
|
||||
newSlice := make([]string, 0)
|
||||
index := sliceIndex(slice, item)
|
||||
if index < 0 {
|
||||
newSlice = append(newSlice, slice...)
|
||||
return newSlice
|
||||
}
|
||||
|
||||
newSlice = append(newSlice, slice[:index]...)
|
||||
|
||||
return append(newSlice, slice[index+1:]...)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
306
acme/db/nosql/eab.go
Normal file
306
acme/db/nosql/eab.go
Normal file
|
@ -0,0 +1,306 @@
|
|||
package nosql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/acme"
|
||||
nosqlDB "github.com/smallstep/nosql"
|
||||
)
|
||||
|
||||
// CreateExternalAccountKey creates a new External Account Binding key with a name
|
||||
func (db *DB) CreateExternalAccountKey(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
|
||||
keyID, err := randID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
random := make([]byte, 32)
|
||||
_, err = rand.Read(random)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbeak := &dbExternalAccountKey{
|
||||
ID: keyID,
|
||||
ProvisionerID: provisionerID,
|
||||
Reference: reference,
|
||||
KeyBytes: random,
|
||||
CreatedAt: clock.Now(),
|
||||
}
|
||||
|
||||
if err := db.save(ctx, keyID, dbeak, nil, "external_account_key", externalAccountKeyTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := db.addEAKID(ctx, provisionerID, dbeak.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dbeak.Reference != "" {
|
||||
dbExternalAccountKeyReference := &dbExternalAccountKeyReference{
|
||||
Reference: dbeak.Reference,
|
||||
ExternalAccountKeyID: dbeak.ID,
|
||||
}
|
||||
if err := db.save(ctx, referenceKey(provisionerID, dbeak.Reference), dbExternalAccountKeyReference, nil, "external_account_key_reference", externalAccountKeysByReferenceTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &acme.ExternalAccountKey{
|
||||
ID: dbeak.ID,
|
||||
ProvisionerID: dbeak.ProvisionerID,
|
||||
Reference: dbeak.Reference,
|
||||
AccountID: dbeak.AccountID,
|
||||
KeyBytes: dbeak.KeyBytes,
|
||||
CreatedAt: dbeak.CreatedAt,
|
||||
BoundAt: dbeak.BoundAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKey retrieves an External Account Binding key by KeyID
|
||||
func (db *DB) GetExternalAccountKey(ctx context.Context, provisionerID, keyID string) (*acme.ExternalAccountKey, error) {
|
||||
dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dbeak.ProvisionerID != provisionerID {
|
||||
return nil, acme.NewError(acme.ErrorUnauthorizedType, "provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
return &acme.ExternalAccountKey{
|
||||
ID: dbeak.ID,
|
||||
ProvisionerID: dbeak.ProvisionerID,
|
||||
Reference: dbeak.Reference,
|
||||
AccountID: dbeak.AccountID,
|
||||
KeyBytes: dbeak.KeyBytes,
|
||||
CreatedAt: dbeak.CreatedAt,
|
||||
BoundAt: dbeak.BoundAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *DB) DeleteExternalAccountKey(ctx context.Context, provisionerID, keyID string) error {
|
||||
dbeak, err := db.getDBExternalAccountKey(ctx, keyID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error loading ACME EAB Key with Key ID %s", keyID)
|
||||
}
|
||||
|
||||
if dbeak.ProvisionerID != provisionerID {
|
||||
return errors.New("provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
if dbeak.Reference != "" {
|
||||
if err := db.db.Del(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, dbeak.Reference))); err != nil {
|
||||
return errors.Wrapf(err, "error deleting ACME EAB Key reference with Key ID %s and reference %s", keyID, dbeak.Reference)
|
||||
}
|
||||
}
|
||||
if err := db.db.Del(externalAccountKeyTable, []byte(keyID)); err != nil {
|
||||
return errors.Wrapf(err, "error deleting ACME EAB Key with Key ID %s", keyID)
|
||||
}
|
||||
if err := db.deleteEAKID(ctx, provisionerID, keyID); err != nil {
|
||||
return errors.Wrapf(err, "error removing ACME EAB Key ID %s", keyID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeys retrieves all External Account Binding keys for a provisioner
|
||||
func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerID string) ([]*acme.ExternalAccountKey, error) {
|
||||
|
||||
// TODO: mutex?
|
||||
|
||||
var eakIDs []string
|
||||
r, err := db.db.Get(externalAccountKeyIDsByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "error loading ACME EAB Key IDs for provisioner %s", provisionerID)
|
||||
}
|
||||
// it may happen that no record is found; we'll continue with an empty slice
|
||||
} else {
|
||||
if err := json.Unmarshal(r, &eakIDs); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling ACME EAB Key IDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
keys := []*acme.ExternalAccountKey{}
|
||||
for _, eakID := range eakIDs {
|
||||
if eakID == "" {
|
||||
continue // shouldn't happen; just in case
|
||||
}
|
||||
eak, err := db.getDBExternalAccountKey(ctx, eakID)
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "error retrieving ACME EAB Key for provisioner %s and keyID %s", provisionerID, eakID)
|
||||
}
|
||||
}
|
||||
keys = append(keys, &acme.ExternalAccountKey{
|
||||
ID: eak.ID,
|
||||
KeyBytes: eak.KeyBytes,
|
||||
ProvisionerID: eak.ProvisionerID,
|
||||
Reference: eak.Reference,
|
||||
AccountID: eak.AccountID,
|
||||
CreatedAt: eak.CreatedAt,
|
||||
BoundAt: eak.BoundAt,
|
||||
})
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeyByReference retrieves an External Account Binding key with unique reference
|
||||
func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerID, reference string) (*acme.ExternalAccountKey, error) {
|
||||
if reference == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
k, err := db.db.Get(externalAccountKeysByReferenceTable, []byte(referenceKey(provisionerID, reference)))
|
||||
if nosqlDB.IsErrNotFound(err) {
|
||||
return nil, acme.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading ACME EAB key for reference %s", reference)
|
||||
}
|
||||
dbExternalAccountKeyReference := new(dbExternalAccountKeyReference)
|
||||
if err := json.Unmarshal(k, dbExternalAccountKeyReference); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling ACME EAB key for reference %s", reference)
|
||||
}
|
||||
|
||||
return db.GetExternalAccountKey(ctx, provisionerID, dbExternalAccountKeyReference.ExternalAccountKeyID)
|
||||
}
|
||||
|
||||
func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *acme.ExternalAccountKey) error {
|
||||
old, err := db.getDBExternalAccountKey(ctx, eak.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if old.ProvisionerID != provisionerID {
|
||||
return errors.New("provisioner does not match provisioner for which the EAB key was created")
|
||||
}
|
||||
|
||||
if old.ProvisionerID != eak.ProvisionerID {
|
||||
return errors.New("cannot change provisioner for an existing ACME EAB Key")
|
||||
}
|
||||
|
||||
if old.Reference != eak.Reference {
|
||||
return errors.New("cannot change reference for an existing ACME EAB Key")
|
||||
}
|
||||
|
||||
nu := dbExternalAccountKey{
|
||||
ID: eak.ID,
|
||||
ProvisionerID: eak.ProvisionerID,
|
||||
Reference: eak.Reference,
|
||||
AccountID: eak.AccountID,
|
||||
KeyBytes: eak.KeyBytes,
|
||||
CreatedAt: eak.CreatedAt,
|
||||
BoundAt: eak.BoundAt,
|
||||
}
|
||||
|
||||
return db.save(ctx, nu.ID, nu, old, "external_account_key", externalAccountKeyTable)
|
||||
}
|
||||
|
||||
func (db *DB) addEAKID(ctx context.Context, provisionerID, eakID string) error {
|
||||
referencesByProvisionerIndexMux.Lock()
|
||||
defer referencesByProvisionerIndexMux.Unlock()
|
||||
|
||||
if eakID == "" {
|
||||
return errors.Errorf("can't add empty eakID for provisioner %s", provisionerID)
|
||||
}
|
||||
|
||||
var eakIDs []string
|
||||
b, err := db.db.Get(externalAccountKeyIDsByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return errors.Wrapf(err, "error loading eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
// it may happen that no record is found; we'll continue with an empty slice
|
||||
} else {
|
||||
if err := json.Unmarshal(b, &eakIDs); err != nil {
|
||||
return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range eakIDs {
|
||||
if id == eakID {
|
||||
// return an error when a duplicate ID is found
|
||||
return errors.Errorf("eakID %s already exists for provisioner %s", eakID, provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
var newEAKIDs []string
|
||||
newEAKIDs = append(newEAKIDs, eakIDs...)
|
||||
newEAKIDs = append(newEAKIDs, eakID)
|
||||
var (
|
||||
_old interface{} = eakIDs
|
||||
_new interface{} = newEAKIDs
|
||||
)
|
||||
|
||||
if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeyIDsByProvisionerID", externalAccountKeyIDsByProvisionerIDTable); err != nil {
|
||||
return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) deleteEAKID(ctx context.Context, provisionerID, eakID string) error {
|
||||
referencesByProvisionerIndexMux.Lock()
|
||||
defer referencesByProvisionerIndexMux.Unlock()
|
||||
|
||||
var eakIDs []string
|
||||
b, err := db.db.Get(externalAccountKeyIDsByProvisionerIDTable, []byte(provisionerID))
|
||||
if err != nil {
|
||||
if !nosqlDB.IsErrNotFound(err) {
|
||||
return errors.Wrapf(err, "error loading eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
// it may happen that no record is found; we'll continue with an empty slice
|
||||
} else {
|
||||
if err := json.Unmarshal(b, &eakIDs); err != nil {
|
||||
return errors.Wrapf(err, "error unmarshaling eakIDs for provisioner %s", provisionerID)
|
||||
}
|
||||
}
|
||||
|
||||
newEAKIDs := removeElement(eakIDs, eakID)
|
||||
var (
|
||||
_old interface{} = eakIDs
|
||||
_new interface{} = newEAKIDs
|
||||
)
|
||||
|
||||
if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeyIDsByProvisionerID", externalAccountKeyIDsByProvisionerIDTable); err != nil {
|
||||
return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// referenceKey returns a unique key for a reference per provisioner
|
||||
func referenceKey(provisionerID, reference string) string {
|
||||
return provisionerID + "." + reference
|
||||
}
|
||||
|
||||
// sliceIndex finds the index of item in slice
|
||||
func sliceIndex(slice []string, item string) int {
|
||||
for i := range slice {
|
||||
if slice[i] == item {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// removeElement deletes the item if it exists in the
|
||||
// slice. It returns a new slice, keeping the old one intact.
|
||||
func removeElement(slice []string, item string) []string {
|
||||
|
||||
newSlice := make([]string, 0)
|
||||
index := sliceIndex(slice, item)
|
||||
if index < 0 {
|
||||
newSlice = append(newSlice, slice...)
|
||||
return newSlice
|
||||
}
|
||||
|
||||
newSlice = append(newSlice, slice[:index]...)
|
||||
|
||||
return append(newSlice, slice[index+1:]...)
|
||||
}
|
1710
acme/db/nosql/eab_test.go
Normal file
1710
acme/db/nosql/eab_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -11,18 +11,18 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
accountTable = []byte("acme_accounts")
|
||||
accountByKeyIDTable = []byte("acme_keyID_accountID_index")
|
||||
authzTable = []byte("acme_authzs")
|
||||
challengeTable = []byte("acme_challenges")
|
||||
nonceTable = []byte("nonces")
|
||||
orderTable = []byte("acme_orders")
|
||||
ordersByAccountIDTable = []byte("acme_account_orders_index")
|
||||
certTable = []byte("acme_certs")
|
||||
certBySerialTable = []byte("acme_serial_certs_index")
|
||||
externalAccountKeyTable = []byte("acme_external_account_keys")
|
||||
externalAccountKeysByReferenceTable = []byte("acme_external_account_key_reference_index")
|
||||
externalAccountKeysByProvisionerIDTable = []byte("acme_external_account_keyID_provisionerID_index")
|
||||
accountTable = []byte("acme_accounts")
|
||||
accountByKeyIDTable = []byte("acme_keyID_accountID_index")
|
||||
authzTable = []byte("acme_authzs")
|
||||
challengeTable = []byte("acme_challenges")
|
||||
nonceTable = []byte("nonces")
|
||||
orderTable = []byte("acme_orders")
|
||||
ordersByAccountIDTable = []byte("acme_account_orders_index")
|
||||
certTable = []byte("acme_certs")
|
||||
certBySerialTable = []byte("acme_serial_certs_index")
|
||||
externalAccountKeyTable = []byte("acme_external_account_keys")
|
||||
externalAccountKeysByReferenceTable = []byte("acme_external_account_key_reference_index")
|
||||
externalAccountKeyIDsByProvisionerIDTable = []byte("acme_external_account_keyID_provisionerID_index")
|
||||
)
|
||||
|
||||
// DB is a struct that implements the AcmeDB interface.
|
||||
|
@ -35,7 +35,7 @@ func New(db nosqlDB.DB) (*DB, error) {
|
|||
tables := [][]byte{accountTable, accountByKeyIDTable, authzTable,
|
||||
challengeTable, nonceTable, orderTable, ordersByAccountIDTable,
|
||||
certTable, certBySerialTable, externalAccountKeyTable,
|
||||
externalAccountKeysByReferenceTable, externalAccountKeysByProvisionerIDTable,
|
||||
externalAccountKeysByReferenceTable, externalAccountKeyIDsByProvisionerIDTable,
|
||||
}
|
||||
for _, b := range tables {
|
||||
if err := db.CreateTable(b); err != nil {
|
||||
|
|
|
@ -391,6 +391,54 @@ func TestHandler_provisionerHasEABEnabled(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_provisionerFromContext(t *testing.T) {
|
||||
prov := &linkedca.Provisioner{
|
||||
Id: "provID",
|
||||
Name: "acmeProv",
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
want *linkedca.Provisioner
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "fail/no-provisioner",
|
||||
ctx: context.Background(),
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "fail/wrong-type",
|
||||
ctx: context.WithValue(context.Background(), provisionerContextKey, "prov"),
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
ctx: context.WithValue(context.Background(), provisionerContextKey, prov),
|
||||
want: &linkedca.Provisioner{
|
||||
Id: "provID",
|
||||
Name: "acmeProv",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := provisionerFromContext(tt.ctx)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("provisionerFromContext() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
opts := []cmp.Option{cmpopts.IgnoreUnexported(linkedca.Provisioner{})}
|
||||
if !cmp.Equal(tt.want, got, opts...) {
|
||||
t.Errorf("provisionerFromContext() diff =\n %s", cmp.Diff(tt.want, got, opts...))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateExternalAccountKeyRequest_Validate(t *testing.T) {
|
||||
type fields struct {
|
||||
Reference string
|
||||
|
@ -488,6 +536,28 @@ func TestHandler_CreateExternalAccountKey(t *testing.T) {
|
|||
},
|
||||
}
|
||||
},
|
||||
"fail/no-provisioner-in-context": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||
req := CreateExternalAccountKeyRequest{
|
||||
Reference: "aRef",
|
||||
}
|
||||
body, err := json.Marshal(req)
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
body: body,
|
||||
statusCode: 500,
|
||||
eak: nil,
|
||||
err: &admin.Error{
|
||||
Type: admin.ErrorServerInternalType.String(),
|
||||
Status: 500,
|
||||
Detail: "the server experienced an internal error",
|
||||
Message: "error getting provisioner from context: provisioner expected in request context",
|
||||
},
|
||||
}
|
||||
},
|
||||
"fail/acmeDB.GetExternalAccountKeyByReference": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
|
@ -759,6 +829,21 @@ func TestHandler_DeleteExternalAccountKey(t *testing.T) {
|
|||
err *admin.Error
|
||||
}
|
||||
var tests = map[string]func(t *testing.T) test{
|
||||
"fail/no-provisioner-in-context": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
err: &admin.Error{
|
||||
Type: admin.ErrorServerInternalType.String(),
|
||||
Status: 500,
|
||||
Detail: "the server experienced an internal error",
|
||||
Message: "error getting provisioner from context: provisioner expected in request context",
|
||||
},
|
||||
}
|
||||
},
|
||||
"fail/acmeDB.DeleteExternalAccountKey": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
|
@ -861,6 +946,23 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
err *admin.Error
|
||||
}
|
||||
var tests = map[string]func(t *testing.T) test{
|
||||
"fail/no-provisioner-in-context": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||
req := httptest.NewRequest("GET", "/foo", nil)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 500,
|
||||
req: req,
|
||||
err: &admin.Error{
|
||||
Type: admin.ErrorServerInternalType.String(),
|
||||
Status: 500,
|
||||
Detail: "the server experienced an internal error",
|
||||
Message: "error getting provisioner from context: provisioner expected in request context",
|
||||
},
|
||||
}
|
||||
},
|
||||
"fail/acmeDB.GetExternalAccountKeyByReference": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
|
|
Loading…
Reference in a new issue