forked from TrueCloudLab/certificates
1710 lines
51 KiB
Go
1710 lines
51 KiB
Go
package nosql
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/assert"
|
|
"github.com/smallstep/certificates/acme"
|
|
certdb "github.com/smallstep/certificates/db"
|
|
"github.com/smallstep/nosql"
|
|
nosqldb "github.com/smallstep/nosql/database"
|
|
)
|
|
|
|
func TestDB_getDBExternalAccountKey(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
dbeak *dbExternalAccountKey
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: nil,
|
|
dbeak: dbeak,
|
|
}
|
|
},
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
err: acme.ErrNotFound,
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading external account key keyID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling external account key keyID into dbExternalAccountKey"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if dbeak, err := d.getDBExternalAccountKey(context.Background(), keyID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, dbeak.ID, tc.dbeak.ID)
|
|
assert.Equals(t, dbeak.KeyBytes, tc.dbeak.KeyBytes)
|
|
assert.Equals(t, dbeak.ProvisionerID, tc.dbeak.ProvisionerID)
|
|
assert.Equals(t, dbeak.Reference, tc.dbeak.Reference)
|
|
assert.Equals(t, dbeak.CreatedAt, tc.dbeak.CreatedAt)
|
|
assert.Equals(t, dbeak.AccountID, tc.dbeak.AccountID)
|
|
assert.Equals(t, dbeak.BoundAt, tc.dbeak.BoundAt)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetExternalAccountKey(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
eak *acme.ExternalAccountKey
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
},
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading external account key keyID: force"),
|
|
}
|
|
},
|
|
"fail/non-matching-provisioner": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: "aDifferentProvID",
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
},
|
|
acmeErr: acme.NewError(acme.ErrorUnauthorizedType, "provisioner does not match provisioner for which the EAB key was created"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if eak, err := d.GetExternalAccountKey(context.Background(), provID, keyID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, eak.ID, tc.eak.ID)
|
|
assert.Equals(t, eak.KeyBytes, tc.eak.KeyBytes)
|
|
assert.Equals(t, eak.ProvisionerID, tc.eak.ProvisionerID)
|
|
assert.Equals(t, eak.Reference, tc.eak.Reference)
|
|
assert.Equals(t, eak.CreatedAt, tc.eak.CreatedAt)
|
|
assert.Equals(t, eak.AccountID, tc.eak.AccountID)
|
|
assert.Equals(t, eak.BoundAt, tc.eak.BoundAt)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetExternalAccountKeyByReference(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
ref := "ref"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
ref string
|
|
acmeErr *acme.Error
|
|
eak *acme.ExternalAccountKey
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
ref: ref,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
"ok/no-reference": func(t *testing.T) test {
|
|
return test{
|
|
ref: "",
|
|
eak: nil,
|
|
err: nil,
|
|
}
|
|
},
|
|
"fail/reference-not-found": func(t *testing.T) test {
|
|
return test{
|
|
ref: ref,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
err: errors.New("not found"),
|
|
}
|
|
},
|
|
"fail/reference-load-error": func(t *testing.T) test {
|
|
return test{
|
|
ref: ref,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading ACME EAB key for reference ref: force"),
|
|
}
|
|
},
|
|
"fail/reference-unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
ref: ref,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeysByReferenceTable))
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return []byte{0}, nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling ACME EAB key for reference ref"),
|
|
}
|
|
},
|
|
"fail/db.GetExternalAccountKey-error": func(t *testing.T) test {
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
ref: ref,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil, errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error loading external account key keyID: force"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if eak, err := d.GetExternalAccountKeyByReference(context.Background(), provID, tc.ref); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) && tc.eak != nil {
|
|
assert.Equals(t, eak.ID, tc.eak.ID)
|
|
assert.Equals(t, eak.AccountID, tc.eak.AccountID)
|
|
assert.Equals(t, eak.BoundAt, tc.eak.BoundAt)
|
|
assert.Equals(t, eak.CreatedAt, tc.eak.CreatedAt)
|
|
assert.Equals(t, eak.KeyBytes, tc.eak.KeyBytes)
|
|
assert.Equals(t, eak.ProvisionerID, tc.eak.ProvisionerID)
|
|
assert.Equals(t, eak.Reference, tc.eak.Reference)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetExternalAccountKeys(t *testing.T) {
|
|
keyID1 := "keyID1"
|
|
keyID2 := "keyID2"
|
|
keyID3 := "keyID3"
|
|
provID := "provID"
|
|
ref := "ref"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
eaks []*acme.ExternalAccountKey
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak1 := &dbExternalAccountKey{
|
|
ID: keyID1,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b1, err := json.Marshal(dbeak1)
|
|
assert.FatalError(t, err)
|
|
dbeak2 := &dbExternalAccountKey{
|
|
ID: keyID2,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b2, err := json.Marshal(dbeak2)
|
|
assert.FatalError(t, err)
|
|
dbeak3 := &dbExternalAccountKey{
|
|
ID: keyID3,
|
|
ProvisionerID: "aDifferentProvID",
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b3, err := json.Marshal(dbeak3)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
keys := []string{"", keyID1, keyID2} // includes an empty keyID
|
|
b, err := json.Marshal(keys)
|
|
assert.FatalError(t, err)
|
|
return b, nil
|
|
case string(externalAccountKeyTable):
|
|
switch string(key) {
|
|
case keyID1:
|
|
return b1, nil
|
|
case keyID2:
|
|
return b2, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected key %s", string(key)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
// TODO: remove the MList
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeyTable):
|
|
return []*nosqldb.Entry{
|
|
{
|
|
Bucket: bucket,
|
|
Key: []byte(keyID1),
|
|
Value: b1,
|
|
},
|
|
{
|
|
Bucket: bucket,
|
|
Key: []byte(keyID2),
|
|
Value: b2,
|
|
},
|
|
{
|
|
Bucket: bucket,
|
|
Key: []byte(keyID3),
|
|
Value: b3,
|
|
},
|
|
}, nil
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
keys := []string{keyID1, keyID2}
|
|
b, err := json.Marshal(keys)
|
|
assert.FatalError(t, err)
|
|
return []*nosqldb.Entry{
|
|
{
|
|
Bucket: bucket,
|
|
Key: []byte(provID),
|
|
Value: b,
|
|
},
|
|
}, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
eaks: []*acme.ExternalAccountKey{
|
|
{
|
|
ID: keyID1,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
},
|
|
{
|
|
ID: keyID2,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
},
|
|
},
|
|
}
|
|
},
|
|
"fail/db.Get-externalAccountKeysByProvisionerIDTable": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyIDsByProvisionerIDTable))
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading ACME EAB Key IDs for provisioner provID: force"),
|
|
}
|
|
},
|
|
"fail/db.Get-externalAccountKeysByProvisionerIDTable-unmarshal": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyIDsByProvisionerIDTable))
|
|
b, _ := json.Marshal(1)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling ACME EAB Key IDs for provisioner provID: json: cannot unmarshal number into Go value of type []string"),
|
|
}
|
|
},
|
|
"fail/db.getDBExternalAccountKey": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
keys := []string{keyID1, keyID2}
|
|
b, err := json.Marshal(keys)
|
|
assert.FatalError(t, err)
|
|
return b, nil
|
|
case string(externalAccountKeyTable):
|
|
return nil, errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force bucket")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error retrieving ACME EAB Key for provisioner provID and keyID keyID1: error loading external account key keyID1: force"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if eaks, err := d.GetExternalAccountKeys(context.Background(), provID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.Equals(t, tc.err.Error(), err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, len(eaks), len(tc.eaks))
|
|
for i, eak := range eaks {
|
|
assert.Equals(t, eak.ID, tc.eaks[i].ID)
|
|
assert.Equals(t, eak.KeyBytes, tc.eaks[i].KeyBytes)
|
|
assert.Equals(t, eak.ProvisionerID, tc.eaks[i].ProvisionerID)
|
|
assert.Equals(t, eak.Reference, tc.eaks[i].Reference)
|
|
assert.Equals(t, eak.CreatedAt, tc.eaks[i].CreatedAt)
|
|
assert.Equals(t, eak.AccountID, tc.eaks[i].AccountID)
|
|
assert.Equals(t, eak.BoundAt, tc.eaks[i].BoundAt)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_DeleteExternalAccountKey(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
ref := "ref"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
assert.Equals(t, provID, string(key))
|
|
b, err := json.Marshal([]string{keyID})
|
|
assert.FatalError(t, err)
|
|
return b, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
MDel: func(bucket, key []byte) error {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return errors.New("force default")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, new []byte) ([]byte, bool, error) {
|
|
fmt.Println(string(bucket))
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, provID+"."+ref, string(key))
|
|
return nil, true, nil
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
assert.Equals(t, provID, string(key))
|
|
return nil, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
}
|
|
},
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyTable))
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
err: errors.New("error loading ACME EAB Key with Key ID keyID: not found"),
|
|
}
|
|
},
|
|
"fail/non-matching-provisioner": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: "aDifferentProvID",
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyTable))
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("provisioner does not match provisioner for which the EAB key was created"),
|
|
}
|
|
},
|
|
"fail/delete-reference": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
MDel: func(bucket, key []byte) error {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return errors.New("force")
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error deleting ACME EAB Key reference with Key ID keyID and reference ref: force"),
|
|
}
|
|
},
|
|
"fail/delete-eak": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
MDel: func(bucket, key []byte) error {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error deleting ACME EAB Key with Key ID keyID: force"),
|
|
}
|
|
},
|
|
"fail/delete-eakID": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
dbref := &dbExternalAccountKeyReference{
|
|
Reference: ref,
|
|
ExternalAccountKeyID: keyID,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
dbrefBytes, err := json.Marshal(dbref)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), ref)
|
|
return dbrefBytes, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
return b, errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force default")
|
|
}
|
|
},
|
|
MDel: func(bucket, key []byte) error {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), provID+"."+ref)
|
|
return nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, string(key), keyID)
|
|
return nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error removing ACME EAB Key ID keyID: error loading eakIDs for provisioner provID: force"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if err := d.DeleteExternalAccountKey(context.Background(), provID, keyID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.Equals(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
assert.Nil(t, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_CreateExternalAccountKey(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
ref := "ref"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
_id *string
|
|
eak *acme.ExternalAccountKey
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
var (
|
|
id string
|
|
idPtr = &id
|
|
)
|
|
now := clock.Now()
|
|
eak := &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "ref",
|
|
AccountID: "",
|
|
CreatedAt: now,
|
|
}
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyIDsByProvisionerIDTable))
|
|
assert.Equals(t, provID, string(key))
|
|
b, _ := json.Marshal([]string{})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
assert.Equals(t, provID, string(key))
|
|
return nu, true, nil
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, provID+"."+ref, string(key))
|
|
assert.Equals(t, nil, old)
|
|
return nu, true, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, nil, old)
|
|
|
|
id = string(key)
|
|
|
|
dbeak := new(dbExternalAccountKey)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbeak))
|
|
assert.Equals(t, string(key), dbeak.ID)
|
|
assert.Equals(t, eak.ProvisionerID, dbeak.ProvisionerID)
|
|
assert.Equals(t, eak.Reference, dbeak.Reference)
|
|
assert.Equals(t, 32, len(dbeak.KeyBytes))
|
|
assert.False(t, dbeak.CreatedAt.IsZero())
|
|
assert.Equals(t, dbeak.AccountID, eak.AccountID)
|
|
assert.True(t, dbeak.BoundAt.IsZero())
|
|
return nu, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
eak: eak,
|
|
_id: idPtr,
|
|
}
|
|
},
|
|
"fail/externalAccountKeyID-cmpAndSwap-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), ref)
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error saving acme external_account_key: force"),
|
|
}
|
|
},
|
|
"fail/addEAKID-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyIDsByProvisionerIDTable))
|
|
assert.Equals(t, provID, string(key))
|
|
return nil, errors.New("force")
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, string(key), ref)
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, nil
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error loading eakIDs for provisioner provID: force"),
|
|
}
|
|
},
|
|
"fail/externalAccountKeyReference-cmpAndSwap-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(externalAccountKeyIDsByProvisionerIDTable))
|
|
assert.Equals(t, provID, string(key))
|
|
b, _ := json.Marshal([]string{})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(externalAccountKeyIDsByProvisionerIDTable):
|
|
assert.Equals(t, provID, string(key))
|
|
return nu, true, nil
|
|
case string(externalAccountKeysByReferenceTable):
|
|
assert.Equals(t, provID+"."+ref, string(key))
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, errors.New("force")
|
|
case string(externalAccountKeyTable):
|
|
assert.Equals(t, old, nil)
|
|
return nu, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force default")
|
|
}
|
|
},
|
|
},
|
|
err: errors.New("error saving acme external_account_key_reference: force"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
eak, err := d.CreateExternalAccountKey(context.Background(), provID, ref)
|
|
if err != nil {
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.Equals(t, err.Error(), tc.err.Error())
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, *tc._id, eak.ID)
|
|
assert.Equals(t, provID, eak.ProvisionerID)
|
|
assert.Equals(t, ref, eak.Reference)
|
|
assert.Equals(t, "", eak.AccountID)
|
|
assert.False(t, eak.CreatedAt.IsZero())
|
|
assert.False(t, eak.AlreadyBound())
|
|
assert.True(t, eak.BoundAt.IsZero())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_UpdateExternalAccountKey(t *testing.T) {
|
|
keyID := "keyID"
|
|
provID := "provID"
|
|
ref := "ref"
|
|
now := clock.Now()
|
|
dbeak := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dbeak)
|
|
assert.FatalError(t, err)
|
|
type test struct {
|
|
db nosql.DB
|
|
eak *acme.ExternalAccountKey
|
|
err error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
|
|
"ok": func(t *testing.T) test {
|
|
eak := &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
return test{
|
|
eak: eak,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, old, b)
|
|
|
|
dbNew := new(dbExternalAccountKey)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbNew))
|
|
assert.Equals(t, dbNew.ID, dbeak.ID)
|
|
assert.Equals(t, dbNew.ProvisionerID, dbeak.ProvisionerID)
|
|
assert.Equals(t, dbNew.Reference, dbeak.Reference)
|
|
assert.Equals(t, dbNew.AccountID, dbeak.AccountID)
|
|
assert.Equals(t, dbNew.CreatedAt, dbeak.CreatedAt)
|
|
assert.Equals(t, dbNew.BoundAt, dbeak.BoundAt)
|
|
assert.Equals(t, dbNew.KeyBytes, dbeak.KeyBytes)
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
},
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading external account key keyID: force"),
|
|
}
|
|
},
|
|
"fail/provisioner-mismatch": func(t *testing.T) test {
|
|
newDBEAK := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: "aDifferentProvID",
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(newDBEAK)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
},
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("provisioner does not match provisioner for which the EAB key was created"),
|
|
}
|
|
},
|
|
"fail/provisioner-change": func(t *testing.T) test {
|
|
newDBEAK := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(newDBEAK)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: "aDifferentProvisionerID",
|
|
},
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("cannot change provisioner for an existing ACME EAB Key"),
|
|
}
|
|
},
|
|
"fail/reference-change": func(t *testing.T) test {
|
|
newDBEAK := &dbExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: ref,
|
|
AccountID: "",
|
|
KeyBytes: []byte{1, 3, 3, 7},
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(newDBEAK)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
eak: &acme.ExternalAccountKey{
|
|
ID: keyID,
|
|
ProvisionerID: provID,
|
|
Reference: "aDifferentReference",
|
|
},
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyTable)
|
|
assert.Equals(t, string(key), keyID)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("cannot change reference for an existing ACME EAB Key"),
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if err := d.UpdateExternalAccountKey(context.Background(), provID, tc.eak); err != nil {
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, dbeak.ID, tc.eak.ID)
|
|
assert.Equals(t, dbeak.ProvisionerID, tc.eak.ProvisionerID)
|
|
assert.Equals(t, dbeak.Reference, tc.eak.Reference)
|
|
assert.Equals(t, dbeak.AccountID, tc.eak.AccountID)
|
|
assert.Equals(t, dbeak.CreatedAt, tc.eak.CreatedAt)
|
|
assert.Equals(t, dbeak.BoundAt, tc.eak.BoundAt)
|
|
assert.Equals(t, dbeak.KeyBytes, tc.eak.KeyBytes)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_addEAKID(t *testing.T) {
|
|
provID := "provID"
|
|
eakID := "eakID"
|
|
type test struct {
|
|
ctx context.Context
|
|
provisionerID string
|
|
eakID string
|
|
db nosql.DB
|
|
err error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/empty-eakID": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: "",
|
|
err: errors.New("can't add empty eakID for provisioner provID"),
|
|
}
|
|
},
|
|
"fail/db.Get": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading eakIDs for provisioner provID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal(1)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling eakIDs for provisioner provID: json: cannot unmarshal number into Go value of type []string"),
|
|
}
|
|
},
|
|
"fail/eakID-already-exists": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal([]string{eakID})
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("eakID eakID already exists for provisioner provID"),
|
|
}
|
|
},
|
|
"fail/db.save": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal([]string{"id1"})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
oldB, _ := json.Marshal([]string{"id1"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"id1", eakID})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving eakIDs index for provisioner provID: error saving acme externalAccountKeyIDsByProvisionerID: force"),
|
|
}
|
|
},
|
|
"ok/db.Get-not-found": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
assert.Equals(t, old, []byte{110, 117, 108, 108})
|
|
b, _ := json.Marshal([]string{eakID})
|
|
assert.Equals(t, nu, b)
|
|
return b, true, nil
|
|
},
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal([]string{"id1", "id2"})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
oldB, _ := json.Marshal([]string{"id1", "id2"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"id1", "id2", eakID})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
},
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := &DB{
|
|
db: tc.db,
|
|
}
|
|
wantErr := tc.err != nil
|
|
err := db.addEAKID(tc.ctx, tc.provisionerID, tc.eakID)
|
|
if (err != nil) != wantErr {
|
|
t.Errorf("DB.addEAKID() error = %v, wantErr %v", err, wantErr)
|
|
}
|
|
if err != nil {
|
|
assert.Equals(t, tc.err.Error(), err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_deleteEAKID(t *testing.T) {
|
|
provID := "provID"
|
|
eakID := "eakID"
|
|
type test struct {
|
|
ctx context.Context
|
|
provisionerID string
|
|
eakID string
|
|
db nosql.DB
|
|
err error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/db.Get": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading eakIDs for provisioner provID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal(1)
|
|
return b, nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling eakIDs for provisioner provID: json: cannot unmarshal number into Go value of type []string"),
|
|
}
|
|
},
|
|
"fail/db.save": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal([]string{"id1", eakID})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
oldB, _ := json.Marshal([]string{"id1", eakID})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"id1"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving eakIDs index for provisioner provID: error saving acme externalAccountKeyIDsByProvisionerID: force"),
|
|
}
|
|
},
|
|
"ok/db.Get-not-found": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
assert.Equals(t, old, []byte{110, 117, 108, 108})
|
|
b, _ := json.Marshal([]string{})
|
|
assert.Equals(t, nu, b)
|
|
return b, true, nil
|
|
},
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
provisionerID: provID,
|
|
eakID: eakID,
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
b, _ := json.Marshal([]string{"id1", eakID, "id2"})
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
oldB, _ := json.Marshal([]string{"id1", eakID, "id2"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"id1", "id2"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
},
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := &DB{
|
|
db: tc.db,
|
|
}
|
|
wantErr := tc.err != nil
|
|
err := db.deleteEAKID(tc.ctx, tc.provisionerID, tc.eakID)
|
|
if (err != nil) != wantErr {
|
|
t.Errorf("DB.deleteEAKID() error = %v, wantErr %v", err, wantErr)
|
|
}
|
|
if err != nil {
|
|
assert.Equals(t, tc.err.Error(), err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_addAndDeleteEAKID(t *testing.T) {
|
|
provID := "provID"
|
|
callCounter := 0
|
|
type test struct {
|
|
ctx context.Context
|
|
db nosql.DB
|
|
err error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok/multi": func(t *testing.T) test {
|
|
return test{
|
|
ctx: context.Background(),
|
|
db: &certdb.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
switch callCounter {
|
|
case 0:
|
|
return nil, nosqldb.ErrNotFound
|
|
case 1:
|
|
b, _ := json.Marshal([]string{"eakID"})
|
|
return b, nil
|
|
case 2:
|
|
b, _ := json.Marshal([]string{})
|
|
return b, nil
|
|
case 3:
|
|
b, _ := json.Marshal([]string{"eakID1"})
|
|
return b, nil
|
|
case 4:
|
|
b, _ := json.Marshal([]string{"eakID1", "eakID2"})
|
|
return b, nil
|
|
case 5:
|
|
b, _ := json.Marshal([]string{"eakID2"})
|
|
return b, nil
|
|
default:
|
|
assert.FatalError(t, errors.New("unexpected get iteration"))
|
|
return nil, errors.New("force get default")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, externalAccountKeyIDsByProvisionerIDTable)
|
|
assert.Equals(t, string(key), provID)
|
|
switch callCounter {
|
|
case 0:
|
|
assert.Equals(t, old, []byte{110, 117, 108, 108})
|
|
newB, _ := json.Marshal([]string{"eakID"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
case 1:
|
|
oldB, _ := json.Marshal([]string{"eakID"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{})
|
|
return newB, true, nil
|
|
case 2:
|
|
oldB, _ := json.Marshal([]string{})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"eakID1"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
case 3:
|
|
oldB, _ := json.Marshal([]string{"eakID1"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"eakID1", "eakID2"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
case 4:
|
|
oldB, _ := json.Marshal([]string{"eakID1", "eakID2"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{"eakID2"})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
case 5:
|
|
oldB, _ := json.Marshal([]string{"eakID2"})
|
|
assert.Equals(t, old, oldB)
|
|
newB, _ := json.Marshal([]string{})
|
|
assert.Equals(t, nu, newB)
|
|
return newB, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.New("unexpected get iteration"))
|
|
return nil, true, errors.New("force save default")
|
|
}
|
|
},
|
|
},
|
|
err: nil,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
// goal of this test is to simulate multiple calls; no errors expected.
|
|
|
|
db := &DB{
|
|
db: tc.db,
|
|
}
|
|
|
|
err := db.addEAKID(tc.ctx, provID, "eakID")
|
|
if err != nil {
|
|
t.Errorf("DB.addEAKID() error = %v", err)
|
|
}
|
|
|
|
callCounter++
|
|
err = db.deleteEAKID(tc.ctx, provID, "eakID")
|
|
if err != nil {
|
|
t.Errorf("DB.deleteEAKID() error = %v", err)
|
|
}
|
|
|
|
callCounter++
|
|
err = db.addEAKID(tc.ctx, provID, "eakID1")
|
|
if err != nil {
|
|
t.Errorf("DB.addEAKID() error = %v", err)
|
|
}
|
|
|
|
callCounter++
|
|
err = db.addEAKID(tc.ctx, provID, "eakID2")
|
|
if err != nil {
|
|
t.Errorf("DB.addEAKID() error = %v", err)
|
|
}
|
|
|
|
callCounter++
|
|
err = db.deleteEAKID(tc.ctx, provID, "eakID1")
|
|
if err != nil {
|
|
t.Errorf("DB.deleteEAKID() error = %v", err)
|
|
}
|
|
|
|
callCounter++
|
|
err = db.deleteEAKID(tc.ctx, provID, "eakID2")
|
|
if err != nil {
|
|
t.Errorf("DB.deleteAKID() error = %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_removeElement(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
slice []string
|
|
item string
|
|
want []string
|
|
}{
|
|
{
|
|
name: "remove-first",
|
|
slice: []string{"id1", "id2", "id3"},
|
|
item: "id1",
|
|
want: []string{"id2", "id3"},
|
|
},
|
|
{
|
|
name: "remove-last",
|
|
slice: []string{"id1", "id2", "id3"},
|
|
item: "id3",
|
|
want: []string{"id1", "id2"},
|
|
},
|
|
{
|
|
name: "remove-middle",
|
|
slice: []string{"id1", "id2", "id3"},
|
|
item: "id2",
|
|
want: []string{"id1", "id3"},
|
|
},
|
|
{
|
|
name: "remove-non-existing",
|
|
slice: []string{"id1", "id2", "id3"},
|
|
item: "none",
|
|
want: []string{"id1", "id2", "id3"},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := removeElement(tt.slice, tt.item)
|
|
if !cmp.Equal(tt.want, got) {
|
|
t.Errorf("removeElement() diff =\n %s", cmp.Diff(tt.want, got))
|
|
}
|
|
})
|
|
}
|
|
}
|