database DEKs (#317)
This commit is contained in:
22 files changed
+2320
-22
No files matched your search
@@ -85,6 +85,7 @@ protobuild:
|
||||
@protoc-go-inject-tag -input=./internal/auth/password/store/password.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/auth/password/store/argon2.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/kms/store/root_key.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/kms/store/database_key.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/target/store/target.pb.go
|
||||
|
||||
@rm -R ${TMP_DIR}
|
||||
|
||||
@@ -91,7 +91,7 @@ before insert on kms_root_key_version
|
||||
|
||||
create table kms_database_key (
|
||||
private_id wt_private_id primary key,
|
||||
root_key_id wt_private_id not null
|
||||
root_key_id wt_private_id not null unique -- there can be only one database dek per root key
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
@@ -147,7 +147,7 @@ before insert on kms_database_key_version
|
||||
|
||||
create table kms_oplog_key (
|
||||
private_id wt_private_id primary key,
|
||||
root_key_id wt_private_id not null
|
||||
root_key_id wt_private_id not null unique -- there can be only one oplog dek per root key
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
@@ -203,7 +203,7 @@ before insert on kms_oplog_key_version
|
||||
|
||||
create table kms_session_key (
|
||||
private_id wt_private_id primary key,
|
||||
root_key_id wt_private_id not null
|
||||
root_key_id wt_private_id not null unique -- there can be only one session dek per root key
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDatabaseKeyTableName = "kms_database_key"
|
||||
)
|
||||
|
||||
type DatabaseKey struct {
|
||||
*store.DatabaseKey
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewDatabaseKey creates a new in memory database key. No options
|
||||
// are currently supported.
|
||||
func NewDatabaseKey(rootKeyId string, opt ...Option) (*DatabaseKey, error) {
|
||||
if rootKeyId == "" {
|
||||
return nil, fmt.Errorf("new root key: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
c := &DatabaseKey{
|
||||
DatabaseKey: &store.DatabaseKey{
|
||||
RootKeyId: rootKeyId,
|
||||
},
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// AllocDatabaseKey will allocate a DatabaseKey
|
||||
func AllocDatabaseKey() DatabaseKey {
|
||||
return DatabaseKey{
|
||||
DatabaseKey: &store.DatabaseKey{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the DatabaseKey
|
||||
func (k *DatabaseKey) Clone() interface{} {
|
||||
cp := proto.Clone(k.DatabaseKey)
|
||||
return &DatabaseKey{
|
||||
DatabaseKey: cp.(*store.DatabaseKey),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the kms root
|
||||
// before it's written.
|
||||
func (k *DatabaseKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("database key vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if k.RootKeyId == "" {
|
||||
return fmt.Errorf("database key vet for write: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
case db.UpdateOp:
|
||||
return fmt.Errorf("database key vet for write: key is immutable: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *DatabaseKey) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultDatabaseKeyTableName
|
||||
}
|
||||
|
||||
// SetTableName sets the tablename and satisfies the ReplayableMessage
|
||||
// interface. If the caller attempts to set the name to "" the name will be
|
||||
// reset to the default name.
|
||||
func (k *DatabaseKey) SetTableName(n string) {
|
||||
k.tableName = n
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package kms_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// NOTE: there are no update tests since all the DatabaseKey attributes are
|
||||
// immutable and those tests are covered by TestDatabaseKey_ImmutableFields
|
||||
|
||||
func TestDatabaseKey_Create(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
|
||||
type args struct {
|
||||
rootKeyId string
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *kms.DatabaseKey
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty-rootKeyId",
|
||||
args: args{},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
rootKeyId: rk.PrivateId,
|
||||
},
|
||||
want: func() *kms.DatabaseKey {
|
||||
k := kms.AllocDatabaseKey()
|
||||
k.RootKeyId = rk.PrivateId
|
||||
return &k
|
||||
}(),
|
||||
create: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocDatabaseKey()).Error)
|
||||
got, err := kms.NewDatabaseKey(tt.args.rootKeyId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.want, got)
|
||||
if tt.create {
|
||||
id, err := db.NewPublicId(kms.DatabaseKeyPrefix)
|
||||
require.NoError(err)
|
||||
got.PrivateId = id
|
||||
err = db.New(conn).Create(context.Background(), got)
|
||||
if tt.wantCreateErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKey_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocDatabaseKey()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.DatabaseKey
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestDatabaseKey(t, conn, rk.PrivateId),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.DatabaseKey {
|
||||
k := kms.AllocDatabaseKey()
|
||||
id, err := db.NewPublicId(kms.DatabaseKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k.PrivateId = id
|
||||
k.RootKeyId = rk.PrivateId
|
||||
return &k
|
||||
}(),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
deleteKey := kms.AllocDatabaseKey()
|
||||
deleteKey.PrivateId = tt.key.PrivateId
|
||||
deletedRows, err := rw.Delete(context.Background(), &deleteKey)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
if tt.wantRowsDeleted == 0 {
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
return
|
||||
}
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
foundKey := kms.AllocDatabaseKey()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKey_Clone(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
k := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.DatabaseKey).DatabaseKey, k.DatabaseKey))
|
||||
})
|
||||
t.Run("not-equal", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
org2, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
rk2 := kms.TestRootKey(t, conn, org2.PublicId)
|
||||
k := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestDatabaseKey(t, conn, rk2.PrivateId)
|
||||
|
||||
cp := k.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.DatabaseKey).DatabaseKey, k2.DatabaseKey))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDatabaseKey_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultDatabaseKeyTableName
|
||||
tests := []struct {
|
||||
name string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-name",
|
||||
setNameTo: "new-name",
|
||||
want: "new-name",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
def := kms.AllocDatabaseKey()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocDatabaseKey()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms/store"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/structwrapping"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDatabaseKeyVersionTableName = "kms_database_key_version"
|
||||
)
|
||||
|
||||
type DatabaseKeyVersion struct {
|
||||
*store.DatabaseKeyVersion
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewDatabaseKeyVersion creates a new in memory database key version. No options are
|
||||
// currently supported.
|
||||
func NewDatabaseKeyVersion(databaseKeyId string, key []byte, rootKeyVersionId string, opt ...Option) (*DatabaseKeyVersion, error) {
|
||||
if databaseKeyId == "" {
|
||||
return nil, fmt.Errorf("new database key version: missing database key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("new database key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rootKeyVersionId == "" {
|
||||
return nil, fmt.Errorf("new database key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
|
||||
k := &DatabaseKeyVersion{
|
||||
DatabaseKeyVersion: &store.DatabaseKeyVersion{
|
||||
DatabaseKeyId: databaseKeyId,
|
||||
RootKeyVersionId: rootKeyVersionId,
|
||||
Key: key,
|
||||
},
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// AllocDatabaseKeyVersion allocates a DatabaseKeyVersion
|
||||
func AllocDatabaseKeyVersion() DatabaseKeyVersion {
|
||||
return DatabaseKeyVersion{
|
||||
DatabaseKeyVersion: &store.DatabaseKeyVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the DatabaseKeyVersion
|
||||
func (k *DatabaseKeyVersion) Clone() interface{} {
|
||||
cp := proto.Clone(k.DatabaseKeyVersion)
|
||||
return &DatabaseKeyVersion{
|
||||
DatabaseKeyVersion: cp.(*store.DatabaseKeyVersion),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the database key
|
||||
// version before it's written.
|
||||
func (k *DatabaseKeyVersion) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("database key version vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if k.CtKey == nil {
|
||||
return fmt.Errorf("database key version vet for write: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.DatabaseKeyId == "" {
|
||||
return fmt.Errorf("database key version vet for write: missing database key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.RootKeyVersionId == "" {
|
||||
return fmt.Errorf("database key version vet for write: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
case db.UpdateOp:
|
||||
return fmt.Errorf("database key version vet for write: key is immutable: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *DatabaseKeyVersion) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultDatabaseKeyVersionTableName
|
||||
}
|
||||
|
||||
// SetTableName sets the tablename and satisfies the ReplayableMessage
|
||||
// interface. If the caller attempts to set the name to "" the name will be
|
||||
// reset to the default name.
|
||||
func (k *DatabaseKeyVersion) SetTableName(n string) {
|
||||
k.tableName = n
|
||||
}
|
||||
|
||||
// Encrypt will encrypt the database key version's key
|
||||
func (k *DatabaseKeyVersion) Encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.DatabaseKeyVersion directly
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, k.DatabaseKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting kms database key version: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt will decrypt the database key version's key
|
||||
func (k *DatabaseKeyVersion) Decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.DatabaseKeyVersion directly
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, k.DatabaseKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting kms database key version: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package kms_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// NOTE: there are no update tests since all the DatabaseKeyVersion attributes are
|
||||
// immutable and those tests are covered by TestDatabaseKey_ImmutableFields
|
||||
|
||||
func TestDatabaseKeyVersion_Create(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
rkv, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
databaseKeyId string
|
||||
key []byte
|
||||
rootKeyVersionId string
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *kms.DatabaseKeyVersion
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty-database-id",
|
||||
args: args{
|
||||
key: []byte("database key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
databaseKeyId: dk.PrivateId,
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-root-key-version-id",
|
||||
args: args{
|
||||
databaseKeyId: dk.PrivateId,
|
||||
key: []byte("database key"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
databaseKeyId: dk.PrivateId,
|
||||
key: []byte("database key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
want: func() *kms.DatabaseKeyVersion {
|
||||
k := kms.AllocDatabaseKeyVersion()
|
||||
k.RootKeyVersionId = rkv.PrivateId
|
||||
k.Key = []byte("database key")
|
||||
k.DatabaseKeyId = dk.PrivateId
|
||||
return &k
|
||||
}(),
|
||||
create: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocDatabaseKeyVersion()).Error)
|
||||
got, err := kms.NewDatabaseKeyVersion(tt.args.databaseKeyId, tt.args.key, tt.args.rootKeyVersionId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.want, got)
|
||||
if tt.create {
|
||||
id, err := db.NewPublicId(kms.DatabaseKeyPrefix)
|
||||
require.NoError(err)
|
||||
got.PrivateId = id
|
||||
err = got.Encrypt(context.Background(), rkvWrapper)
|
||||
require.NoError(err)
|
||||
err = db.New(conn).Create(context.Background(), got)
|
||||
if tt.wantCreateErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocDatabaseKeyVersion()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.DatabaseKeyVersion
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("database key")),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.DatabaseKeyVersion {
|
||||
k := kms.AllocDatabaseKeyVersion()
|
||||
id, err := db.NewPublicId(kms.DatabaseKeyVersionPrefix)
|
||||
require.NoError(t, err)
|
||||
k.PrivateId = id
|
||||
return &k
|
||||
}(),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
deleteKey := kms.AllocDatabaseKeyVersion()
|
||||
deleteKey.PrivateId = tt.key.PrivateId
|
||||
deletedRows, err := rw.Delete(context.Background(), &deleteKey)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
if tt.wantRowsDeleted == 0 {
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
return
|
||||
}
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
foundKey := kms.AllocDatabaseKeyVersion()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion_Clone(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
|
||||
k := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
kv := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test db key"))
|
||||
cp := kv.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.DatabaseKeyVersion).DatabaseKeyVersion, kv.DatabaseKeyVersion))
|
||||
})
|
||||
t.Run("not-equal", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
org2, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
rk2 := kms.TestRootKey(t, conn, org2.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
_, rkvWrapper2 := kms.TestRootKeyVersion(t, conn, wrapper, rk2.PrivateId)
|
||||
|
||||
k := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestDatabaseKey(t, conn, rk2.PrivateId)
|
||||
kv := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test db key"))
|
||||
kv2 := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper2, k2.PrivateId, []byte("test db key 2"))
|
||||
|
||||
cp := kv.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.DatabaseKeyVersion).DatabaseKeyVersion, kv2.DatabaseKeyVersion))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultDatabaseKeyVersionTableName
|
||||
tests := []struct {
|
||||
name string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-name",
|
||||
setNameTo: "new-name",
|
||||
want: "new-name",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
def := kms.AllocDatabaseKeyVersion()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocDatabaseKeyVersion()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
+20
-2
@@ -7,8 +7,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
RootKeyPrefix = "krk"
|
||||
RootKeyVersionPrefix = "krkv"
|
||||
RootKeyPrefix = "krk"
|
||||
RootKeyVersionPrefix = "krkv"
|
||||
DatabaseKeyPrefix = "kdk"
|
||||
DatabaseKeyVersionPrefix = "kdkv"
|
||||
)
|
||||
|
||||
func newRootKeyId() (string, error) {
|
||||
@@ -26,3 +28,19 @@ func newRootKeyVersionId() (string, error) {
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newDatabaseKeyId() (string, error) {
|
||||
id, err := db.NewPublicId(DatabaseKeyPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new database key id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newDatabaseKeyVersionId() (string, error) {
|
||||
id, err := db.NewPublicId(DatabaseKeyVersionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new database key version id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -20,4 +20,14 @@ func Test_Ids(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, RootKeyVersionPrefix+"_"))
|
||||
})
|
||||
t.Run("kdk", func(t *testing.T) {
|
||||
id, err := newDatabaseKeyId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, DatabaseKeyPrefix+"_"))
|
||||
})
|
||||
t.Run("kdkv", func(t *testing.T) {
|
||||
id, err := newDatabaseKeyVersionId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, DatabaseKeyVersionPrefix+"_"))
|
||||
})
|
||||
}
|
||||
@@ -16,8 +16,6 @@ import (
|
||||
|
||||
// TODO (jimlambrt 8/2020) define tests for immutable fields in the remaining
|
||||
// kms tables:
|
||||
// kms_database_key
|
||||
// kms_database_key_version
|
||||
// kms_oplog_key
|
||||
// kms_oplog_key_version
|
||||
// kms_session_key
|
||||
@@ -35,7 +33,7 @@ func TestRootKeyVersion_ImmutableFields(t *testing.T) {
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKeyVersion()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
new := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key"))
|
||||
new, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
@@ -178,3 +176,171 @@ func TestRootKey_ImmutableFields(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKey_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocDatabaseKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
new := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
update *kms.DatabaseKey
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "private_id",
|
||||
update: func() *kms.DatabaseKey {
|
||||
k := new.Clone().(*kms.DatabaseKey)
|
||||
k.PrivateId = "o_thisIsNotAValidId"
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"PrivateId"},
|
||||
},
|
||||
{
|
||||
name: "create time",
|
||||
update: func() *kms.DatabaseKey {
|
||||
k := new.Clone().(*kms.DatabaseKey)
|
||||
k.CreateTime = &ts
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"CreateTime"},
|
||||
},
|
||||
{
|
||||
name: "root_key_id",
|
||||
update: func() *kms.DatabaseKey {
|
||||
k := new.Clone().(*kms.DatabaseKey)
|
||||
k.RootKeyId = "o_thisIsNotAValidId"
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"ScopeId"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := new.Clone()
|
||||
err := rw.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil)
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := new.Clone()
|
||||
err = rw.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig.(*kms.DatabaseKey), after.(*kms.DatabaseKey)))
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
rw := db.New(conn)
|
||||
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
new := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("database key"))
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
update *kms.DatabaseKeyVersion
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "private_id",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.PrivateId = "o_thisIsNotAValidId"
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"PrivateId"},
|
||||
},
|
||||
{
|
||||
name: "create time",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.CreateTime = &ts
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"CreateTime"},
|
||||
},
|
||||
{
|
||||
name: "database_key_id",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.DatabaseKeyId = "o_thisIsNotAValidId"
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"RootKeyId"},
|
||||
},
|
||||
{
|
||||
name: "root_key_version_id",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.RootKeyVersionId = "o_thisIsNotAValidId"
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"RootKeyId"},
|
||||
},
|
||||
{
|
||||
name: "version",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.Version = uint32(22)
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"Version"},
|
||||
},
|
||||
{
|
||||
name: "key",
|
||||
update: func() *kms.DatabaseKeyVersion {
|
||||
k := new.Clone().(*kms.DatabaseKeyVersion)
|
||||
k.Key = []byte("updated key")
|
||||
return k
|
||||
}(),
|
||||
fieldMask: []string{"CtKey"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := new.Clone()
|
||||
err := rw.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
err = tt.update.Encrypt(context.Background(), wrapper)
|
||||
require.NoError(err)
|
||||
rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil)
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := new.Clone()
|
||||
err = rw.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig.(*kms.DatabaseKeyVersion), after.(*kms.DatabaseKeyVersion)))
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
)
|
||||
|
||||
// CreateDatabaseKey inserts into the repository and returns the new database key and
|
||||
// database key version. There are no valid options at this time.
|
||||
func (r *Repository) CreateDatabaseKey(ctx context.Context, rkvWrapper wrapping.Wrapper, key []byte, opt ...Option) (*DatabaseKey, *DatabaseKeyVersion, error) {
|
||||
var returnedDk, returnedDv interface{}
|
||||
_, err := r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(reader db.Reader, w db.Writer) error {
|
||||
var err error
|
||||
if returnedDk, returnedDv, err = CreateDatabaseKeyTx(ctx, reader, w, rkvWrapper, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: %w", err)
|
||||
}
|
||||
return returnedDk.(*DatabaseKey), returnedDv.(*DatabaseKeyVersion), err
|
||||
}
|
||||
|
||||
// CreateDatabaseKeyTx inserts into the db (via db.Writer) and returns the new database key
|
||||
// and database key version. This function encapsulates all the work required within
|
||||
// a db.TxHandler and allows this capability to be shared with the iam repo.
|
||||
func CreateDatabaseKeyTx(ctx context.Context, r db.Reader, w db.Writer, rkvWrapper wrapping.Wrapper, key []byte) (*DatabaseKey, *DatabaseKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, nil, fmt.Errorf("create database key: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, nil, fmt.Errorf("create database key: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, nil, fmt.Errorf("create database key: root key version id %s doesn't start with prefix %s: %w", rootKeyVersionId, RootKeyVersionPrefix, db.ErrInvalidParameter)
|
||||
case rootKeyVersionId == "":
|
||||
return nil, nil, fmt.Errorf("create database key: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rv := AllocRootKeyVersion()
|
||||
rv.PrivateId = rootKeyVersionId
|
||||
err := r.LookupById(ctx, &rv)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: unable to lookup root key version %s: %w", rootKeyVersionId, err)
|
||||
}
|
||||
|
||||
dk := AllocDatabaseKey()
|
||||
dv := AllocDatabaseKeyVersion()
|
||||
id, err := newDatabaseKeyId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: %w", err)
|
||||
}
|
||||
dk.PrivateId = id
|
||||
dk.RootKeyId = rv.RootKeyId
|
||||
|
||||
id, err = newDatabaseKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: %w", err)
|
||||
}
|
||||
dv.PrivateId = id
|
||||
dv.DatabaseKeyId = dk.PrivateId
|
||||
dv.RootKeyVersionId = rootKeyVersionId
|
||||
dv.Key = key
|
||||
if err := dv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: %w", err)
|
||||
}
|
||||
|
||||
// no oplog entries for keys
|
||||
if err := w.Create(ctx, &dk); err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: database create: %w", err)
|
||||
}
|
||||
// no oplog entries for key versions
|
||||
if err := w.Create(ctx, &dv); err != nil {
|
||||
return nil, nil, fmt.Errorf("create database key: version create: %w", err)
|
||||
}
|
||||
|
||||
return &dk, &dv, err
|
||||
}
|
||||
|
||||
// LookupDatabaseKey will look up a key in the repository. If the key is not
|
||||
// found, it will return nil, nil.
|
||||
func (r *Repository) LookupDatabaseKey(ctx context.Context, privateId string, opt ...Option) (*DatabaseKey, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup database key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocDatabaseKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup database key: failed %w for %s", err, privateId)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteDatabaseKey deletes the key for the provided id from the
|
||||
// repository returning a count of the number of records deleted. All options
|
||||
// are ignored.
|
||||
func (r *Repository) DeleteDatabaseKey(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocDatabaseKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key: failed %w for %s", err, privateId)
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
_, err := r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) (err error) {
|
||||
dk := k.Clone()
|
||||
// no oplog entries for root keys
|
||||
rowsDeleted, err = w.Delete(ctx, dk)
|
||||
if err == nil && rowsDeleted > 1 {
|
||||
return db.ErrMultipleRecords
|
||||
}
|
||||
return err
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// ListDatabaseKeys will list the keys. Supports the WithLimit option.
|
||||
func (r *Repository) ListDatabaseKeys(ctx context.Context, opt ...Option) ([]*DatabaseKey, error) {
|
||||
var keys []*DatabaseKey
|
||||
err := r.list(ctx, &keys, "1=1", nil, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list database keys: %w", err)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package kms_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestRepository_CreateDatabaseKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
scopeId string
|
||||
key []byte
|
||||
keyWrapper wrapping.Wrapper
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid-org",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
key: []byte("test key"),
|
||||
keyWrapper: rkvWrapper,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-key",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
keyWrapper: rkvWrapper,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
keyWrapper: rkvWrapper,
|
||||
key: []byte(""),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
key: []byte("test key"),
|
||||
keyWrapper: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-rkv-wrapper",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
key: []byte("test key"),
|
||||
keyWrapper: wrapper,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "wrapper-missing-id",
|
||||
args: args{
|
||||
scopeId: org.PublicId,
|
||||
key: []byte("test key"),
|
||||
keyWrapper: func() wrapping.Wrapper {
|
||||
w := db.TestWrapper(t)
|
||||
_, err = w.(*aead.Wrapper).SetConfig(map[string]string{
|
||||
"key_id": "",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return w
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
dk, dv, err := repo.CreateDatabaseKey(context.Background(), tt.args.keyWrapper, tt.args.key, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(dk)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotNil(dk.CreateTime)
|
||||
foundKey, err := repo.LookupDatabaseKey(context.Background(), dk.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKey, dk))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, dk.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
|
||||
assert.NotNil(dv.CreateTime)
|
||||
foundKeyVersion, err := repo.LookupDatabaseKeyVersion(context.Background(), tt.args.keyWrapper, dv.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKeyVersion, dv))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, dv.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_DeleteDatabaseKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
|
||||
type args struct {
|
||||
key *kms.DatabaseKey
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestDatabaseKey(t, conn, rk.PrivateId),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.DatabaseKey {
|
||||
k := kms.AllocDatabaseKey()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.DatabaseKey {
|
||||
id, err := db.NewPublicId(kms.RootKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocDatabaseKey()
|
||||
k.PrivateId = id
|
||||
require.NoError(t, err)
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrRecordNotFound,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
deletedRows, err := repo.DeleteDatabaseKey(context.Background(), tt.args.key.PrivateId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Equal(0, deletedRows)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, tt.args.key.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
foundKey, err := repo.LookupDatabaseKey(context.Background(), tt.args.key.PrivateId)
|
||||
assert.Error(err)
|
||||
assert.Nil(foundKey)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, tt.args.key.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListDatabaseKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
const testLimit = 10
|
||||
rw := db.New(conn)
|
||||
repo, err := kms.NewRepository(rw, rw, kms.WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
type args struct {
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
createCnt int
|
||||
args args
|
||||
wantCnt int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
opt: []kms.Option{kms.WithLimit(-1)},
|
||||
},
|
||||
wantCnt: repo.DefaultLimit() + 1, // org and project both have keys, plus global scope
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "default-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{},
|
||||
wantCnt: repo.DefaultLimit(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
opt: []kms.Option{kms.WithLimit(3)},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("scope_id=?", []interface{}{proj.PublicId}).Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, proj.PublicId)
|
||||
kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
require.NoError(err)
|
||||
}
|
||||
got, err := repo.ListDatabaseKeys(context.Background(), tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
)
|
||||
|
||||
// CreateDatabaseKeyVersion inserts into the repository and returns the new key
|
||||
// version with its PrivateId. There are no valid options at this time.
|
||||
func (r *Repository) CreateDatabaseKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, databaseKeyId string, key []byte, opt ...Option) (*DatabaseKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("create database key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, fmt.Errorf("create database key version: root key version id %s doesn't start with prefix %s: %w", rootKeyVersionId, RootKeyVersionPrefix, db.ErrInvalidParameter)
|
||||
case rootKeyVersionId == "":
|
||||
return nil, fmt.Errorf("create database key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if databaseKeyId == "" {
|
||||
return nil, fmt.Errorf("create database key version: missing database key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("create database key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
kv := AllocDatabaseKeyVersion()
|
||||
id, err := newDatabaseKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create database key version: %w", err)
|
||||
}
|
||||
kv.PrivateId = id
|
||||
kv.RootKeyVersionId = rootKeyVersionId
|
||||
kv.Key = key
|
||||
kv.DatabaseKeyId = databaseKeyId
|
||||
if err := kv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("create database key version: encrypt: %w", err)
|
||||
}
|
||||
|
||||
var returnedKey interface{}
|
||||
_, err = r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
returnedKey = kv.Clone()
|
||||
// no oplog entries for root key version
|
||||
if err := w.Create(ctx, returnedKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create database key version: %w for %s database key id", err, kv.DatabaseKeyId)
|
||||
}
|
||||
return returnedKey.(*DatabaseKeyVersion), err
|
||||
}
|
||||
|
||||
// LookupDatabaseKeyVersion will look up a key version in the repository. If
|
||||
// the key version is not found, it will return nil, nil.
|
||||
func (r *Repository) LookupDatabaseKeyVersion(ctx context.Context, keyWrapper wrapping.Wrapper, privateId string, opt ...Option) (*DatabaseKeyVersion, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup database key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if keyWrapper == nil {
|
||||
return nil, fmt.Errorf("lookup database key version: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocDatabaseKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup database key version: failed %w for %s", err, privateId)
|
||||
}
|
||||
if err := k.Decrypt(ctx, keyWrapper); err != nil {
|
||||
return nil, fmt.Errorf("lookup database key version: decrypt: %w", err)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteDatabaseKeyVersion deletes the key version for the provided id from the
|
||||
// repository returning a count of the number of records deleted. All options
|
||||
// are ignored.
|
||||
func (r *Repository) DeleteDatabaseKeyVersion(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocDatabaseKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key version: failed %w for %s", err, privateId)
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
_, err := r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) (err error) {
|
||||
dk := k.Clone()
|
||||
// no oplog entries for the key version
|
||||
rowsDeleted, err = w.Delete(ctx, dk)
|
||||
if err == nil && rowsDeleted > 1 {
|
||||
return db.ErrMultipleRecords
|
||||
}
|
||||
return err
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete database key version: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// LatestDatabaseKeyVersion searches for the key version with the highest
|
||||
// version number. When no results are found, it returns nil,
|
||||
// db.ErrRecordNotFound.
|
||||
func (r *Repository) LatestDatabaseKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, databaseKeyId string, opt ...Option) (*DatabaseKeyVersion, error) {
|
||||
if databaseKeyId == "" {
|
||||
return nil, fmt.Errorf("latest database key version: missing database key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("latest database key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var foundKeys []*DatabaseKeyVersion
|
||||
if err := r.reader.SearchWhere(ctx, &foundKeys, "database_key_id = ?", []interface{}{databaseKeyId}, db.WithLimit(1), db.WithOrder("version desc")); err != nil {
|
||||
return nil, fmt.Errorf("latest database key version: failed %w for %s", err, databaseKeyId)
|
||||
}
|
||||
if len(foundKeys) == 0 {
|
||||
return nil, db.ErrRecordNotFound
|
||||
}
|
||||
if err := foundKeys[0].Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("latest database key version: %w", err)
|
||||
}
|
||||
return foundKeys[0], nil
|
||||
}
|
||||
|
||||
// ListDatabaseKeyVersions will lists versions of a key. Supports the WithLimit option.
|
||||
func (r *Repository) ListDatabaseKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, databaseKeyId string, opt ...Option) ([]*DatabaseKeyVersion, error) {
|
||||
if databaseKeyId == "" {
|
||||
return nil, fmt.Errorf("list database key versions: missing database key id %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("list database key versions: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var versions []*DatabaseKeyVersion
|
||||
err := r.list(ctx, &versions, "database_key_id = ?", []interface{}{databaseKeyId}, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list database key versions: %w", err)
|
||||
}
|
||||
for i, k := range versions {
|
||||
if err := k.Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("list database key versions: error decrypting key num %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
return versions, nil
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
package kms_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestRepository_CreateDatabaseKeyVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key []byte
|
||||
databaseKeyId string
|
||||
keyWrapper wrapping.Wrapper
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: rkvWrapper,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-rkv-wrapper",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: wrapper,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
key: nil,
|
||||
keyWrapper: wrapper,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
k, err := repo.CreateDatabaseKeyVersion(context.Background(), tt.args.keyWrapper, tt.args.databaseKeyId, tt.args.key, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(k)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotNil(k.CreateTime)
|
||||
foundKey, err := repo.LookupDatabaseKeyVersion(context.Background(), tt.args.keyWrapper, k.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKey, k))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, k.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_DeleteDatabaseKeyVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key *kms.DatabaseKeyVersion
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("database key")),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.DatabaseKeyVersion {
|
||||
k := kms.AllocDatabaseKeyVersion()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.DatabaseKeyVersion {
|
||||
id, err := db.NewPublicId(kms.DatabaseKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocDatabaseKeyVersion()
|
||||
k.PrivateId = id
|
||||
require.NoError(t, err)
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrRecordNotFound,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
deletedRows, err := repo.DeleteDatabaseKeyVersion(context.Background(), tt.args.key.PrivateId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Equal(0, deletedRows)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
err = db.TestVerifyOplog(t, rw, tt.args.key.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||
foundKey, err := repo.LookupDatabaseKeyVersion(context.Background(), wrapper, tt.args.key.PrivateId)
|
||||
assert.Error(err)
|
||||
assert.Nil(foundKey)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, tt.args.key.PrivateId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||
assert.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_LatestDatabaseKeyVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
createCnt int
|
||||
keyWrapper wrapping.Wrapper
|
||||
wantVersion uint32
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "5",
|
||||
createCnt: 5,
|
||||
keyWrapper: rkvWrapper,
|
||||
wantVersion: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1",
|
||||
createCnt: 1,
|
||||
keyWrapper: rkvWrapper,
|
||||
wantVersion: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "0",
|
||||
createCnt: 0,
|
||||
keyWrapper: rkvWrapper,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrRecordNotFound,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
createCnt: 5,
|
||||
keyWrapper: nil,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocDatabaseKeyVersion()).Error)
|
||||
testKeys := []*kms.DatabaseKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("test database key"))
|
||||
testKeys = append(testKeys, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testKeys))
|
||||
got, err := repo.LatestDatabaseKeyVersion(context.Background(), tt.keyWrapper, dk.PrivateId)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Equal(tt.wantVersion, got.Version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListDatabaseKeyVersions(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
const testLimit = 10
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := kms.NewRepository(rw, rw, kms.WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rkvWrapper := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
databaseKeyId string
|
||||
keyWrapper wrapping.Wrapper
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
createCnt int
|
||||
args args
|
||||
wantCnt int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
databaseKeyId: dk.PrivateId,
|
||||
keyWrapper: rkvWrapper,
|
||||
opt: []kms.Option{kms.WithLimit(-1)},
|
||||
},
|
||||
wantCnt: repo.DefaultLimit() + 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "default-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
},
|
||||
wantCnt: repo.DefaultLimit(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
opt: []kms.Option{kms.WithLimit(3)},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-org",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
databaseKeyId: "bad-id",
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: nil,
|
||||
databaseKeyId: dk.PrivateId,
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocDatabaseKeyVersion()).Error)
|
||||
keyVersions := []*kms.DatabaseKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestDatabaseKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("database key"))
|
||||
keyVersions = append(keyVersions, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(keyVersions))
|
||||
got, err := repo.ListDatabaseKeyVersions(context.Background(), tt.args.keyWrapper, tt.args.databaseKeyId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,7 @@ func (r *Repository) CreateRootKey(ctx context.Context, keyWrapper wrapping.Wrap
|
||||
|
||||
// CreateRootKeyTx inserts into the db (via db.Writer) and returns the new root key
|
||||
// and root key version. This function encapsulates all the work required within
|
||||
// a db.TxHandler and allows this capability to be shared with the iam repo via
|
||||
// a common pkg without circular dependencies: kms/common.CreateRootKeyTx
|
||||
// a db.TxHandler and allows this capability to be shared with the iam repo.
|
||||
func CreateRootKeyTx(ctx context.Context, w db.Writer, keyWrapper wrapping.Wrapper, scopeId string, key []byte) (*RootKey, *RootKeyVersion, error) {
|
||||
if scopeId == "" {
|
||||
return nil, nil, fmt.Errorf("create root key: missing scope id: %w", db.ErrInvalidParameter)
|
||||
|
||||
@@ -129,7 +129,10 @@ func TestRepository_DeleteRootKeyVersion(t *testing.T) {
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key")),
|
||||
key: func() *kms.RootKeyVersion {
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
return k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
@@ -247,7 +250,8 @@ func TestRepository_LatestRootKeyVersion(t *testing.T) {
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocRootKeyVersion()).Error)
|
||||
testKeys := []*kms.RootKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
testKeys = append(testKeys, kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key")))
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
testKeys = append(testKeys, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testKeys))
|
||||
got, err := repo.LatestRootKeyVersion(context.Background(), tt.keyWrapper, rk.PrivateId)
|
||||
@@ -349,7 +353,8 @@ func TestRepository_ListRootKeyVersions(t *testing.T) {
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocRootKeyVersion()).Error)
|
||||
testRootKeyVersions := []*kms.RootKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
testRootKeyVersions = append(testRootKeyVersions, kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key")))
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
testRootKeyVersions = append(testRootKeyVersions, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testRootKeyVersions))
|
||||
got, err := repo.ListRootKeyVersions(context.Background(), tt.args.keyWrapper, tt.args.rootKeyId, tt.args.opt...)
|
||||
|
||||
@@ -55,10 +55,13 @@ func (k *RootKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("root key vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if k.ScopeId == "" {
|
||||
return fmt.Errorf("root key vet for write: missing scope id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
case db.UpdateOp:
|
||||
return fmt.Errorf("root key vet for write: key is immutable: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,13 +60,16 @@ func (k *RootKeyVersion) VetForWrite(ctx context.Context, r db.Reader, opType db
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("root key version vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if k.CtKey == nil {
|
||||
return fmt.Errorf("root key version vet for write: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.RootKeyId == "" {
|
||||
return fmt.Errorf("root key version vet for write: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
case db.UpdateOp:
|
||||
return fmt.Errorf("root key version vet for write: key is immutable: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestRootKeyVersion_Delete(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key")),
|
||||
key: func() *kms.RootKeyVersion { k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId); return k }(),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
@@ -169,7 +169,7 @@ func TestRootKeyVersion_Clone(t *testing.T) {
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
k := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key"))
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.RootKeyVersion).RootKeyVersion, k.RootKeyVersion))
|
||||
})
|
||||
@@ -178,8 +178,8 @@ func TestRootKeyVersion_Clone(t *testing.T) {
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
k := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key"))
|
||||
k2 := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key"))
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
k2, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.RootKeyVersion).RootKeyVersion, k2.RootKeyVersion))
|
||||
})
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.12.4
|
||||
// source: controller/storage/kms/store/v1/database_key.proto
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
_ "github.com/hashicorp/boundary/internal/gen/controller/protooptions"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type DatabaseKey struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// private_id is used to access the key via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty" gorm:"primary_key"`
|
||||
// root key id for the key
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
RootKeyId string `protobuf:"bytes,20,opt,name=root_key_id,json=rootKeyId,proto3" json:"root_key_id,omitempty" gorm:"default:null"`
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
}
|
||||
|
||||
func (x *DatabaseKey) Reset() {
|
||||
*x = DatabaseKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_database_key_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DatabaseKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DatabaseKey) ProtoMessage() {}
|
||||
|
||||
func (x *DatabaseKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_database_key_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DatabaseKey.ProtoReflect.Descriptor instead.
|
||||
func (*DatabaseKey) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_database_key_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *DatabaseKey) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DatabaseKey) GetRootKeyId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DatabaseKey) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DatabaseKeyVersion struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// private_id is used to access the key version via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty" gorm:"primary_key"`
|
||||
// database id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
DatabaseKeyId string `protobuf:"bytes,20,opt,name=database_key_id,json=databaseKeyId,proto3" json:"database_key_id,omitempty" gorm:"default:null"`
|
||||
// root_key_version_id of the version of the root key data.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
RootKeyVersionId string `protobuf:"bytes,30,opt,name=root_key_version_id,json=rootKeyVersionId,proto3" json:"root_key_version_id,omitempty" gorm:"default:null"`
|
||||
// plain-text of the key data. we are NOT storing this plain-text key
|
||||
// in the db.
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,key_data"`
|
||||
Key []byte `protobuf:"bytes,40,opt,name=key,proto3" json:"key,omitempty" gorm:"-" wrapping:"pt,key_data"`
|
||||
// ciphertext key data stored in the database
|
||||
// @inject_tag: `gorm:"column:key;not_null" wrapping:"ct,key_data"`
|
||||
CtKey []byte `protobuf:"bytes,50,opt,name=ct_key,json=ctKey,proto3" json:"ct_key,omitempty" gorm:"column:key;not_null" wrapping:"ct,key_data"`
|
||||
// version of the key data. This is not used for optimistic locking, since
|
||||
// key versions are immutable. It's just the version of the key.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,60,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,70,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) Reset() {
|
||||
*x = DatabaseKeyVersion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_database_key_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DatabaseKeyVersion) ProtoMessage() {}
|
||||
|
||||
func (x *DatabaseKeyVersion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_database_key_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DatabaseKeyVersion.ProtoReflect.Descriptor instead.
|
||||
func (*DatabaseKeyVersion) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_database_key_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetDatabaseKeyId() string {
|
||||
if x != nil {
|
||||
return x.DatabaseKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetRootKeyVersionId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyVersionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetKey() []byte {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetCtKey() []byte {
|
||||
if x != nil {
|
||||
return x.CtKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *DatabaseKeyVersion) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_kms_store_v1_database_key_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_kms_store_v1_database_key_proto_rawDesc = []byte{
|
||||
0x0a, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x6b, 0x6d, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
|
||||
0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0x99, 0x01, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4b,
|
||||
0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49,
|
||||
0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49,
|
||||
0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x9a,
|
||||
0x02, 0x0a, 0x12, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61,
|
||||
0x74, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65,
|
||||
0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64,
|
||||
0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x13,
|
||||
0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x4b,
|
||||
0x65, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x15, 0x0a,
|
||||
0x06, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63,
|
||||
0x74, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4b,
|
||||
0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x38, 0x5a, 0x36, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||
0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6b, 0x6d, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_storage_kms_store_v1_database_key_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_kms_store_v1_database_key_proto_rawDescData = file_controller_storage_kms_store_v1_database_key_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_kms_store_v1_database_key_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_kms_store_v1_database_key_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_kms_store_v1_database_key_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_kms_store_v1_database_key_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_kms_store_v1_database_key_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_kms_store_v1_database_key_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_storage_kms_store_v1_database_key_proto_goTypes = []interface{}{
|
||||
(*DatabaseKey)(nil), // 0: controller.storage.kms.store.v1.DatabaseKey
|
||||
(*DatabaseKeyVersion)(nil), // 1: controller.storage.kms.store.v1.DatabaseKeyVersion
|
||||
(*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_kms_store_v1_database_key_proto_depIdxs = []int32{
|
||||
2, // 0: controller.storage.kms.store.v1.DatabaseKey.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // 1: controller.storage.kms.store.v1.DatabaseKeyVersion.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_kms_store_v1_database_key_proto_init() }
|
||||
func file_controller_storage_kms_store_v1_database_key_proto_init() {
|
||||
if File_controller_storage_kms_store_v1_database_key_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_kms_store_v1_database_key_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DatabaseKey); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_storage_kms_store_v1_database_key_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DatabaseKeyVersion); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_kms_store_v1_database_key_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_kms_store_v1_database_key_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_kms_store_v1_database_key_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_kms_store_v1_database_key_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_kms_store_v1_database_key_proto = out.File
|
||||
file_controller_storage_kms_store_v1_database_key_proto_rawDesc = nil
|
||||
file_controller_storage_kms_store_v1_database_key_proto_goTypes = nil
|
||||
file_controller_storage_kms_store_v1_database_key_proto_depIdxs = nil
|
||||
}
|
||||
+43
-2
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -25,10 +26,12 @@ func TestRootKey(t *testing.T, conn *gorm.DB, scopeId string) *RootKey {
|
||||
return k
|
||||
}
|
||||
|
||||
func TestRootKeyVersion(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, rootId string, key []byte) *RootKeyVersion {
|
||||
func TestRootKeyVersion(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, rootId string) (kv *RootKeyVersion, kvWrapper wrapping.Wrapper) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
rootKeyVersionWrapper := db.TestWrapper(t)
|
||||
key := rootKeyVersionWrapper.(*aead.Wrapper).GetKeyBytes()
|
||||
k, err := NewRootKeyVersion(rootId, key)
|
||||
require.NoError(err)
|
||||
id, err := newRootKeyVersionId()
|
||||
@@ -38,7 +41,11 @@ func TestRootKeyVersion(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, r
|
||||
require.NoError(err)
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
_, err = rootKeyVersionWrapper.(*aead.Wrapper).SetConfig(map[string]string{
|
||||
"key_id": k.GetPrivateId(),
|
||||
})
|
||||
require.NoError(err)
|
||||
return k, rootKeyVersionWrapper
|
||||
}
|
||||
|
||||
func TestKms(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper) *Kms {
|
||||
@@ -53,3 +60,37 @@ func TestKms(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper) *Kms {
|
||||
require.NoError(err)
|
||||
return kms
|
||||
}
|
||||
|
||||
func TestDatabaseKey(t *testing.T, conn *gorm.DB, rootKeyId string) *DatabaseKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
require.NoError(conn.Where("root_key_id = ?", rootKeyId).Delete(AllocDatabaseKey()).Error)
|
||||
k, err := NewDatabaseKey(rootKeyId)
|
||||
require.NoError(err)
|
||||
id, err := newDatabaseKeyId()
|
||||
require.NoError(err)
|
||||
k.PrivateId = id
|
||||
k.RootKeyId = rootKeyId
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestDatabaseKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, databaseKeyId string, key []byte) *DatabaseKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
rootKeyVersionId := rootKeyVersionWrapper.KeyID()
|
||||
require.NotEmpty(rootKeyVersionId)
|
||||
k, err := NewDatabaseKeyVersion(databaseKeyId, key, rootKeyVersionId)
|
||||
require.NoError(err)
|
||||
id, err := newDatabaseKeyVersionId()
|
||||
require.NoError(err)
|
||||
k.PrivateId = id
|
||||
err = k.Encrypt(context.Background(), rootKeyVersionWrapper)
|
||||
require.NoError(err)
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
@@ -30,7 +30,38 @@ func Test_TestRootKeyVersion(t *testing.T) {
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
k := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId, []byte("test key"))
|
||||
k, _ := kms.TestRootKeyVersion(t, conn, wrapper, rk.PrivateId)
|
||||
require.NotNil(k)
|
||||
assert.NotEmpty(k.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestDatabaseKey(t *testing.T) {
|
||||
t.Helper()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
k := kms.TestRootKey(t, conn, org.PublicId)
|
||||
require.NotNil(k)
|
||||
assert.NotEmpty(k.PrivateId)
|
||||
|
||||
dk := kms.TestDatabaseKey(t, conn, k.PrivateId)
|
||||
require.NotNil(dk)
|
||||
assert.NotEmpty(dk.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestDatabaseKeyVersion(t *testing.T) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
kmsWrapper := db.TestWrapper(t)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, kmsWrapper))
|
||||
require.NoError(conn.Where("1=1").Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, org.PublicId)
|
||||
_, rootKeyVersionWrapper := kms.TestRootKeyVersion(t, conn, kmsWrapper, rk.PrivateId)
|
||||
dk := kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
dv := kms.TestDatabaseKeyVersion(t, conn, rootKeyVersionWrapper, dk.PrivateId, []byte("test dek key"))
|
||||
require.NotNil(dv)
|
||||
require.NotEmpty(dv.PrivateId)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package controller.storage.kms.store.v1;
|
||||
option go_package = "github.com/hashicorp/boundary/internal/kms/store;store";
|
||||
|
||||
import "controller/storage/timestamp/v1/timestamp.proto";
|
||||
import "controller/custom_options/v1/options.proto";
|
||||
|
||||
message DatabaseKey {
|
||||
// private_id is used to access the key via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string private_id = 10;
|
||||
|
||||
// root key id for the key
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string root_key_id = 20;
|
||||
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 30;
|
||||
}
|
||||
|
||||
message DatabaseKeyVersion {
|
||||
// private_id is used to access the key version via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string private_id = 10;
|
||||
|
||||
// database id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string database_key_id = 20;
|
||||
|
||||
// root_key_version_id of the version of the root key data.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string root_key_version_id = 30;
|
||||
|
||||
// plain-text of the key data. we are NOT storing this plain-text key
|
||||
// in the db.
|
||||
// @inject_tag: `gorm:"-" wrapping:"pt,key_data"`
|
||||
bytes key = 40;
|
||||
|
||||
// ciphertext key data stored in the database
|
||||
// @inject_tag: `gorm:"column:key;not_null" wrapping:"ct,key_data"`
|
||||
bytes ct_key = 50;
|
||||
|
||||
// version of the key data. This is not used for optimistic locking, since
|
||||
// key versions are immutable. It's just the version of the key.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 60;
|
||||
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 70;
|
||||
}
|
||||
Reference in new issue
Block a user