1120 lines
32 KiB
Go
1120 lines
32 KiB
Go
package nosql
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/assert"
|
|
"github.com/smallstep/certificates/authority/admin"
|
|
"github.com/smallstep/certificates/db"
|
|
"github.com/smallstep/nosql"
|
|
nosqldb "github.com/smallstep/nosql/database"
|
|
"go.step.sm/linkedca"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
func TestDB_getDBAdminBytes(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admin adminID: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db}
|
|
if b, err := db.getDBAdminBytes(context.Background(), adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.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, string(b), "foo")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_getDBAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
dba *dbAdmin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admin adminID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling admin adminID into dbAdmin"),
|
|
}
|
|
},
|
|
"fail/deleted": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: now,
|
|
DeletedAt: now,
|
|
}
|
|
b, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: now,
|
|
}
|
|
b, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
dba: dba,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if dba, err := db.getDBAdmin(context.Background(), adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) {
|
|
assert.Equals(t, dba.ID, adminID)
|
|
assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID)
|
|
assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID)
|
|
assert.Equals(t, dba.Subject, tc.dba.Subject)
|
|
assert.Equals(t, dba.Type, tc.dba.Type)
|
|
assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt)
|
|
assert.Fatal(t, dba.DeletedAt.IsZero())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_unmarshalDBAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
in []byte
|
|
err error
|
|
adminErr *admin.Error
|
|
dba *dbAdmin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
in: []byte("foo"),
|
|
err: errors.New("error unmarshaling admin adminID into dbAdmin"),
|
|
}
|
|
},
|
|
"fail/deleted-error": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
DeletedAt: time.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
in: data,
|
|
adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"),
|
|
}
|
|
},
|
|
"fail/authority-mismatch-error": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: "foo",
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
in: data,
|
|
adminErr: admin.NewError(admin.ErrorAuthorityMismatchType,
|
|
"admin %s is not owned by authority %s", adminID, admin.DefaultAuthorityID),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
Subject: "max@smallstep.com",
|
|
ProvisionerID: "provID",
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
in: data,
|
|
dba: dba,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{authorityID: admin.DefaultAuthorityID}
|
|
if dba, err := db.unmarshalDBAdmin(tc.in, adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) {
|
|
assert.Equals(t, dba.ID, adminID)
|
|
assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID)
|
|
assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID)
|
|
assert.Equals(t, dba.Subject, tc.dba.Subject)
|
|
assert.Equals(t, dba.Type, tc.dba.Type)
|
|
assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt)
|
|
assert.Fatal(t, dba.DeletedAt.IsZero())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_unmarshalAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
in []byte
|
|
err error
|
|
adminErr *admin.Error
|
|
dba *dbAdmin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
in: []byte("foo"),
|
|
err: errors.New("error unmarshaling admin adminID into dbAdmin"),
|
|
}
|
|
},
|
|
"fail/deleted-error": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
DeletedAt: time.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
in: data,
|
|
adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
Subject: "max@smallstep.com",
|
|
ProvisionerID: "provID",
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
in: data,
|
|
dba: dba,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{authorityID: admin.DefaultAuthorityID}
|
|
if adm, err := db.unmarshalAdmin(tc.in, adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) {
|
|
assert.Equals(t, adm.Id, adminID)
|
|
assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID)
|
|
assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID)
|
|
assert.Equals(t, adm.Subject, tc.dba.Subject)
|
|
assert.Equals(t, adm.Type, tc.dba.Type)
|
|
assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt))
|
|
assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
dba *dbAdmin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admin adminID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling admin adminID into dbAdmin"),
|
|
}
|
|
},
|
|
"fail/deleted": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
DeletedAt: clock.Now(),
|
|
}
|
|
b, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
dba: dba,
|
|
adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"),
|
|
}
|
|
},
|
|
"fail/authorityID-mismatch": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: "foo",
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
b, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
dba: dba,
|
|
adminErr: admin.NewError(admin.ErrorAuthorityMismatchType,
|
|
"admin %s is not owned by authority %s", dba.ID, admin.DefaultAuthorityID),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
b, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
dba: dba,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if adm, err := db.GetAdmin(context.Background(), adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) {
|
|
assert.Equals(t, adm.Id, adminID)
|
|
assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID)
|
|
assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID)
|
|
assert.Equals(t, adm.Subject, tc.dba.Subject)
|
|
assert.Equals(t, adm.Type, tc.dba.Type)
|
|
assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt))
|
|
assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_DeleteAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admin adminID: force"),
|
|
}
|
|
},
|
|
"fail/save-error": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return data, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
assert.Equals(t, string(old), string(data))
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.Equals(t, _dba.ID, dba.ID)
|
|
assert.Equals(t, _dba.AuthorityID, dba.AuthorityID)
|
|
assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID)
|
|
assert.Equals(t, _dba.Subject, dba.Subject)
|
|
assert.Equals(t, _dba.Type, dba.Type)
|
|
assert.Equals(t, _dba.CreatedAt, dba.CreatedAt)
|
|
|
|
assert.True(t, _dba.DeletedAt.Before(time.Now()))
|
|
assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute)))
|
|
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving authority admin: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return data, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
assert.Equals(t, string(old), string(data))
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.Equals(t, _dba.ID, dba.ID)
|
|
assert.Equals(t, _dba.AuthorityID, dba.AuthorityID)
|
|
assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID)
|
|
assert.Equals(t, _dba.Subject, dba.Subject)
|
|
assert.Equals(t, _dba.Type, dba.Type)
|
|
assert.Equals(t, _dba.CreatedAt, dba.CreatedAt)
|
|
|
|
assert.True(t, _dba.DeletedAt.Before(time.Now()))
|
|
assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute)))
|
|
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if err := db.DeleteAdmin(context.Background(), adminID); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_UpdateAdmin(t *testing.T) {
|
|
adminID := "adminID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
adm *linkedca.Admin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
adm: &linkedca.Admin{Id: adminID},
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, nosqldb.ErrNotFound
|
|
},
|
|
},
|
|
adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
adm: &linkedca.Admin{Id: adminID},
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admin adminID: force"),
|
|
}
|
|
},
|
|
"fail/save-error": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
|
|
upd := dba.convert()
|
|
upd.Type = linkedca.Admin_ADMIN
|
|
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
adm: upd,
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return data, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
assert.Equals(t, string(old), string(data))
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.Equals(t, _dba.ID, dba.ID)
|
|
assert.Equals(t, _dba.AuthorityID, dba.AuthorityID)
|
|
assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID)
|
|
assert.Equals(t, _dba.Subject, dba.Subject)
|
|
assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN)
|
|
assert.Equals(t, _dba.CreatedAt, dba.CreatedAt)
|
|
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving authority admin: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
dba := &dbAdmin{
|
|
ID: adminID,
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: clock.Now(),
|
|
}
|
|
|
|
upd := dba.convert()
|
|
upd.Type = linkedca.Admin_ADMIN
|
|
|
|
data, err := json.Marshal(dba)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
adm: upd,
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
|
|
return data, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, string(key), adminID)
|
|
assert.Equals(t, string(old), string(data))
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.Equals(t, _dba.ID, dba.ID)
|
|
assert.Equals(t, _dba.AuthorityID, dba.AuthorityID)
|
|
assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID)
|
|
assert.Equals(t, _dba.Subject, dba.Subject)
|
|
assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN)
|
|
assert.Equals(t, _dba.CreatedAt, dba.CreatedAt)
|
|
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if err := db.UpdateAdmin(context.Background(), tc.adm); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_CreateAdmin(t *testing.T) {
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
adm *linkedca.Admin
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/save-error": func(t *testing.T) test {
|
|
adm := &linkedca.Admin{
|
|
AuthorityId: admin.DefaultAuthorityID,
|
|
ProvisionerId: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_ADMIN,
|
|
}
|
|
|
|
return test{
|
|
adm: adm,
|
|
db: &db.MockNoSQLDB{
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, old, nil)
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key))
|
|
assert.Equals(t, _dba.AuthorityID, adm.AuthorityId)
|
|
assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId)
|
|
assert.Equals(t, _dba.Subject, adm.Subject)
|
|
assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN)
|
|
|
|
assert.True(t, _dba.CreatedAt.Before(time.Now()))
|
|
assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute)))
|
|
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving authority admin: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
adm := &linkedca.Admin{
|
|
AuthorityId: admin.DefaultAuthorityID,
|
|
ProvisionerId: "provID",
|
|
Subject: "max@smallstep.com",
|
|
Type: linkedca.Admin_ADMIN,
|
|
}
|
|
|
|
return test{
|
|
adm: adm,
|
|
db: &db.MockNoSQLDB{
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
assert.Equals(t, old, nil)
|
|
|
|
var _dba = new(dbAdmin)
|
|
assert.FatalError(t, json.Unmarshal(nu, _dba))
|
|
|
|
assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key))
|
|
assert.Equals(t, _dba.AuthorityID, adm.AuthorityId)
|
|
assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId)
|
|
assert.Equals(t, _dba.Subject, adm.Subject)
|
|
assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN)
|
|
|
|
assert.True(t, _dba.CreatedAt.Before(time.Now()))
|
|
assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute)))
|
|
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if err := db.CreateAdmin(context.Background(), tc.adm); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetAdmins(t *testing.T) {
|
|
now := clock.Now()
|
|
fooAdmin := &dbAdmin{
|
|
ID: "foo",
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "foo@smallstep.com",
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
CreatedAt: now,
|
|
}
|
|
foob, err := json.Marshal(fooAdmin)
|
|
assert.FatalError(t, err)
|
|
|
|
barAdmin := &dbAdmin{
|
|
ID: "bar",
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "bar@smallstep.com",
|
|
Type: linkedca.Admin_ADMIN,
|
|
CreatedAt: now,
|
|
DeletedAt: now,
|
|
}
|
|
barb, err := json.Marshal(barAdmin)
|
|
assert.FatalError(t, err)
|
|
|
|
bazAdmin := &dbAdmin{
|
|
ID: "baz",
|
|
AuthorityID: "bazzer",
|
|
ProvisionerID: "provID",
|
|
Subject: "baz@smallstep.com",
|
|
Type: linkedca.Admin_ADMIN,
|
|
CreatedAt: now,
|
|
}
|
|
bazb, err := json.Marshal(bazAdmin)
|
|
assert.FatalError(t, err)
|
|
|
|
zapAdmin := &dbAdmin{
|
|
ID: "zap",
|
|
AuthorityID: admin.DefaultAuthorityID,
|
|
ProvisionerID: "provID",
|
|
Subject: "zap@smallstep.com",
|
|
Type: linkedca.Admin_ADMIN,
|
|
CreatedAt: now,
|
|
}
|
|
zapb, err := json.Marshal(zapAdmin)
|
|
assert.FatalError(t, err)
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
adminErr *admin.Error
|
|
verify func(*testing.T, []*linkedca.Admin)
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/db.List-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading admins: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
ret := []*nosqldb.Entry{
|
|
{Bucket: adminsTable, Key: []byte("foo"), Value: foob},
|
|
{Bucket: adminsTable, Key: []byte("bar"), Value: barb},
|
|
{Bucket: adminsTable, Key: []byte("zap"), Value: []byte("zap")},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
|
|
return ret, nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling admin zap into dbAdmin"),
|
|
}
|
|
},
|
|
"ok/none": func(t *testing.T) test {
|
|
ret := []*nosqldb.Entry{}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
|
|
return ret, nil
|
|
},
|
|
},
|
|
verify: func(t *testing.T, admins []*linkedca.Admin) {
|
|
assert.Equals(t, len(admins), 0)
|
|
},
|
|
}
|
|
},
|
|
"ok/only-invalid": func(t *testing.T) test {
|
|
ret := []*nosqldb.Entry{
|
|
{Bucket: adminsTable, Key: []byte("bar"), Value: barb},
|
|
{Bucket: adminsTable, Key: []byte("baz"), Value: bazb},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
|
|
return ret, nil
|
|
},
|
|
},
|
|
verify: func(t *testing.T, admins []*linkedca.Admin) {
|
|
assert.Equals(t, len(admins), 0)
|
|
},
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
ret := []*nosqldb.Entry{
|
|
{Bucket: adminsTable, Key: []byte("foo"), Value: foob},
|
|
{Bucket: adminsTable, Key: []byte("bar"), Value: barb},
|
|
{Bucket: adminsTable, Key: []byte("baz"), Value: bazb},
|
|
{Bucket: adminsTable, Key: []byte("zap"), Value: zapb},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
|
assert.Equals(t, bucket, adminsTable)
|
|
|
|
return ret, nil
|
|
},
|
|
},
|
|
verify: func(t *testing.T, admins []*linkedca.Admin) {
|
|
assert.Equals(t, len(admins), 2)
|
|
|
|
assert.Equals(t, admins[0].Id, fooAdmin.ID)
|
|
assert.Equals(t, admins[0].AuthorityId, fooAdmin.AuthorityID)
|
|
assert.Equals(t, admins[0].ProvisionerId, fooAdmin.ProvisionerID)
|
|
assert.Equals(t, admins[0].Subject, fooAdmin.Subject)
|
|
assert.Equals(t, admins[0].Type, fooAdmin.Type)
|
|
assert.Equals(t, admins[0].CreatedAt, timestamppb.New(fooAdmin.CreatedAt))
|
|
assert.Equals(t, admins[0].DeletedAt, timestamppb.New(fooAdmin.DeletedAt))
|
|
|
|
assert.Equals(t, admins[1].Id, zapAdmin.ID)
|
|
assert.Equals(t, admins[1].AuthorityId, zapAdmin.AuthorityID)
|
|
assert.Equals(t, admins[1].ProvisionerId, zapAdmin.ProvisionerID)
|
|
assert.Equals(t, admins[1].Subject, zapAdmin.Subject)
|
|
assert.Equals(t, admins[1].Type, zapAdmin.Type)
|
|
assert.Equals(t, admins[1].CreatedAt, timestamppb.New(zapAdmin.CreatedAt))
|
|
assert.Equals(t, admins[1].DeletedAt, timestamppb.New(zapAdmin.DeletedAt))
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID}
|
|
if admins, err := db.GetAdmins(context.Background()); err != nil {
|
|
switch k := err.(type) {
|
|
case *admin.Error:
|
|
if assert.NotNil(t, tc.adminErr) {
|
|
assert.Equals(t, k.Type, tc.adminErr.Type)
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
assert.Equals(t, k.Status, tc.adminErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.adminErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) {
|
|
tc.verify(t, admins)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|