support for additional deks: oplog, session and token and new CreateKeysTx() (#321)
This commit is contained in:
47 files changed
+6863
-85
No files matched your search
@@ -86,6 +86,9 @@ protobuild:
|
||||
@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/kms/store/oplog_key.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/kms/store/token_key.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/kms/store/session_key.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/target/store/target.pb.go
|
||||
|
||||
@rm -R ${TMP_DIR}
|
||||
|
||||
@@ -2613,39 +2613,41 @@ commit;
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
├────────────────────────────────────────────────────────────────┐ ○
|
||||
├────────────────────────────────────┐ ○ ┼
|
||||
│ ○ ┼ ┌────────────────────────┐
|
||||
┼ ┼ ┌────────────────────────┐ │ kms_session_key │
|
||||
┌────────────────────────┐ ┌────────────────────────┐ │ kms_oplog_key │ ├────────────────────────┤
|
||||
│ kms_root_key │ │ kms_database_key │ ├────────────────────────┤ │private_id │
|
||||
├────────────────────────┤ ├────────────────────────┤ │private_id │ │root_key_id │
|
||||
│private_id │ │private_id │ │root_key_id │ │ │
|
||||
│scope_id │ │root_key_id │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└────────────────────────┘ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘
|
||||
┼ ┼ ┼ ┼
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
┼ ┼ ┼ ┼
|
||||
╱│╲ ╱│╲ ╱│╲ ╱│╲
|
||||
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
|
||||
│ kms_root_key_version │ │kms_database_key_version│ │ kms_oplog_key_version │ │kms_session_key_version │
|
||||
├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤
|
||||
│private_id │ │private_id │ │private_id │ │private_id │
|
||||
│root_key_id │ │database_key_id │ │oplog_key_id │ │session_key_id │
|
||||
│key │ │root_key_id │ │root_key_id │ │root_key_id │
|
||||
│version │ │key │ │key │ │key │
|
||||
│ │ │version │ │version │ │version │
|
||||
└────────────────────────┘ └────────────────────────┘ │ │ │ │
|
||||
┼ ┼ └────────────────────────┘ │ │
|
||||
│ ○ ┼ └────────────────────────┘
|
||||
├────────────────────────────────────┘ ○ ┼
|
||||
├────────────────────────────────────────────────────────────────┘ ○
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────┐ ○
|
||||
├────────────────────────────────────────────────────────────────┐ ○ ┼
|
||||
├────────────────────────────────────┐ ○ ┼ ┌────────────────────────┐
|
||||
│ ○ ┼ ┌────────────────────────┐ │ kms_token_key │
|
||||
┼ ┼ ┌────────────────────────┐ │ kms_session_key │ ├────────────────────────┤
|
||||
┌────────────────────────┐ ┌────────────────────────┐ │ kms_oplog_key │ ├────────────────────────┤ │private_id │
|
||||
│ kms_root_key │ │ kms_database_key │ ├────────────────────────┤ │private_id │ │root_key_id │
|
||||
├────────────────────────┤ ├────────────────────────┤ │private_id │ │root_key_id │ │ │
|
||||
│private_id │ │private_id │ │root_key_id │ │ │ │ │
|
||||
│scope_id │ │root_key_id │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │ └────────────────────────┘
|
||||
└────────────────────────┘ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘ ┼
|
||||
┼ ┼ ┼ ┼ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
┼ ┼ ┼ ┼ │
|
||||
╱│╲ ╱│╲ ╱│╲ ╱│╲ ┼
|
||||
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ╱│╲
|
||||
│ kms_root_key_version │ │kms_database_key_version│ │ kms_oplog_key_version │ │kms_session_key_version │ ┌────────────────────────┐
|
||||
├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ │ kms_token_key_version │
|
||||
│private_id │ │private_id │ │private_id │ │private_id │ ├────────────────────────┤
|
||||
│root_key_id │ │database_key_id │ │oplog_key_id │ │session_key_id │ │private_id │
|
||||
│key │ │root_key_id │ │root_key_id │ │root_key_id │ │token_key_id │
|
||||
│version │ │key │ │key │ │key │ │root_key_id │
|
||||
│ │ │version │ │version │ │version │ │key │
|
||||
└────────────────────────┘ └────────────────────────┘ │ │ │ │ │version │
|
||||
┼ ┼ └────────────────────────┘ │ │ │ │
|
||||
│ ○ ┼ └────────────────────────┘ │ │
|
||||
├────────────────────────────────────┘ ○ ┼ └────────────────────────┘
|
||||
├────────────────────────────────────────────────────────────────┘ ○ ┼
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────┘ ○
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
|
||||
create table kms_root_key (
|
||||
@@ -2870,6 +2872,62 @@ create trigger
|
||||
before insert on kms_session_key_version
|
||||
for each row execute procedure kms_version_column('session_key_id');
|
||||
|
||||
create table kms_token_key (
|
||||
private_id wt_private_id primary key,
|
||||
root_key_id wt_private_id not null unique -- there can be only one token dek per root key
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
create_time wt_timestamp
|
||||
);
|
||||
|
||||
-- define the immutable fields for kms_token_key (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on kms_token_key
|
||||
for each row execute procedure immutable_columns('private_id', 'root_key_id', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on kms_token_key
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create table kms_token_key_version (
|
||||
private_id wt_private_id primary key,
|
||||
token_key_id wt_private_id not null
|
||||
references kms_token_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
root_key_version_id wt_private_id not null
|
||||
references kms_root_key_version(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
version wt_version,
|
||||
key bytea not null,
|
||||
create_time wt_timestamp,
|
||||
unique(token_key_id, version)
|
||||
);
|
||||
|
||||
-- define the immutable fields for kms_token_key_version (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on kms_token_key_version
|
||||
for each row execute procedure immutable_columns('private_id', 'token_key_id', 'root_key_version_id', 'version', 'key', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on kms_token_key_version
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
kms_version_column
|
||||
before insert on kms_token_key_version
|
||||
for each row execute procedure kms_version_column('token_key_id');
|
||||
|
||||
insert into oplog_ticket
|
||||
(name, version)
|
||||
values
|
||||
|
||||
@@ -1,39 +1,41 @@
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
├────────────────────────────────────────────────────────────────┐ ○
|
||||
├────────────────────────────────────┐ ○ ┼
|
||||
│ ○ ┼ ┌────────────────────────┐
|
||||
┼ ┼ ┌────────────────────────┐ │ kms_session_key │
|
||||
┌────────────────────────┐ ┌────────────────────────┐ │ kms_oplog_key │ ├────────────────────────┤
|
||||
│ kms_root_key │ │ kms_database_key │ ├────────────────────────┤ │private_id │
|
||||
├────────────────────────┤ ├────────────────────────┤ │private_id │ │root_key_id │
|
||||
│private_id │ │private_id │ │root_key_id │ │ │
|
||||
│scope_id │ │root_key_id │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└────────────────────────┘ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘
|
||||
┼ ┼ ┼ ┼
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
┼ ┼ ┼ ┼
|
||||
╱│╲ ╱│╲ ╱│╲ ╱│╲
|
||||
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
|
||||
│ kms_root_key_version │ │kms_database_key_version│ │ kms_oplog_key_version │ │kms_session_key_version │
|
||||
├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤
|
||||
│private_id │ │private_id │ │private_id │ │private_id │
|
||||
│root_key_id │ │database_key_id │ │oplog_key_id │ │session_key_id │
|
||||
│key │ │root_key_id │ │root_key_id │ │root_key_id │
|
||||
│version │ │key │ │key │ │key │
|
||||
│ │ │version │ │version │ │version │
|
||||
└────────────────────────┘ └────────────────────────┘ │ │ │ │
|
||||
┼ ┼ └────────────────────────┘ │ │
|
||||
│ ○ ┼ └────────────────────────┘
|
||||
├────────────────────────────────────┘ ○ ┼
|
||||
├────────────────────────────────────────────────────────────────┘ ○
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────┐ ○
|
||||
├────────────────────────────────────────────────────────────────┐ ○ ┼
|
||||
├────────────────────────────────────┐ ○ ┼ ┌────────────────────────┐
|
||||
│ ○ ┼ ┌────────────────────────┐ │ kms_token_key │
|
||||
┼ ┼ ┌────────────────────────┐ │ kms_session_key │ ├────────────────────────┤
|
||||
┌────────────────────────┐ ┌────────────────────────┐ │ kms_oplog_key │ ├────────────────────────┤ │private_id │
|
||||
│ kms_root_key │ │ kms_database_key │ ├────────────────────────┤ │private_id │ │root_key_id │
|
||||
├────────────────────────┤ ├────────────────────────┤ │private_id │ │root_key_id │ │ │
|
||||
│private_id │ │private_id │ │root_key_id │ │ │ │ │
|
||||
│scope_id │ │root_key_id │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │ └────────────────────────┘
|
||||
└────────────────────────┘ └────────────────────────┘ └────────────────────────┘ └────────────────────────┘ ┼
|
||||
┼ ┼ ┼ ┼ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
┼ ┼ ┼ ┼ │
|
||||
╱│╲ ╱│╲ ╱│╲ ╱│╲ ┼
|
||||
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐ ╱│╲
|
||||
│ kms_root_key_version │ │kms_database_key_version│ │ kms_oplog_key_version │ │kms_session_key_version │ ┌────────────────────────┐
|
||||
├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ ├────────────────────────┤ │ kms_token_key_version │
|
||||
│private_id │ │private_id │ │private_id │ │private_id │ ├────────────────────────┤
|
||||
│root_key_id │ │database_key_id │ │oplog_key_id │ │session_key_id │ │private_id │
|
||||
│key │ │root_key_id │ │root_key_id │ │root_key_id │ │token_key_id │
|
||||
│version │ │key │ │key │ │key │ │root_key_id │
|
||||
│ │ │version │ │version │ │version │ │key │
|
||||
└────────────────────────┘ └────────────────────────┘ │ │ │ │ │version │
|
||||
┼ ┼ └────────────────────────┘ │ │ │ │
|
||||
│ ○ ┼ └────────────────────────┘ │ │
|
||||
├────────────────────────────────────┘ ○ ┼ └────────────────────────┘
|
||||
├────────────────────────────────────────────────────────────────┘ ○ ┼
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────┘ ○
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
|
||||
create table kms_root_key (
|
||||
@@ -258,6 +260,62 @@ create trigger
|
||||
before insert on kms_session_key_version
|
||||
for each row execute procedure kms_version_column('session_key_id');
|
||||
|
||||
create table kms_token_key (
|
||||
private_id wt_private_id primary key,
|
||||
root_key_id wt_private_id not null unique -- there can be only one token dek per root key
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
create_time wt_timestamp
|
||||
);
|
||||
|
||||
-- define the immutable fields for kms_token_key (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on kms_token_key
|
||||
for each row execute procedure immutable_columns('private_id', 'root_key_id', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on kms_token_key
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create table kms_token_key_version (
|
||||
private_id wt_private_id primary key,
|
||||
token_key_id wt_private_id not null
|
||||
references kms_token_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
root_key_version_id wt_private_id not null
|
||||
references kms_root_key_version(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
version wt_version,
|
||||
key bytea not null,
|
||||
create_time wt_timestamp,
|
||||
unique(token_key_id, version)
|
||||
);
|
||||
|
||||
-- define the immutable fields for kms_token_key_version (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on kms_token_key_version
|
||||
for each row execute procedure immutable_columns('private_id', 'token_key_id', 'root_key_version_id', 'version', 'key', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on kms_token_key_version
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
kms_version_column
|
||||
before insert on kms_token_key_version
|
||||
for each row execute procedure kms_version_column('token_key_id');
|
||||
|
||||
insert into oplog_ticket
|
||||
(name, version)
|
||||
values
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
)
|
||||
|
||||
// CreateScope will create a scope in the repository and return the written
|
||||
@@ -126,10 +125,6 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
|
||||
if reader == nil {
|
||||
reader = rand.Reader
|
||||
}
|
||||
rootKey, err := uuid.GenerateRandomBytesWithReader(32, reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create scope: error generating random bytes for scope root key: %w", err)
|
||||
}
|
||||
|
||||
_, err = r.writer.DoTx(
|
||||
ctx,
|
||||
@@ -146,10 +141,10 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
|
||||
|
||||
s := scopeRaw.(*Scope)
|
||||
|
||||
// Create the scope's root key
|
||||
_, _, err := kms.CreateRootKeyTx(ctx, w, externalWrappers.Root(), s.PublicId, rootKey)
|
||||
// Create the scope's keys
|
||||
_, err = kms.CreateKeysTx(ctx, dbr, w, externalWrappers.Root(), reader, s.PublicId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating scope root key: %w", err)
|
||||
return fmt.Errorf("error creating scope keys: %w", err)
|
||||
}
|
||||
|
||||
kmsRepo, err := kms.NewRepository(dbr, w)
|
||||
|
||||
@@ -21,6 +21,9 @@ const (
|
||||
|
||||
// KeyPurposeTokens is used for token encryption
|
||||
KeyPurposeTokens
|
||||
|
||||
// KeyPurposeSessions is used for session encryption
|
||||
KeyPurposeSessions
|
||||
)
|
||||
|
||||
// String returns the key purpose cast as a string, just so it can be called as
|
||||
@@ -35,6 +38,56 @@ func (k KeyPurpose) String() string {
|
||||
return "recovery"
|
||||
case KeyPurposeTokens:
|
||||
return "tokens"
|
||||
case KeyPurposeSessions:
|
||||
return "sessions"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// KeyType allows the kms repo to return a map[KeyType]Key which can be easily
|
||||
// used without type casting.
|
||||
type KeyType uint
|
||||
|
||||
const (
|
||||
KeyTypeUnknown KeyType = iota
|
||||
KeyTypeRootKey
|
||||
KeyTypeRootKeyVersion
|
||||
KeyTypeDatabaseKey
|
||||
KeyTypeDatabaseKeyVersion
|
||||
KeyTypeOplogKey
|
||||
KeyTypeOplogKeyVersion
|
||||
KeyTypeTokenKey
|
||||
KeyTypeTokenKeyVersion
|
||||
KeyTypeSessionKey
|
||||
KeyTypeSessionKeyVersion
|
||||
)
|
||||
|
||||
// String returns the key type cast as a string, just so it can be called as
|
||||
// a function instead of direct casting elsewhere, yw
|
||||
func (k KeyType) String() string {
|
||||
switch k {
|
||||
case KeyTypeRootKey:
|
||||
return "rootKey"
|
||||
case KeyTypeRootKeyVersion:
|
||||
return "rootKeyVersion"
|
||||
case KeyTypeDatabaseKey:
|
||||
return "databaseKey"
|
||||
case KeyTypeDatabaseKeyVersion:
|
||||
return "databaseKeyVersion"
|
||||
case KeyTypeOplogKey:
|
||||
return "oplogKey"
|
||||
case KeyTypeOplogKeyVersion:
|
||||
return "oplogKeyVersion"
|
||||
case KeyTypeTokenKey:
|
||||
return "tokenKey"
|
||||
case KeyTypeTokenKeyVersion:
|
||||
return "tokenKeyVersion"
|
||||
case KeyTypeSessionKey:
|
||||
return "sessionKey"
|
||||
case KeyTypeSessionKeyVersion:
|
||||
return "sessionKeyVersion"
|
||||
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (k *DatabaseKey) Clone() interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the kms root
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// before it's written.
|
||||
func (k *DatabaseKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
|
||||
@@ -11,6 +11,12 @@ const (
|
||||
RootKeyVersionPrefix = "krkv"
|
||||
DatabaseKeyPrefix = "kdk"
|
||||
DatabaseKeyVersionPrefix = "kdkv"
|
||||
OplogKeyPrefix = "kopk"
|
||||
OplogKeyVersionPrefix = "kopkv"
|
||||
TokenKeyPrefix = "ktk"
|
||||
TokenKeyVersionPrefix = "ktv"
|
||||
SessionKeyPrefix = "ksk"
|
||||
SessionKeyVersionPrefix = "kskv"
|
||||
)
|
||||
|
||||
func newRootKeyId() (string, error) {
|
||||
@@ -44,3 +50,51 @@ func newDatabaseKeyVersionId() (string, error) {
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newOplogKeyId() (string, error) {
|
||||
id, err := db.NewPublicId(OplogKeyPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new oplog key id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newOplogKeyVersionId() (string, error) {
|
||||
id, err := db.NewPublicId(OplogKeyVersionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new oplog key version id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newTokenKeyId() (string, error) {
|
||||
id, err := db.NewPublicId(TokenKeyPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new token key id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newTokenKeyVersionId() (string, error) {
|
||||
id, err := db.NewPublicId(TokenKeyVersionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new token key version id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newSessionKeyId() (string, error) {
|
||||
id, err := db.NewPublicId(SessionKeyPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session key id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newSessionKeyVersionId() (string, error) {
|
||||
id, err := db.NewPublicId(SessionKeyVersionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session key version id: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -30,4 +30,34 @@ func Test_Ids(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, DatabaseKeyVersionPrefix+"_"))
|
||||
})
|
||||
t.Run("kopk", func(t *testing.T) {
|
||||
id, err := newOplogKeyId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, OplogKeyPrefix+"_"))
|
||||
})
|
||||
t.Run("kopkv", func(t *testing.T) {
|
||||
id, err := newOplogKeyVersionId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, OplogKeyVersionPrefix+"_"))
|
||||
})
|
||||
t.Run("ktk", func(t *testing.T) {
|
||||
id, err := newTokenKeyId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, TokenKeyPrefix+"_"))
|
||||
})
|
||||
t.Run("ktkv", func(t *testing.T) {
|
||||
id, err := newTokenKeyVersionId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, TokenKeyVersionPrefix+"_"))
|
||||
})
|
||||
t.Run("ksk", func(t *testing.T) {
|
||||
id, err := newSessionKeyId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, SessionKeyPrefix+"_"))
|
||||
})
|
||||
t.Run("kskv", func(t *testing.T) {
|
||||
id, err := newSessionKeyVersionId()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(id, SessionKeyVersionPrefix+"_"))
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -149,7 +149,7 @@ func generateKeyId(scopeId string, purpose KeyPurpose, version uint32) string {
|
||||
// decryption.
|
||||
func (k *Kms) GetWrapper(ctx context.Context, scopeId string, purpose KeyPurpose, opt ...Option) (wrapping.Wrapper, error) {
|
||||
switch purpose {
|
||||
case KeyPurposeOplog, KeyPurposeDatabase, KeyPurposeTokens:
|
||||
case KeyPurposeOplog, KeyPurposeDatabase, KeyPurposeTokens, KeyPurposeSessions:
|
||||
case KeyPurposeUnknown:
|
||||
return nil, errors.New("key purpose not specified")
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultOplogKeyTableName = "kms_oplog_key"
|
||||
)
|
||||
|
||||
type OplogKey struct {
|
||||
*store.OplogKey
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewOplogKey creates a new in memory key. No options
|
||||
// are currently supported.
|
||||
func NewOplogKey(rootKeyId string, opt ...Option) (*OplogKey, error) {
|
||||
if rootKeyId == "" {
|
||||
return nil, fmt.Errorf("new root key: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
c := &OplogKey{
|
||||
OplogKey: &store.OplogKey{
|
||||
RootKeyId: rootKeyId,
|
||||
},
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// AllocOplogKey will allocate a key
|
||||
func AllocOplogKey() OplogKey {
|
||||
return OplogKey{
|
||||
OplogKey: &store.OplogKey{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key
|
||||
func (k *OplogKey) Clone() interface{} {
|
||||
cp := proto.Clone(k.OplogKey)
|
||||
return &OplogKey{
|
||||
OplogKey: cp.(*store.OplogKey),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// before it's written.
|
||||
func (k *OplogKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("oplog key vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.RootKeyId == "" {
|
||||
return fmt.Errorf("oplog key vet for write: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *OplogKey) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultOplogKeyTableName
|
||||
}
|
||||
|
||||
// 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 *OplogKey) 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 OplogKey attributes are
|
||||
// immutable and those tests are covered by TestOplogKey_ImmutableFields
|
||||
|
||||
func TestOplogKey_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.OplogKey
|
||||
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.OplogKey {
|
||||
k := kms.AllocOplogKey()
|
||||
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.AllocOplogKey()).Error)
|
||||
got, err := kms.NewOplogKey(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.OplogKeyPrefix)
|
||||
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 TestOplogKey_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.AllocOplogKey()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.OplogKey
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestOplogKey(t, conn, rk.PrivateId),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.OplogKey {
|
||||
k := kms.AllocOplogKey()
|
||||
id, err := db.NewPublicId(kms.OplogKeyPrefix)
|
||||
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.AllocOplogKey()
|
||||
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.AllocOplogKey()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOplogKey_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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.OplogKey).OplogKey, k.OplogKey))
|
||||
})
|
||||
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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestOplogKey(t, conn, rk2.PrivateId)
|
||||
|
||||
cp := k.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.OplogKey).OplogKey, k2.OplogKey))
|
||||
})
|
||||
}
|
||||
|
||||
func TestOplogKey_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultOplogKeyTableName
|
||||
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.AllocOplogKey()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocOplogKey()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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 (
|
||||
DefaultOplogKeyVersionTableName = "kms_oplog_key_version"
|
||||
)
|
||||
|
||||
type OplogKeyVersion struct {
|
||||
*store.OplogKeyVersion
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// OplogKeyVersion creates a new in memory key version. No options are
|
||||
// currently supported.
|
||||
func NewOplogKeyVersion(oplogKeyId string, key []byte, rootKeyVersionId string, opt ...Option) (*OplogKeyVersion, error) {
|
||||
if oplogKeyId == "" {
|
||||
return nil, fmt.Errorf("new oplog key version: missing oplog key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("new oplog key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rootKeyVersionId == "" {
|
||||
return nil, fmt.Errorf("new oplog key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
|
||||
k := &OplogKeyVersion{
|
||||
OplogKeyVersion: &store.OplogKeyVersion{
|
||||
OplogKeyId: oplogKeyId,
|
||||
RootKeyVersionId: rootKeyVersionId,
|
||||
Key: key,
|
||||
},
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// AllocOplogKeyVersion allocates a key version
|
||||
func AllocOplogKeyVersion() OplogKeyVersion {
|
||||
return OplogKeyVersion{
|
||||
OplogKeyVersion: &store.OplogKeyVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key version
|
||||
func (k *OplogKeyVersion) Clone() interface{} {
|
||||
cp := proto.Clone(k.OplogKeyVersion)
|
||||
return &OplogKeyVersion{
|
||||
OplogKeyVersion: cp.(*store.OplogKeyVersion),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// version before it's written.
|
||||
func (k *OplogKeyVersion) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("oplog key version vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.CtKey == nil {
|
||||
return fmt.Errorf("oplog key version vet for write: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.OplogKeyId == "" {
|
||||
return fmt.Errorf("oplog key version vet for write: missing oplog key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.RootKeyVersionId == "" {
|
||||
return fmt.Errorf("oplog key version vet for write: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *OplogKeyVersion) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultOplogKeyVersionTableName
|
||||
}
|
||||
|
||||
// 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 *OplogKeyVersion) SetTableName(n string) {
|
||||
k.tableName = n
|
||||
}
|
||||
|
||||
// Encrypt will encrypt the key version's key
|
||||
func (k *OplogKeyVersion) Encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.OplogKeyVersion directly
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, k.OplogKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting kms oplog key version: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt will decrypt the key version's key
|
||||
func (k *OplogKeyVersion) Decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.OplogKeyVersion directly
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, k.OplogKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting kms oplog 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 OplogKeyVersion attributes are
|
||||
// immutable and those tests are covered by TestOplogKey_ImmutableFields
|
||||
|
||||
func TestOplogKeyVersion_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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
oplogKeyId string
|
||||
key []byte
|
||||
rootKeyVersionId string
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *kms.OplogKeyVersion
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty-id",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
oplogKeyId: dk.PrivateId,
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-root-key-version-id",
|
||||
args: args{
|
||||
oplogKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
oplogKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
want: func() *kms.OplogKeyVersion {
|
||||
k := kms.AllocOplogKeyVersion()
|
||||
k.RootKeyVersionId = rkv.PrivateId
|
||||
k.Key = []byte("test key")
|
||||
k.OplogKeyId = 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.AllocOplogKeyVersion()).Error)
|
||||
got, err := kms.NewOplogKeyVersion(tt.args.oplogKeyId, 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.OplogKeyPrefix)
|
||||
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 TestOplogKeyVersion_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)
|
||||
opk := kms.TestOplogKey(t, conn, rk.PrivateId)
|
||||
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocOplogKeyVersion()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.OplogKeyVersion
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestOplogKeyVersion(t, conn, rkvWrapper, opk.PrivateId, []byte("test key")),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.OplogKeyVersion {
|
||||
k := kms.AllocOplogKeyVersion()
|
||||
id, err := db.NewPublicId(kms.OplogKeyVersionPrefix)
|
||||
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.AllocOplogKeyVersion()
|
||||
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.AllocOplogKeyVersion()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOplogKeyVersion_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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
kv := kms.TestOplogKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
cp := kv.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.OplogKeyVersion).OplogKeyVersion, kv.OplogKeyVersion))
|
||||
})
|
||||
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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestOplogKey(t, conn, rk2.PrivateId)
|
||||
kv := kms.TestOplogKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
kv2 := kms.TestOplogKeyVersion(t, conn, rkvWrapper2, k2.PrivateId, []byte("test key 2"))
|
||||
|
||||
cp := kv.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.OplogKeyVersion).OplogKeyVersion, kv2.OplogKeyVersion))
|
||||
})
|
||||
}
|
||||
|
||||
func TestOplogKeyVersion_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultOplogKeyVersionTableName
|
||||
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.AllocOplogKeyVersion()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocOplogKeyVersion()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,13 @@ package kms
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
)
|
||||
|
||||
// Repository is the iam database repository
|
||||
@@ -57,3 +62,110 @@ func (r *Repository) list(ctx context.Context, resources interface{}, where stri
|
||||
func (r *Repository) DefaultLimit() int {
|
||||
return r.defaultLimit
|
||||
}
|
||||
|
||||
// KeyIder defines a common interface for all keys returned from CreateKeysTx in
|
||||
// a Keys map
|
||||
type KeyIder interface {
|
||||
GetPrivateId() string
|
||||
}
|
||||
|
||||
// Keys defines a return type for CreateKeysTx so the returned keys can be
|
||||
// easily accessed via their KeyType
|
||||
type Keys map[KeyType]KeyIder
|
||||
|
||||
// CreateKeysTx creates the root key and DEKs returns a map of the new keys This
|
||||
// function encapsulates all the work required within a db.TxHandler and allows
|
||||
// this capability to be shared with the iam repo.
|
||||
func CreateKeysTx(ctx context.Context, dbReader db.Reader, dbWriter db.Writer, rootWrapper wrapping.Wrapper, randomReader io.Reader, scopeId string) (Keys, error) {
|
||||
if dbReader == nil {
|
||||
return nil, fmt.Errorf("create keys: missing db reader: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if dbWriter == nil {
|
||||
return nil, fmt.Errorf("create keys: missing db writer: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rootWrapper == nil {
|
||||
return nil, fmt.Errorf("create keys: missing root wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if randomReader == nil {
|
||||
return nil, fmt.Errorf("create keys: missing random reader: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("create keys: missing scope id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k, err := generateKey(randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: error generating random bytes for root key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
rootKey, rootKeyVersion, err := createRootKeyTx(ctx, dbWriter, rootWrapper, scopeId, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: unable to create root key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
rkvWrapper := aead.NewWrapper(nil)
|
||||
if _, err := rkvWrapper.SetConfig(map[string]string{
|
||||
"key_id": rootKeyVersion.GetPrivateId(),
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("create keys: error setting config on aead root wrapper in scope %s: %w", scopeId, err)
|
||||
}
|
||||
if err := rkvWrapper.SetAESGCMKeyBytes(rootKeyVersion.GetKey()); err != nil {
|
||||
return nil, fmt.Errorf("create keys: error setting key bytes on aead root wrapper in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
k, err = generateKey(randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: error generating random bytes for database key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
dbKey, dbKeyVersion, err := createDatabaseKeyTx(ctx, dbReader, dbWriter, rkvWrapper, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: unable to create database key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
k, err = generateKey(randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: error generating random bytes for oplog key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
oplogKey, oplogKeyVersion, err := createOplogKeyTx(ctx, dbReader, dbWriter, rkvWrapper, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: unable to create oplog key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
k, err = generateKey(randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: error generating random bytes for session key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
sessionKey, sessionKeyVersion, err := createSessionKeyTx(ctx, dbReader, dbWriter, rkvWrapper, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: unable to create session key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
k, err = generateKey(randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: error generating random bytes for token key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
tokenKey, tokenKeyVersion, err := createTokenKeyTx(ctx, dbReader, dbWriter, rkvWrapper, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keys: unable to create token key in scope %s: %w", scopeId, err)
|
||||
}
|
||||
|
||||
keys := Keys{
|
||||
KeyTypeRootKey: rootKey,
|
||||
KeyTypeRootKeyVersion: rootKeyVersion,
|
||||
KeyTypeDatabaseKey: dbKey,
|
||||
KeyTypeDatabaseKeyVersion: dbKeyVersion,
|
||||
KeyTypeOplogKey: oplogKey,
|
||||
KeyTypeOplogKeyVersion: oplogKeyVersion,
|
||||
KeyTypeSessionKey: sessionKey,
|
||||
KeyTypeSessionKeyVersion: sessionKeyVersion,
|
||||
KeyTypeTokenKey: tokenKey,
|
||||
KeyTypeTokenKeyVersion: tokenKeyVersion,
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func generateKey(randomReader io.Reader) ([]byte, error) {
|
||||
k, err := uuid.GenerateRandomBytesWithReader(32, randomReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating random bytes for key: %w", err)
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func (r *Repository) CreateDatabaseKey(ctx context.Context, rkvWrapper wrapping.
|
||||
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 {
|
||||
if returnedDk, returnedDv, err = createDatabaseKeyTx(ctx, reader, w, rkvWrapper, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -31,10 +31,10 @@ func (r *Repository) CreateDatabaseKey(ctx context.Context, rkvWrapper wrapping.
|
||||
return returnedDk.(*DatabaseKey), returnedDv.(*DatabaseKeyVersion), err
|
||||
}
|
||||
|
||||
// CreateDatabaseKeyTx inserts into the db (via db.Writer) and returns the new database key
|
||||
// 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) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -283,8 +283,8 @@ func TestRepository_ListDatabaseKeys(t *testing.T) {
|
||||
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)
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, proj.PublicId)
|
||||
kms.TestDatabaseKey(t, conn, rk.PrivateId)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
)
|
||||
|
||||
// CreateOplogKey inserts into the repository and returns the new key and the
|
||||
// key version. There are no valid options at this time.
|
||||
func (r *Repository) CreateOplogKey(ctx context.Context, rkvWrapper wrapping.Wrapper, key []byte, opt ...Option) (*OplogKey, *OplogKeyVersion, 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 = createOplogKeyTx(ctx, reader, w, rkvWrapper, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: %w", err)
|
||||
}
|
||||
return returnedDk.(*OplogKey), returnedDv.(*OplogKeyVersion), err
|
||||
}
|
||||
|
||||
// createOplogKeyTx inserts into the db (via db.Writer) and returns the new key
|
||||
// and the 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 createOplogKeyTx(ctx context.Context, r db.Reader, w db.Writer, rkvWrapper wrapping.Wrapper, key []byte) (*OplogKey, *OplogKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, nil, fmt.Errorf("create oplog key: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, nil, fmt.Errorf("create oplog 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 oplog 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 oplog key: unable to lookup root key version %s: %w", rootKeyVersionId, err)
|
||||
}
|
||||
|
||||
opk := AllocOplogKey()
|
||||
opv := AllocOplogKeyVersion()
|
||||
id, err := newOplogKeyId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: %w", err)
|
||||
}
|
||||
opk.PrivateId = id
|
||||
opk.RootKeyId = rv.RootKeyId
|
||||
|
||||
id, err = newOplogKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: %w", err)
|
||||
}
|
||||
opv.PrivateId = id
|
||||
opv.OplogKeyId = opk.PrivateId
|
||||
opv.RootKeyVersionId = rootKeyVersionId
|
||||
opv.Key = key
|
||||
if err := opv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: %w", err)
|
||||
}
|
||||
|
||||
// no oplog entries for keys
|
||||
if err := w.Create(ctx, &opk); err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: key create: %w", err)
|
||||
}
|
||||
// no oplog entries for key versions
|
||||
if err := w.Create(ctx, &opv); err != nil {
|
||||
return nil, nil, fmt.Errorf("create oplog key: version create: %w", err)
|
||||
}
|
||||
|
||||
return &opk, &opv, err
|
||||
}
|
||||
|
||||
// LookupOplogKey will look up a key in the repository. If the key is not
|
||||
// found, it will return nil, nil.
|
||||
func (r *Repository) LookupOplogKey(ctx context.Context, privateId string, opt ...Option) (*OplogKey, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup oplog key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocOplogKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup oplog key: failed %w for %s", err, privateId)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteOplogKey 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) DeleteOplogKey(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete oplog key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocOplogKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete oplog 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 oplog key: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// ListOplogKeys will list the keys. Supports the WithLimit option.
|
||||
func (r *Repository) ListOplogKeys(ctx context.Context, opt ...Option) ([]*OplogKey, error) {
|
||||
var keys []*OplogKey
|
||||
err := r.list(ctx, &keys, "1=1", nil, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list oplog 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_CreateOplogKey(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)
|
||||
opk, opv, err := repo.CreateOplogKey(context.Background(), tt.args.keyWrapper, tt.args.key, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(opk)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotNil(opk.CreateTime)
|
||||
foundKey, err := repo.LookupOplogKey(context.Background(), opk.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKey, opk))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, opk.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(opv.CreateTime)
|
||||
foundKeyVersion, err := repo.LookupOplogKeyVersion(context.Background(), tt.args.keyWrapper, opv.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKeyVersion, opv))
|
||||
|
||||
// make sure there was no oplog written
|
||||
err = db.TestVerifyOplog(t, rw, opv.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_DeleteOplogKey(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.OplogKey
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestOplogKey(t, conn, rk.PrivateId),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.OplogKey {
|
||||
k := kms.AllocOplogKey()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.OplogKey {
|
||||
id, err := db.NewPublicId(kms.RootKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocOplogKey()
|
||||
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.DeleteOplogKey(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.LookupOplogKey(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_ListOplogKeys(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++ {
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, proj.PublicId)
|
||||
kms.TestOplogKey(t, conn, rk.PrivateId)
|
||||
require.NoError(err)
|
||||
}
|
||||
got, err := repo.ListOplogKeys(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"
|
||||
)
|
||||
|
||||
// CreateOplogKeyVersion inserts into the repository and returns the new key
|
||||
// version with its PrivateId. There are no valid options at this time.
|
||||
func (r *Repository) CreateOplogKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, oplogKeyId string, key []byte, opt ...Option) (*OplogKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("create oplog key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, fmt.Errorf("create oplog 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 oplog key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if oplogKeyId == "" {
|
||||
return nil, fmt.Errorf("create oplog key version: missing oplog key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("create oplog key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
kv := AllocOplogKeyVersion()
|
||||
id, err := newOplogKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create oplog key version: %w", err)
|
||||
}
|
||||
kv.PrivateId = id
|
||||
kv.RootKeyVersionId = rootKeyVersionId
|
||||
kv.Key = key
|
||||
kv.OplogKeyId = oplogKeyId
|
||||
if err := kv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("create oplog 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 oplog key version: %w for %s oplog key id", err, kv.OplogKeyId)
|
||||
}
|
||||
return returnedKey.(*OplogKeyVersion), err
|
||||
}
|
||||
|
||||
// LookupOplogKeyVersion will look up a key version in the repository. If
|
||||
// the key version is not found, it will return nil, nil.
|
||||
func (r *Repository) LookupOplogKeyVersion(ctx context.Context, keyWrapper wrapping.Wrapper, privateId string, opt ...Option) (*OplogKeyVersion, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup oplog key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if keyWrapper == nil {
|
||||
return nil, fmt.Errorf("lookup oplog key version: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocOplogKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup oplog key version: failed %w for %s", err, privateId)
|
||||
}
|
||||
if err := k.Decrypt(ctx, keyWrapper); err != nil {
|
||||
return nil, fmt.Errorf("lookup oplog key version: decrypt: %w", err)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteOplogKeyVersion 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) DeleteOplogKeyVersion(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete oplog key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocOplogKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete oplog 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 oplog key version: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// LatestOplogKeyVersion searches for the key version with the highest
|
||||
// version number. When no results are found, it returns nil,
|
||||
// db.ErrRecordNotFound.
|
||||
func (r *Repository) LatestOplogKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, oplogKeyId string, opt ...Option) (*OplogKeyVersion, error) {
|
||||
if oplogKeyId == "" {
|
||||
return nil, fmt.Errorf("latest oplog key version: missing oplog key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("latest oplog key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var foundKeys []*OplogKeyVersion
|
||||
if err := r.reader.SearchWhere(ctx, &foundKeys, "oplog_key_id = ?", []interface{}{oplogKeyId}, db.WithLimit(1), db.WithOrder("version desc")); err != nil {
|
||||
return nil, fmt.Errorf("latest oplog key version: failed %w for %s", err, oplogKeyId)
|
||||
}
|
||||
if len(foundKeys) == 0 {
|
||||
return nil, db.ErrRecordNotFound
|
||||
}
|
||||
if err := foundKeys[0].Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("latest oplog key version: %w", err)
|
||||
}
|
||||
return foundKeys[0], nil
|
||||
}
|
||||
|
||||
// ListOplogKeyVersions will lists versions of a key. Supports the WithLimit option.
|
||||
func (r *Repository) ListOplogKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, oplogKeyId string, opt ...Option) ([]*OplogKeyVersion, error) {
|
||||
if oplogKeyId == "" {
|
||||
return nil, fmt.Errorf("list oplog key versions: missing oplog key id %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("list oplog key versions: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var versions []*OplogKeyVersion
|
||||
err := r.list(ctx, &versions, "oplog_key_id = ?", []interface{}{oplogKeyId}, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list oplog key versions: %w", err)
|
||||
}
|
||||
for i, k := range versions {
|
||||
if err := k.Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("list oplog 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_CreateOplogKeyVersion(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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key []byte
|
||||
oplogKeyId 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,
|
||||
oplogKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-rkv-wrapper",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: wrapper,
|
||||
oplogKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
key: nil,
|
||||
keyWrapper: wrapper,
|
||||
oplogKeyId: 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.CreateOplogKeyVersion(context.Background(), tt.args.keyWrapper, tt.args.oplogKeyId, 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.LookupOplogKeyVersion(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_DeleteOplogKeyVersion(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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key *kms.OplogKeyVersion
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestOplogKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("oplog key")),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.OplogKeyVersion {
|
||||
k := kms.AllocOplogKeyVersion()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.OplogKeyVersion {
|
||||
id, err := db.NewPublicId(kms.OplogKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocOplogKeyVersion()
|
||||
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.DeleteOplogKeyVersion(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.LookupOplogKeyVersion(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_LatestOplogKeyVersion(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.TestOplogKey(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.AllocOplogKeyVersion()).Error)
|
||||
testKeys := []*kms.OplogKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestOplogKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("test oplog key"))
|
||||
testKeys = append(testKeys, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testKeys))
|
||||
got, err := repo.LatestOplogKeyVersion(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_ListOplogKeyVersions(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.TestOplogKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
oplogKeyId 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{
|
||||
oplogKeyId: 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,
|
||||
oplogKeyId: dk.PrivateId,
|
||||
},
|
||||
wantCnt: repo.DefaultLimit(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
oplogKeyId: dk.PrivateId,
|
||||
opt: []kms.Option{kms.WithLimit(3)},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-org",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
oplogKeyId: "bad-id",
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: nil,
|
||||
oplogKeyId: 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.AllocOplogKeyVersion()).Error)
|
||||
keyVersions := []*kms.OplogKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestOplogKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("oplog key"))
|
||||
keyVersions = append(keyVersions, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(keyVersions))
|
||||
got, err := repo.ListOplogKeyVersions(context.Background(), tt.args.keyWrapper, tt.args.oplogKeyId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ func (r *Repository) CreateRootKey(ctx context.Context, keyWrapper wrapping.Wrap
|
||||
db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
var err error
|
||||
if returnedRk, returnedKv, err = CreateRootKeyTx(ctx, w, keyWrapper, scopeId, key); err != nil {
|
||||
if returnedRk, returnedKv, err = createRootKeyTx(ctx, w, keyWrapper, scopeId, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -30,10 +30,10 @@ func (r *Repository) CreateRootKey(ctx context.Context, keyWrapper wrapping.Wrap
|
||||
return returnedRk.(*RootKey), returnedKv.(*RootKeyVersion), err
|
||||
}
|
||||
|
||||
// CreateRootKeyTx inserts into the db (via db.Writer) and returns the new root key
|
||||
// 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.
|
||||
func CreateRootKeyTx(ctx context.Context, w db.Writer, keyWrapper wrapping.Wrapper, scopeId string, key []byte) (*RootKey, *RootKeyVersion, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
)
|
||||
|
||||
// CreateSessionKey inserts into the repository and returns the new key and the
|
||||
// key version. There are no valid options at this time.
|
||||
func (r *Repository) CreateSessionKey(ctx context.Context, rkvWrapper wrapping.Wrapper, key []byte, opt ...Option) (*SessionKey, *SessionKeyVersion, 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 = createSessionKeyTx(ctx, reader, w, rkvWrapper, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: %w", err)
|
||||
}
|
||||
return returnedDk.(*SessionKey), returnedDv.(*SessionKeyVersion), err
|
||||
}
|
||||
|
||||
// createSessionKeyTx inserts into the db (via db.Writer) and returns the new key
|
||||
// and the 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 createSessionKeyTx(ctx context.Context, r db.Reader, w db.Writer, rkvWrapper wrapping.Wrapper, key []byte) (*SessionKey, *SessionKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, nil, fmt.Errorf("create session key: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, nil, fmt.Errorf("create session key: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, nil, fmt.Errorf("create session 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 session 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 session key: unable to lookup root key version %s: %w", rootKeyVersionId, err)
|
||||
}
|
||||
|
||||
tk := AllocSessionKey()
|
||||
tv := AllocSessionKeyVersion()
|
||||
id, err := newSessionKeyId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: %w", err)
|
||||
}
|
||||
tk.PrivateId = id
|
||||
tk.RootKeyId = rv.RootKeyId
|
||||
|
||||
id, err = newSessionKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: %w", err)
|
||||
}
|
||||
tv.PrivateId = id
|
||||
tv.SessionKeyId = tk.PrivateId
|
||||
tv.RootKeyVersionId = rootKeyVersionId
|
||||
tv.Key = key
|
||||
if err := tv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: %w", err)
|
||||
}
|
||||
|
||||
// no session entries for keys
|
||||
if err := w.Create(ctx, &tk); err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: key create: %w", err)
|
||||
}
|
||||
// no session entries for key versions
|
||||
if err := w.Create(ctx, &tv); err != nil {
|
||||
return nil, nil, fmt.Errorf("create session key: version create: %w", err)
|
||||
}
|
||||
|
||||
return &tk, &tv, err
|
||||
}
|
||||
|
||||
// LookupSessionKey will look up a key in the repository. If the key is not
|
||||
// found, it will return nil, nil.
|
||||
func (r *Repository) LookupSessionKey(ctx context.Context, privateId string, opt ...Option) (*SessionKey, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup session key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocSessionKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup session key: failed %w for %s", err, privateId)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteSessionKey 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) DeleteSessionKey(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocSessionKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session 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 session 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 session key: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// ListSessionKeys will list the keys. Supports the WithLimit option.
|
||||
func (r *Repository) ListSessionKeys(ctx context.Context, opt ...Option) ([]*SessionKey, error) {
|
||||
var keys []*SessionKey
|
||||
err := r.list(ctx, &keys, "1=1", nil, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list session 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_CreateSessionKey(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)
|
||||
tk, tv, err := repo.CreateSessionKey(context.Background(), tt.args.keyWrapper, tt.args.key, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(tk)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotNil(tk.CreateTime)
|
||||
foundKey, err := repo.LookupSessionKey(context.Background(), tk.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKey, tk))
|
||||
|
||||
// make sure there was no session written
|
||||
err = db.TestVerifyOplog(t, rw, tk.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(tv.CreateTime)
|
||||
foundKeyVersion, err := repo.LookupSessionKeyVersion(context.Background(), tt.args.keyWrapper, tv.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKeyVersion, tv))
|
||||
|
||||
// make sure there was no session written
|
||||
err = db.TestVerifyOplog(t, rw, tv.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_DeleteSessionKey(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.SessionKey
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestSessionKey(t, conn, rk.PrivateId),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.SessionKey {
|
||||
k := kms.AllocSessionKey()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.SessionKey {
|
||||
id, err := db.NewPublicId(kms.RootKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocSessionKey()
|
||||
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.DeleteSessionKey(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 session 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.LookupSessionKey(context.Background(), tt.args.key.PrivateId)
|
||||
assert.Error(err)
|
||||
assert.Nil(foundKey)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
|
||||
// make sure there was no session 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_ListSessionKeys(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++ {
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, proj.PublicId)
|
||||
kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
require.NoError(err)
|
||||
}
|
||||
got, err := repo.ListSessionKeys(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"
|
||||
)
|
||||
|
||||
// CreateSessionKeyVersion inserts into the repository and returns the new key
|
||||
// version with its PrivateId. There are no valid options at this time.
|
||||
func (r *Repository) CreateSessionKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, sessionKeyId string, key []byte, opt ...Option) (*SessionKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("create session key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, fmt.Errorf("create session 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 session key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if sessionKeyId == "" {
|
||||
return nil, fmt.Errorf("create session key version: missing session key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("create session key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
kv := AllocSessionKeyVersion()
|
||||
id, err := newSessionKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create session key version: %w", err)
|
||||
}
|
||||
kv.PrivateId = id
|
||||
kv.RootKeyVersionId = rootKeyVersionId
|
||||
kv.Key = key
|
||||
kv.SessionKeyId = sessionKeyId
|
||||
if err := kv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("create session 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 session 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 session key version: %w for %s session key id", err, kv.SessionKeyId)
|
||||
}
|
||||
return returnedKey.(*SessionKeyVersion), err
|
||||
}
|
||||
|
||||
// LookupSessionKeyVersion will look up a key version in the repository. If
|
||||
// the key version is not found, it will return nil, nil.
|
||||
func (r *Repository) LookupSessionKeyVersion(ctx context.Context, keyWrapper wrapping.Wrapper, privateId string, opt ...Option) (*SessionKeyVersion, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup session key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if keyWrapper == nil {
|
||||
return nil, fmt.Errorf("lookup session key version: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocSessionKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup session key version: failed %w for %s", err, privateId)
|
||||
}
|
||||
if err := k.Decrypt(ctx, keyWrapper); err != nil {
|
||||
return nil, fmt.Errorf("lookup session key version: decrypt: %w", err)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteSessionKeyVersion 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) DeleteSessionKeyVersion(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocSessionKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session 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 session 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 session key version: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// LatestSessionKeyVersion searches for the key version with the highest
|
||||
// version number. When no results are found, it returns nil,
|
||||
// db.ErrRecordNotFound.
|
||||
func (r *Repository) LatestSessionKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, sessionKeyId string, opt ...Option) (*SessionKeyVersion, error) {
|
||||
if sessionKeyId == "" {
|
||||
return nil, fmt.Errorf("latest session key version: missing session key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("latest session key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var foundKeys []*SessionKeyVersion
|
||||
if err := r.reader.SearchWhere(ctx, &foundKeys, "session_key_id = ?", []interface{}{sessionKeyId}, db.WithLimit(1), db.WithOrder("version desc")); err != nil {
|
||||
return nil, fmt.Errorf("latest session key version: failed %w for %s", err, sessionKeyId)
|
||||
}
|
||||
if len(foundKeys) == 0 {
|
||||
return nil, db.ErrRecordNotFound
|
||||
}
|
||||
if err := foundKeys[0].Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("latest session key version: %w", err)
|
||||
}
|
||||
return foundKeys[0], nil
|
||||
}
|
||||
|
||||
// ListSessionKeyVersions will lists versions of a key. Supports the WithLimit option.
|
||||
func (r *Repository) ListSessionKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, sessionKeyId string, opt ...Option) ([]*SessionKeyVersion, error) {
|
||||
if sessionKeyId == "" {
|
||||
return nil, fmt.Errorf("list session key versions: missing session key id %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("list session key versions: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var versions []*SessionKeyVersion
|
||||
err := r.list(ctx, &versions, "session_key_id = ?", []interface{}{sessionKeyId}, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list session key versions: %w", err)
|
||||
}
|
||||
for i, k := range versions {
|
||||
if err := k.Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("list session 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_CreateSessionKeyVersion(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)
|
||||
sk := kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key []byte
|
||||
sessionKeyId 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,
|
||||
sessionKeyId: sk.PrivateId,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-rkv-wrapper",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: wrapper,
|
||||
sessionKeyId: sk.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
key: nil,
|
||||
keyWrapper: wrapper,
|
||||
sessionKeyId: sk.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.CreateSessionKeyVersion(context.Background(), tt.args.keyWrapper, tt.args.sessionKeyId, 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.LookupSessionKeyVersion(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_DeleteSessionKeyVersion(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)
|
||||
sk := kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key *kms.SessionKeyVersion
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestSessionKeyVersion(t, conn, rkvWrapper, sk.PrivateId, []byte("session key")),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.SessionKeyVersion {
|
||||
k := kms.AllocSessionKeyVersion()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.SessionKeyVersion {
|
||||
id, err := db.NewPublicId(kms.SessionKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocSessionKeyVersion()
|
||||
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.DeleteSessionKeyVersion(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.LookupSessionKeyVersion(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_LatestSessionKeyVersion(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)
|
||||
sk := kms.TestSessionKey(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.AllocSessionKeyVersion()).Error)
|
||||
testKeys := []*kms.SessionKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestSessionKeyVersion(t, conn, rkvWrapper, sk.PrivateId, []byte("test key"))
|
||||
testKeys = append(testKeys, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testKeys))
|
||||
got, err := repo.LatestSessionKeyVersion(context.Background(), tt.keyWrapper, sk.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_ListSessionKeyVersions(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)
|
||||
sk := kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
sessionKeyId 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{
|
||||
sessionKeyId: sk.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,
|
||||
sessionKeyId: sk.PrivateId,
|
||||
},
|
||||
wantCnt: repo.DefaultLimit(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
sessionKeyId: sk.PrivateId,
|
||||
opt: []kms.Option{kms.WithLimit(3)},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-org",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
sessionKeyId: "bad-id",
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: nil,
|
||||
sessionKeyId: sk.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.AllocSessionKeyVersion()).Error)
|
||||
keyVersions := []*kms.SessionKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestSessionKeyVersion(t, conn, rkvWrapper, sk.PrivateId, []byte("session key"))
|
||||
keyVersions = append(keyVersions, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(keyVersions))
|
||||
got, err := repo.ListSessionKeyVersions(context.Background(), tt.args.keyWrapper, tt.args.sessionKeyId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
package kms_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -72,3 +78,174 @@ func TestNewRepository(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateKeysTx(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)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dbReader db.Reader
|
||||
dbWriter db.Writer
|
||||
rootWrapper wrapping.Wrapper
|
||||
randomReader io.Reader
|
||||
scopeId string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantErrIs error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
dbReader: rw,
|
||||
dbWriter: rw,
|
||||
rootWrapper: wrapper,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: org.PublicId,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil-reader",
|
||||
args: args{
|
||||
dbReader: nil,
|
||||
dbWriter: rw,
|
||||
rootWrapper: wrapper,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: org.PublicId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrIs: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-writer",
|
||||
args: args{
|
||||
dbReader: rw,
|
||||
dbWriter: nil,
|
||||
rootWrapper: wrapper,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: org.PublicId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrIs: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
args: args{
|
||||
dbReader: rw,
|
||||
dbWriter: rw,
|
||||
rootWrapper: nil,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: org.PublicId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrIs: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-scope",
|
||||
args: args{
|
||||
dbReader: rw,
|
||||
dbWriter: rw,
|
||||
rootWrapper: wrapper,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: "",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrIs: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "bad-scope",
|
||||
args: args{
|
||||
dbReader: rw,
|
||||
dbWriter: rw,
|
||||
rootWrapper: wrapper,
|
||||
randomReader: rand.Reader,
|
||||
scopeId: "o_thisIsAnInvalidScopeId",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
keys, err := kms.CreateKeysTx(tt.args.ctx, tt.args.dbReader, tt.args.dbWriter, tt.args.rootWrapper, tt.args.randomReader, tt.args.scopeId)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantErrIs != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantErrIs), "unexpected error: %s", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
rk := kms.AllocRootKey()
|
||||
rk.PrivateId = keys[kms.KeyTypeRootKey].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &rk)
|
||||
require.NoError(err)
|
||||
assert.Equal(rk.ScopeId, tt.args.scopeId)
|
||||
|
||||
rkv := kms.AllocRootKeyVersion()
|
||||
rkv.PrivateId = keys[kms.KeyTypeRootKeyVersion].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &rkv)
|
||||
require.NoError(err)
|
||||
assert.Equal(rkv.RootKeyId, rk.PrivateId)
|
||||
|
||||
dk := kms.AllocDatabaseKey()
|
||||
dk.PrivateId = keys[kms.KeyTypeDatabaseKey].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &dk)
|
||||
require.NoError(err)
|
||||
assert.Equal(rk.PrivateId, dk.RootKeyId)
|
||||
|
||||
dkv := kms.AllocDatabaseKeyVersion()
|
||||
dkv.PrivateId = keys[kms.KeyTypeDatabaseKeyVersion].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &dkv)
|
||||
require.NoError(err)
|
||||
assert.Equal(dk.PrivateId, dkv.DatabaseKeyId)
|
||||
assert.Equal(rkv.PrivateId, dkv.RootKeyVersionId)
|
||||
|
||||
opk := kms.AllocOplogKey()
|
||||
opk.PrivateId = keys[kms.KeyTypeOplogKey].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &opk)
|
||||
require.NoError(err)
|
||||
assert.Equal(rk.PrivateId, opk.RootKeyId)
|
||||
|
||||
opkv := kms.AllocOplogKeyVersion()
|
||||
opkv.PrivateId = keys[kms.KeyTypeOplogKeyVersion].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &opkv)
|
||||
require.NoError(err)
|
||||
assert.Equal(opk.PrivateId, opkv.OplogKeyId)
|
||||
assert.Equal(rkv.PrivateId, opkv.RootKeyVersionId)
|
||||
|
||||
sk := kms.AllocSessionKey()
|
||||
sk.PrivateId = keys[kms.KeyTypeSessionKey].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &sk)
|
||||
require.NoError(err)
|
||||
assert.Equal(rk.PrivateId, sk.RootKeyId)
|
||||
|
||||
skv := kms.AllocSessionKeyVersion()
|
||||
skv.PrivateId = keys[kms.KeyTypeSessionKeyVersion].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &skv)
|
||||
require.NoError(err)
|
||||
assert.Equal(sk.PrivateId, skv.SessionKeyId)
|
||||
assert.Equal(rkv.PrivateId, skv.RootKeyVersionId)
|
||||
|
||||
tk := kms.AllocTokenKey()
|
||||
tk.PrivateId = keys[kms.KeyTypeTokenKey].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &tk)
|
||||
require.NoError(err)
|
||||
assert.Equal(rk.PrivateId, tk.RootKeyId)
|
||||
|
||||
tkv := kms.AllocTokenKeyVersion()
|
||||
tkv.PrivateId = keys[kms.KeyTypeTokenKeyVersion].GetPrivateId()
|
||||
err = rw.LookupById(context.Background(), &tkv)
|
||||
require.NoError(err)
|
||||
assert.Equal(tk.PrivateId, tkv.TokenKeyId)
|
||||
assert.Equal(rkv.PrivateId, tkv.RootKeyVersionId)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
)
|
||||
|
||||
// CreateTokenKey inserts into the repository and returns the new key and the
|
||||
// key version. There are no valid options at this time.
|
||||
func (r *Repository) CreateTokenKey(ctx context.Context, rkvWrapper wrapping.Wrapper, key []byte, opt ...Option) (*TokenKey, *TokenKeyVersion, 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 = createTokenKeyTx(ctx, reader, w, rkvWrapper, key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: %w", err)
|
||||
}
|
||||
return returnedDk.(*TokenKey), returnedDv.(*TokenKeyVersion), err
|
||||
}
|
||||
|
||||
// createTokenKeyTx inserts into the db (via db.Writer) and returns the new key
|
||||
// and the 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 createTokenKeyTx(ctx context.Context, r db.Reader, w db.Writer, rkvWrapper wrapping.Wrapper, key []byte) (*TokenKey, *TokenKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, nil, fmt.Errorf("create token key: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, nil, fmt.Errorf("create token key: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, nil, fmt.Errorf("create token 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 token 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 token key: unable to lookup root key version %s: %w", rootKeyVersionId, err)
|
||||
}
|
||||
|
||||
tk := AllocTokenKey()
|
||||
tv := AllocTokenKeyVersion()
|
||||
id, err := newTokenKeyId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: %w", err)
|
||||
}
|
||||
tk.PrivateId = id
|
||||
tk.RootKeyId = rv.RootKeyId
|
||||
|
||||
id, err = newTokenKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: %w", err)
|
||||
}
|
||||
tv.PrivateId = id
|
||||
tv.TokenKeyId = tk.PrivateId
|
||||
tv.RootKeyVersionId = rootKeyVersionId
|
||||
tv.Key = key
|
||||
if err := tv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: %w", err)
|
||||
}
|
||||
|
||||
// no token entries for keys
|
||||
if err := w.Create(ctx, &tk); err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: key create: %w", err)
|
||||
}
|
||||
// no token entries for key versions
|
||||
if err := w.Create(ctx, &tv); err != nil {
|
||||
return nil, nil, fmt.Errorf("create token key: version create: %w", err)
|
||||
}
|
||||
|
||||
return &tk, &tv, err
|
||||
}
|
||||
|
||||
// LookupTokenKey will look up a key in the repository. If the key is not
|
||||
// found, it will return nil, nil.
|
||||
func (r *Repository) LookupTokenKey(ctx context.Context, privateId string, opt ...Option) (*TokenKey, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup token key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocTokenKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup token key: failed %w for %s", err, privateId)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteTokenKey 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) DeleteTokenKey(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete token key: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocTokenKey()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete token 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 token 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 token key: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// ListTokenKeys will list the keys. Supports the WithLimit option.
|
||||
func (r *Repository) ListTokenKeys(ctx context.Context, opt ...Option) ([]*TokenKey, error) {
|
||||
var keys []*TokenKey
|
||||
err := r.list(ctx, &keys, "1=1", nil, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list token 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_CreateTokenKey(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)
|
||||
tk, tv, err := repo.CreateTokenKey(context.Background(), tt.args.keyWrapper, tt.args.key, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(tk)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.NotNil(tk.CreateTime)
|
||||
foundKey, err := repo.LookupTokenKey(context.Background(), tk.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKey, tk))
|
||||
|
||||
// make sure there was no token written
|
||||
err = db.TestVerifyOplog(t, rw, tk.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(tv.CreateTime)
|
||||
foundKeyVersion, err := repo.LookupTokenKeyVersion(context.Background(), tt.args.keyWrapper, tv.PrivateId)
|
||||
assert.NoError(err)
|
||||
assert.True(proto.Equal(foundKeyVersion, tv))
|
||||
|
||||
// make sure there was no token written
|
||||
err = db.TestVerifyOplog(t, rw, tv.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_DeleteTokenKey(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.TokenKey
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestTokenKey(t, conn, rk.PrivateId),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.TokenKey {
|
||||
k := kms.AllocTokenKey()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.TokenKey {
|
||||
id, err := db.NewPublicId(kms.RootKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocTokenKey()
|
||||
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.DeleteTokenKey(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 token 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.LookupTokenKey(context.Background(), tt.args.key.PrivateId)
|
||||
assert.Error(err)
|
||||
assert.Nil(foundKey)
|
||||
assert.True(errors.Is(err, db.ErrRecordNotFound))
|
||||
|
||||
// make sure there was no token 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_ListTokenKeys(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++ {
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
|
||||
rk := kms.TestRootKey(t, conn, proj.PublicId)
|
||||
kms.TestTokenKey(t, conn, rk.PrivateId)
|
||||
require.NoError(err)
|
||||
}
|
||||
got, err := repo.ListTokenKeys(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"
|
||||
)
|
||||
|
||||
// CreateTokenKeyVersion inserts into the repository and returns the new key
|
||||
// version with its PrivateId. There are no valid options at this time.
|
||||
func (r *Repository) CreateTokenKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, tokenKeyId string, key []byte, opt ...Option) (*TokenKeyVersion, error) {
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("create token key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
rootKeyVersionId := rkvWrapper.KeyID()
|
||||
switch {
|
||||
case !strings.HasPrefix(rootKeyVersionId, RootKeyVersionPrefix):
|
||||
return nil, fmt.Errorf("create token 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 token key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if tokenKeyId == "" {
|
||||
return nil, fmt.Errorf("create token key version: missing token key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("create token key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
kv := AllocTokenKeyVersion()
|
||||
id, err := newTokenKeyVersionId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create token key version: %w", err)
|
||||
}
|
||||
kv.PrivateId = id
|
||||
kv.RootKeyVersionId = rootKeyVersionId
|
||||
kv.Key = key
|
||||
kv.TokenKeyId = tokenKeyId
|
||||
if err := kv.Encrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("create token 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 token 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 token key version: %w for %s token key id", err, kv.TokenKeyId)
|
||||
}
|
||||
return returnedKey.(*TokenKeyVersion), err
|
||||
}
|
||||
|
||||
// LookupTokenKeyVersion will look up a key version in the repository. If
|
||||
// the key version is not found, it will return nil, nil.
|
||||
func (r *Repository) LookupTokenKeyVersion(ctx context.Context, keyWrapper wrapping.Wrapper, privateId string, opt ...Option) (*TokenKeyVersion, error) {
|
||||
if privateId == "" {
|
||||
return nil, fmt.Errorf("lookup token key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if keyWrapper == nil {
|
||||
return nil, fmt.Errorf("lookup token key version: missing key wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocTokenKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return nil, fmt.Errorf("lookup token key version: failed %w for %s", err, privateId)
|
||||
}
|
||||
if err := k.Decrypt(ctx, keyWrapper); err != nil {
|
||||
return nil, fmt.Errorf("lookup token key version: decrypt: %w", err)
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// DeleteTokenKeyVersion 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) DeleteTokenKeyVersion(ctx context.Context, privateId string, opt ...Option) (int, error) {
|
||||
if privateId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete token key version: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
k := AllocTokenKeyVersion()
|
||||
k.PrivateId = privateId
|
||||
if err := r.reader.LookupById(ctx, &k); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete token 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 token 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 token key version: %s: %w", privateId, err)
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
// LatestTokenKeyVersion searches for the key version with the highest
|
||||
// version number. When no results are found, it returns nil,
|
||||
// db.ErrRecordNotFound.
|
||||
func (r *Repository) LatestTokenKeyVersion(ctx context.Context, rkvWrapper wrapping.Wrapper, tokenKeyId string, opt ...Option) (*TokenKeyVersion, error) {
|
||||
if tokenKeyId == "" {
|
||||
return nil, fmt.Errorf("latest token key version: missing token key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("latest token key version: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var foundKeys []*TokenKeyVersion
|
||||
if err := r.reader.SearchWhere(ctx, &foundKeys, "token_key_id = ?", []interface{}{tokenKeyId}, db.WithLimit(1), db.WithOrder("version desc")); err != nil {
|
||||
return nil, fmt.Errorf("latest token key version: failed %w for %s", err, tokenKeyId)
|
||||
}
|
||||
if len(foundKeys) == 0 {
|
||||
return nil, db.ErrRecordNotFound
|
||||
}
|
||||
if err := foundKeys[0].Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("latest token key version: %w", err)
|
||||
}
|
||||
return foundKeys[0], nil
|
||||
}
|
||||
|
||||
// ListTokenKeyVersions will lists versions of a key. Supports the WithLimit option.
|
||||
func (r *Repository) ListTokenKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, tokenKeyId string, opt ...Option) ([]*TokenKeyVersion, error) {
|
||||
if tokenKeyId == "" {
|
||||
return nil, fmt.Errorf("list token key versions: missing token key id %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rkvWrapper == nil {
|
||||
return nil, fmt.Errorf("list token key versions: missing root key version wrapper: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
var versions []*TokenKeyVersion
|
||||
err := r.list(ctx, &versions, "token_key_id = ?", []interface{}{tokenKeyId}, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list token key versions: %w", err)
|
||||
}
|
||||
for i, k := range versions {
|
||||
if err := k.Decrypt(ctx, rkvWrapper); err != nil {
|
||||
return nil, fmt.Errorf("list token 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_CreateTokenKeyVersion(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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key []byte
|
||||
tokenKeyId 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,
|
||||
tokenKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-rkv-wrapper",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
keyWrapper: wrapper,
|
||||
tokenKeyId: dk.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
key: nil,
|
||||
keyWrapper: wrapper,
|
||||
tokenKeyId: 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.CreateTokenKeyVersion(context.Background(), tt.args.keyWrapper, tt.args.tokenKeyId, 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.LookupTokenKeyVersion(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_DeleteTokenKeyVersion(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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
key *kms.TokenKeyVersion
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
key: kms.TestTokenKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("token key")),
|
||||
},
|
||||
wantRowsDeleted: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-private-id",
|
||||
args: args{
|
||||
key: func() *kms.TokenKeyVersion {
|
||||
k := kms.AllocTokenKeyVersion()
|
||||
return &k
|
||||
}(),
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantIsError: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
args: args{
|
||||
key: func() *kms.TokenKeyVersion {
|
||||
id, err := db.NewPublicId(kms.TokenKeyPrefix)
|
||||
require.NoError(t, err)
|
||||
k := kms.AllocTokenKeyVersion()
|
||||
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.DeleteTokenKeyVersion(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.LookupTokenKeyVersion(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_LatestTokenKeyVersion(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.TestTokenKey(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.AllocTokenKeyVersion()).Error)
|
||||
testKeys := []*kms.TokenKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestTokenKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("test key"))
|
||||
testKeys = append(testKeys, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testKeys))
|
||||
got, err := repo.LatestTokenKeyVersion(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_ListTokenKeyVersions(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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
tokenKeyId 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{
|
||||
tokenKeyId: 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,
|
||||
tokenKeyId: dk.PrivateId,
|
||||
},
|
||||
wantCnt: repo.DefaultLimit(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.DefaultLimit() + 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
tokenKeyId: dk.PrivateId,
|
||||
opt: []kms.Option{kms.WithLimit(3)},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-org",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: rkvWrapper,
|
||||
tokenKeyId: "bad-id",
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
createCnt: 1,
|
||||
args: args{
|
||||
keyWrapper: nil,
|
||||
tokenKeyId: 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.AllocTokenKeyVersion()).Error)
|
||||
keyVersions := []*kms.TokenKeyVersion{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
k := kms.TestTokenKeyVersion(t, conn, rkvWrapper, dk.PrivateId, []byte("token key"))
|
||||
keyVersions = append(keyVersions, k)
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(keyVersions))
|
||||
got, err := repo.ListTokenKeyVersions(context.Background(), tt.args.keyWrapper, tt.args.tokenKeyId, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func (k *RootKey) Clone() interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the kms root
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// before it's written.
|
||||
func (k *RootKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultSessionKeyTableName = "kms_session_key"
|
||||
)
|
||||
|
||||
type SessionKey struct {
|
||||
*store.SessionKey
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewSessionKey creates a new in memory key. No options
|
||||
// are currently supported.
|
||||
func NewSessionKey(rootKeyId string, opt ...Option) (*SessionKey, error) {
|
||||
if rootKeyId == "" {
|
||||
return nil, fmt.Errorf("new root key: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
c := &SessionKey{
|
||||
SessionKey: &store.SessionKey{
|
||||
RootKeyId: rootKeyId,
|
||||
},
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// AllocSessionKey will allocate a key
|
||||
func AllocSessionKey() SessionKey {
|
||||
return SessionKey{
|
||||
SessionKey: &store.SessionKey{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key
|
||||
func (k *SessionKey) Clone() interface{} {
|
||||
cp := proto.Clone(k.SessionKey)
|
||||
return &SessionKey{
|
||||
SessionKey: cp.(*store.SessionKey),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// before it's written.
|
||||
func (k *SessionKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("session key vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.RootKeyId == "" {
|
||||
return fmt.Errorf("session key vet for write: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *SessionKey) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultSessionKeyTableName
|
||||
}
|
||||
|
||||
// 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 *SessionKey) 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 SessionKey attributes are
|
||||
// immutable and those tests are covered by TestSessionKey_ImmutableFields
|
||||
|
||||
func TestSessionKey_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.SessionKey
|
||||
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.SessionKey {
|
||||
k := kms.AllocSessionKey()
|
||||
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.AllocSessionKey()).Error)
|
||||
got, err := kms.NewSessionKey(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.SessionKeyPrefix)
|
||||
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 TestSessionKey_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.AllocSessionKey()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.SessionKey
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestSessionKey(t, conn, rk.PrivateId),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.SessionKey {
|
||||
k := kms.AllocSessionKey()
|
||||
id, err := db.NewPublicId(kms.SessionKeyPrefix)
|
||||
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.AllocSessionKey()
|
||||
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.AllocSessionKey()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionKey_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.TestSessionKey(t, conn, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.SessionKey).SessionKey, k.SessionKey))
|
||||
})
|
||||
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.TestSessionKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestSessionKey(t, conn, rk2.PrivateId)
|
||||
|
||||
cp := k.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.SessionKey).SessionKey, k2.SessionKey))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSessionKey_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultSessionKeyTableName
|
||||
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.AllocSessionKey()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocSessionKey()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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 (
|
||||
DefaultSessionKeyVersionTableName = "kms_session_key_version"
|
||||
)
|
||||
|
||||
type SessionKeyVersion struct {
|
||||
*store.SessionKeyVersion
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// SessionKeyVersion creates a new in memory key version. No options are
|
||||
// currently supported.
|
||||
func NewSessionKeyVersion(sessionKeyId string, key []byte, rootKeyVersionId string, opt ...Option) (*SessionKeyVersion, error) {
|
||||
if sessionKeyId == "" {
|
||||
return nil, fmt.Errorf("new session key version: missing session key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("new session key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rootKeyVersionId == "" {
|
||||
return nil, fmt.Errorf("new session key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
|
||||
k := &SessionKeyVersion{
|
||||
SessionKeyVersion: &store.SessionKeyVersion{
|
||||
SessionKeyId: sessionKeyId,
|
||||
RootKeyVersionId: rootKeyVersionId,
|
||||
Key: key,
|
||||
},
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// AllocSessionKeyVersion allocates a key version
|
||||
func AllocSessionKeyVersion() SessionKeyVersion {
|
||||
return SessionKeyVersion{
|
||||
SessionKeyVersion: &store.SessionKeyVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key version
|
||||
func (k *SessionKeyVersion) Clone() interface{} {
|
||||
cp := proto.Clone(k.SessionKeyVersion)
|
||||
return &SessionKeyVersion{
|
||||
SessionKeyVersion: cp.(*store.SessionKeyVersion),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// version before it's written.
|
||||
func (k *SessionKeyVersion) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("session key version vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.CtKey == nil {
|
||||
return fmt.Errorf("session key version vet for write: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.SessionKeyId == "" {
|
||||
return fmt.Errorf("session key version vet for write: missing session key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.RootKeyVersionId == "" {
|
||||
return fmt.Errorf("session key version vet for write: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *SessionKeyVersion) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultSessionKeyVersionTableName
|
||||
}
|
||||
|
||||
// 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 *SessionKeyVersion) SetTableName(n string) {
|
||||
k.tableName = n
|
||||
}
|
||||
|
||||
// Encrypt will encrypt the key version's key
|
||||
func (k *SessionKeyVersion) Encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.SessionKeyVersion directly
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, k.SessionKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting kms session key version: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt will decrypt the key version's key
|
||||
func (k *SessionKeyVersion) Decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.SessionKeyVersion directly
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, k.SessionKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting kms session 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 essionKeyVersion attributes are
|
||||
// immutable and those tests are covered by TestSessionKey_ImmutableFields
|
||||
|
||||
func TestSessionKeyVersion_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.TestSessionKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
sessionKeyId string
|
||||
key []byte
|
||||
rootKeyVersionId string
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *kms.SessionKeyVersion
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty-id",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
sessionKeyId: dk.PrivateId,
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-root-key-version-id",
|
||||
args: args{
|
||||
sessionKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
sessionKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
want: func() *kms.SessionKeyVersion {
|
||||
k := kms.AllocSessionKeyVersion()
|
||||
k.RootKeyVersionId = rkv.PrivateId
|
||||
k.Key = []byte("test key")
|
||||
k.SessionKeyId = 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.AllocSessionKeyVersion()).Error)
|
||||
got, err := kms.NewSessionKeyVersion(tt.args.sessionKeyId, 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.SessionKeyPrefix)
|
||||
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 TestSessionKeyVersion_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)
|
||||
tk := kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocSessionKeyVersion()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.SessionKeyVersion
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestSessionKeyVersion(t, conn, rkvWrapper, tk.PrivateId, []byte("test key")),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.SessionKeyVersion {
|
||||
k := kms.AllocSessionKeyVersion()
|
||||
id, err := db.NewPublicId(kms.SessionKeyVersionPrefix)
|
||||
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.AllocSessionKeyVersion()
|
||||
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.AllocSessionKeyVersion()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionKeyVersion_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.TestSessionKey(t, conn, rk.PrivateId)
|
||||
kv := kms.TestSessionKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
cp := kv.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.SessionKeyVersion).SessionKeyVersion, kv.SessionKeyVersion))
|
||||
})
|
||||
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.TestSessionKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestSessionKey(t, conn, rk2.PrivateId)
|
||||
kv := kms.TestSessionKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
kv2 := kms.TestSessionKeyVersion(t, conn, rkvWrapper2, k2.PrivateId, []byte("test key 2"))
|
||||
|
||||
cp := kv.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.SessionKeyVersion).SessionKeyVersion, kv2.SessionKeyVersion))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSessionKeyVersion_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultSessionKeyVersionTableName
|
||||
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.AllocSessionKeyVersion()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocSessionKeyVersion()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
// 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/oplog_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 OplogKey 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 *OplogKey) Reset() {
|
||||
*x = OplogKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_oplog_key_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *OplogKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OplogKey) ProtoMessage() {}
|
||||
|
||||
func (x *OplogKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_oplog_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 OplogKey.ProtoReflect.Descriptor instead.
|
||||
func (*OplogKey) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_oplog_key_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *OplogKey) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OplogKey) GetRootKeyId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OplogKey) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type OplogKeyVersion 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"`
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
OplogKeyId string `protobuf:"bytes,20,opt,name=oplog_key_id,json=oplogKeyId,proto3" json:"oplog_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 *OplogKeyVersion) Reset() {
|
||||
*x = OplogKeyVersion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_oplog_key_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OplogKeyVersion) ProtoMessage() {}
|
||||
|
||||
func (x *OplogKeyVersion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_oplog_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 OplogKeyVersion.ProtoReflect.Descriptor instead.
|
||||
func (*OplogKeyVersion) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_oplog_key_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetOplogKeyId() string {
|
||||
if x != nil {
|
||||
return x.OplogKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetRootKeyVersionId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyVersionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetKey() []byte {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetCtKey() []byte {
|
||||
if x != nil {
|
||||
return x.CtKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *OplogKeyVersion) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_kms_store_v1_oplog_key_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_kms_store_v1_oplog_key_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 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, 0x6f, 0x70, 0x6c, 0x6f, 0x67, 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,
|
||||
0x96, 0x01, 0x0a, 0x08, 0x4f, 0x70, 0x6c, 0x6f, 0x67, 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, 0x91, 0x02, 0x0a, 0x0f, 0x4f, 0x70, 0x6c,
|
||||
0x6f, 0x67, 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, 0x20, 0x0a, 0x0c, 0x6f,
|
||||
0x70, 0x6c, 0x6f, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x6f, 0x70, 0x6c, 0x6f, 0x67, 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_oplog_key_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_rawDescData = file_controller_storage_kms_store_v1_oplog_key_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_kms_store_v1_oplog_key_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_kms_store_v1_oplog_key_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_kms_store_v1_oplog_key_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_kms_store_v1_oplog_key_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_storage_kms_store_v1_oplog_key_proto_goTypes = []interface{}{
|
||||
(*OplogKey)(nil), // 0: controller.storage.kms.store.v1.OplogKey
|
||||
(*OplogKeyVersion)(nil), // 1: controller.storage.kms.store.v1.OplogKeyVersion
|
||||
(*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_kms_store_v1_oplog_key_proto_depIdxs = []int32{
|
||||
2, // 0: controller.storage.kms.store.v1.OplogKey.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // 1: controller.storage.kms.store.v1.OplogKeyVersion.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_oplog_key_proto_init() }
|
||||
func file_controller_storage_kms_store_v1_oplog_key_proto_init() {
|
||||
if File_controller_storage_kms_store_v1_oplog_key_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*OplogKey); 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_oplog_key_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*OplogKeyVersion); 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_oplog_key_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_kms_store_v1_oplog_key_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_kms_store_v1_oplog_key_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_kms_store_v1_oplog_key_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_kms_store_v1_oplog_key_proto = out.File
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_rawDesc = nil
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_goTypes = nil
|
||||
file_controller_storage_kms_store_v1_oplog_key_proto_depIdxs = nil
|
||||
}
|
||||
@@ -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/session_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 SessionKey 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 *SessionKey) Reset() {
|
||||
*x = SessionKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_session_key_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SessionKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SessionKey) ProtoMessage() {}
|
||||
|
||||
func (x *SessionKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_session_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 SessionKey.ProtoReflect.Descriptor instead.
|
||||
func (*SessionKey) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_session_key_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SessionKey) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SessionKey) GetRootKeyId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SessionKey) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SessionKeyVersion 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"`
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
SessionKeyId string `protobuf:"bytes,20,opt,name=session_key_id,json=sessionKeyId,proto3" json:"session_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 *SessionKeyVersion) Reset() {
|
||||
*x = SessionKeyVersion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_session_key_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SessionKeyVersion) ProtoMessage() {}
|
||||
|
||||
func (x *SessionKeyVersion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_session_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 SessionKeyVersion.ProtoReflect.Descriptor instead.
|
||||
func (*SessionKeyVersion) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_session_key_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetSessionKeyId() string {
|
||||
if x != nil {
|
||||
return x.SessionKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetRootKeyVersionId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyVersionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetKey() []byte {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetCtKey() []byte {
|
||||
if x != nil {
|
||||
return x.CtKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SessionKeyVersion) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_kms_store_v1_session_key_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_kms_store_v1_session_key_proto_rawDesc = []byte{
|
||||
0x0a, 0x31, 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, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0x98, 0x01, 0x0a, 0x0a, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0x97, 0x02, 0x0a,
|
||||
0x11, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69,
|
||||
0x6f, 0x6e, 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_session_key_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_kms_store_v1_session_key_proto_rawDescData = file_controller_storage_kms_store_v1_session_key_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_kms_store_v1_session_key_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_kms_store_v1_session_key_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_kms_store_v1_session_key_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_kms_store_v1_session_key_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_kms_store_v1_session_key_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_kms_store_v1_session_key_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_storage_kms_store_v1_session_key_proto_goTypes = []interface{}{
|
||||
(*SessionKey)(nil), // 0: controller.storage.kms.store.v1.SessionKey
|
||||
(*SessionKeyVersion)(nil), // 1: controller.storage.kms.store.v1.SessionKeyVersion
|
||||
(*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_kms_store_v1_session_key_proto_depIdxs = []int32{
|
||||
2, // 0: controller.storage.kms.store.v1.SessionKey.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // 1: controller.storage.kms.store.v1.SessionKeyVersion.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_session_key_proto_init() }
|
||||
func file_controller_storage_kms_store_v1_session_key_proto_init() {
|
||||
if File_controller_storage_kms_store_v1_session_key_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_kms_store_v1_session_key_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SessionKey); 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_session_key_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SessionKeyVersion); 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_session_key_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_kms_store_v1_session_key_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_kms_store_v1_session_key_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_kms_store_v1_session_key_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_kms_store_v1_session_key_proto = out.File
|
||||
file_controller_storage_kms_store_v1_session_key_proto_rawDesc = nil
|
||||
file_controller_storage_kms_store_v1_session_key_proto_goTypes = nil
|
||||
file_controller_storage_kms_store_v1_session_key_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
// 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/token_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 TokenKey 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 *TokenKey) Reset() {
|
||||
*x = TokenKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_token_key_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TokenKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TokenKey) ProtoMessage() {}
|
||||
|
||||
func (x *TokenKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_token_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 TokenKey.ProtoReflect.Descriptor instead.
|
||||
func (*TokenKey) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_token_key_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *TokenKey) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenKey) GetRootKeyId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenKey) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenKeyVersion 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"`
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
TokenKeyId string `protobuf:"bytes,20,opt,name=token_key_id,json=tokenKeyId,proto3" json:"token_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 *TokenKeyVersion) Reset() {
|
||||
*x = TokenKeyVersion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_kms_store_v1_token_key_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TokenKeyVersion) ProtoMessage() {}
|
||||
|
||||
func (x *TokenKeyVersion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_kms_store_v1_token_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 TokenKeyVersion.ProtoReflect.Descriptor instead.
|
||||
func (*TokenKeyVersion) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_kms_store_v1_token_key_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetPrivateId() string {
|
||||
if x != nil {
|
||||
return x.PrivateId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetTokenKeyId() string {
|
||||
if x != nil {
|
||||
return x.TokenKeyId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetRootKeyVersionId() string {
|
||||
if x != nil {
|
||||
return x.RootKeyVersionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetKey() []byte {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetCtKey() []byte {
|
||||
if x != nil {
|
||||
return x.CtKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TokenKeyVersion) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_kms_store_v1_token_key_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_kms_store_v1_token_key_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 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, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 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,
|
||||
0x96, 0x01, 0x0a, 0x08, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 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, 0x91, 0x02, 0x0a, 0x0f, 0x54, 0x6f, 0x6b,
|
||||
0x65, 0x6e, 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, 0x20, 0x0a, 0x0c, 0x74,
|
||||
0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 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_token_key_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_kms_store_v1_token_key_proto_rawDescData = file_controller_storage_kms_store_v1_token_key_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_kms_store_v1_token_key_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_kms_store_v1_token_key_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_kms_store_v1_token_key_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_kms_store_v1_token_key_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_kms_store_v1_token_key_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_kms_store_v1_token_key_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_controller_storage_kms_store_v1_token_key_proto_goTypes = []interface{}{
|
||||
(*TokenKey)(nil), // 0: controller.storage.kms.store.v1.TokenKey
|
||||
(*TokenKeyVersion)(nil), // 1: controller.storage.kms.store.v1.TokenKeyVersion
|
||||
(*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_kms_store_v1_token_key_proto_depIdxs = []int32{
|
||||
2, // 0: controller.storage.kms.store.v1.TokenKey.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // 1: controller.storage.kms.store.v1.TokenKeyVersion.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_token_key_proto_init() }
|
||||
func file_controller_storage_kms_store_v1_token_key_proto_init() {
|
||||
if File_controller_storage_kms_store_v1_token_key_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_kms_store_v1_token_key_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TokenKey); 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_token_key_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TokenKeyVersion); 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_token_key_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_kms_store_v1_token_key_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_kms_store_v1_token_key_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_kms_store_v1_token_key_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_kms_store_v1_token_key_proto = out.File
|
||||
file_controller_storage_kms_store_v1_token_key_proto_rawDesc = nil
|
||||
file_controller_storage_kms_store_v1_token_key_proto_goTypes = nil
|
||||
file_controller_storage_kms_store_v1_token_key_proto_depIdxs = nil
|
||||
}
|
||||
@@ -94,3 +94,105 @@ func TestDatabaseKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper w
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOplogKey(t *testing.T, conn *gorm.DB, rootKeyId string) *OplogKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
require.NoError(conn.Where("root_key_id = ?", rootKeyId).Delete(AllocOplogKey()).Error)
|
||||
k, err := NewOplogKey(rootKeyId)
|
||||
require.NoError(err)
|
||||
id, err := newOplogKeyId()
|
||||
require.NoError(err)
|
||||
k.PrivateId = id
|
||||
k.RootKeyId = rootKeyId
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestOplogKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, oplogKeyId string, key []byte) *OplogKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
rootKeyVersionId := rootKeyVersionWrapper.KeyID()
|
||||
require.NotEmpty(rootKeyVersionId)
|
||||
k, err := NewOplogKeyVersion(oplogKeyId, key, rootKeyVersionId)
|
||||
require.NoError(err)
|
||||
id, err := newOplogKeyVersionId()
|
||||
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
|
||||
}
|
||||
|
||||
func TestTokenKey(t *testing.T, conn *gorm.DB, rootKeyId string) *TokenKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
require.NoError(conn.Where("root_key_id = ?", rootKeyId).Delete(AllocTokenKey()).Error)
|
||||
k, err := NewTokenKey(rootKeyId)
|
||||
require.NoError(err)
|
||||
id, err := newTokenKeyId()
|
||||
require.NoError(err)
|
||||
k.PrivateId = id
|
||||
k.RootKeyId = rootKeyId
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestTokenKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, tokenKeyId string, key []byte) *TokenKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
rootKeyVersionId := rootKeyVersionWrapper.KeyID()
|
||||
require.NotEmpty(rootKeyVersionId)
|
||||
k, err := NewTokenKeyVersion(tokenKeyId, key, rootKeyVersionId)
|
||||
require.NoError(err)
|
||||
id, err := newTokenKeyVersionId()
|
||||
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
|
||||
}
|
||||
|
||||
func TestSessionKey(t *testing.T, conn *gorm.DB, rootKeyId string) *SessionKey {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
require.NoError(conn.Where("root_key_id = ?", rootKeyId).Delete(AllocSessionKey()).Error)
|
||||
k, err := NewSessionKey(rootKeyId)
|
||||
require.NoError(err)
|
||||
id, err := newSessionKeyId()
|
||||
require.NoError(err)
|
||||
k.PrivateId = id
|
||||
k.RootKeyId = rootKeyId
|
||||
err = rw.Create(context.Background(), k)
|
||||
require.NoError(err)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestSessionKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, sessionKeyId string, key []byte) *SessionKeyVersion {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
rw := db.New(conn)
|
||||
rootKeyVersionId := rootKeyVersionWrapper.KeyID()
|
||||
require.NotEmpty(rootKeyVersionId)
|
||||
k, err := NewSessionKeyVersion(sessionKeyId, key, rootKeyVersionId)
|
||||
require.NoError(err)
|
||||
id, err := newSessionKeyVersionId()
|
||||
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
|
||||
}
|
||||
@@ -65,3 +65,96 @@ func Test_TestDatabaseKeyVersion(t *testing.T) {
|
||||
require.NotNil(dv)
|
||||
require.NotEmpty(dv.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestOplogKey(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)
|
||||
|
||||
opk := kms.TestOplogKey(t, conn, k.PrivateId)
|
||||
require.NotNil(opk)
|
||||
assert.NotEmpty(opk.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestOplogKeyVersion(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)
|
||||
opk := kms.TestOplogKey(t, conn, rk.PrivateId)
|
||||
opv := kms.TestOplogKeyVersion(t, conn, rootKeyVersionWrapper, opk.PrivateId, []byte("test dek key"))
|
||||
require.NotNil(opv)
|
||||
require.NotEmpty(opv.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestTokenKey(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)
|
||||
|
||||
tk := kms.TestTokenKey(t, conn, k.PrivateId)
|
||||
require.NotNil(tk)
|
||||
assert.NotEmpty(tk.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestTokenKeyVersion(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)
|
||||
tk := kms.TestTokenKey(t, conn, rk.PrivateId)
|
||||
tv := kms.TestTokenKeyVersion(t, conn, rootKeyVersionWrapper, tk.PrivateId, []byte("test dek key"))
|
||||
require.NotNil(tv)
|
||||
require.NotEmpty(tv.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestSessionKey(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)
|
||||
|
||||
sk := kms.TestSessionKey(t, conn, k.PrivateId)
|
||||
require.NotNil(sk)
|
||||
assert.NotEmpty(sk.PrivateId)
|
||||
}
|
||||
|
||||
func Test_TestSessionKeyVersion(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)
|
||||
sk := kms.TestSessionKey(t, conn, rk.PrivateId)
|
||||
sv := kms.TestSessionKeyVersion(t, conn, rootKeyVersionWrapper, sk.PrivateId, []byte("test dek key"))
|
||||
require.NotNil(sv)
|
||||
require.NotEmpty(sv.PrivateId)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTokenKeyTableName = "kms_token_key"
|
||||
)
|
||||
|
||||
type TokenKey struct {
|
||||
*store.TokenKey
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewTokenKey creates a new in memory key. No options
|
||||
// are currently supported.
|
||||
func NewTokenKey(rootKeyId string, opt ...Option) (*TokenKey, error) {
|
||||
if rootKeyId == "" {
|
||||
return nil, fmt.Errorf("new root key: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
c := &TokenKey{
|
||||
TokenKey: &store.TokenKey{
|
||||
RootKeyId: rootKeyId,
|
||||
},
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// AllocTokenKey will allocate a key
|
||||
func AllocTokenKey() TokenKey {
|
||||
return TokenKey{
|
||||
TokenKey: &store.TokenKey{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key
|
||||
func (k *TokenKey) Clone() interface{} {
|
||||
cp := proto.Clone(k.TokenKey)
|
||||
return &TokenKey{
|
||||
TokenKey: cp.(*store.TokenKey),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// before it's written.
|
||||
func (k *TokenKey) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("token key vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.RootKeyId == "" {
|
||||
return fmt.Errorf("token key vet for write: missing root key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *TokenKey) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultTokenKeyTableName
|
||||
}
|
||||
|
||||
// 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 *TokenKey) 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 TokenKey attributes are
|
||||
// immutable and those tests are covered by TestTokenKey_ImmutableFields
|
||||
|
||||
func TestTokenKey_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.TokenKey
|
||||
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.TokenKey {
|
||||
k := kms.AllocTokenKey()
|
||||
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.AllocTokenKey()).Error)
|
||||
got, err := kms.NewTokenKey(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.TokenKeyPrefix)
|
||||
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 TestTokenKey_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.AllocTokenKey()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.TokenKey
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestTokenKey(t, conn, rk.PrivateId),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.TokenKey {
|
||||
k := kms.AllocTokenKey()
|
||||
id, err := db.NewPublicId(kms.TokenKeyPrefix)
|
||||
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.AllocTokenKey()
|
||||
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.AllocTokenKey()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenKey_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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
cp := k.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.TokenKey).TokenKey, k.TokenKey))
|
||||
})
|
||||
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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestTokenKey(t, conn, rk2.PrivateId)
|
||||
|
||||
cp := k.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.TokenKey).TokenKey, k2.TokenKey))
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenKey_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultTokenKeyTableName
|
||||
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.AllocTokenKey()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocTokenKey()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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 (
|
||||
DefaultTokenKeyVersionTableName = "kms_token_key_version"
|
||||
)
|
||||
|
||||
type TokenKeyVersion struct {
|
||||
*store.TokenKeyVersion
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// TokenKeyVersion creates a new in memory key version. No options are
|
||||
// currently supported.
|
||||
func NewTokenKeyVersion(tokenKeyId string, key []byte, rootKeyVersionId string, opt ...Option) (*TokenKeyVersion, error) {
|
||||
if tokenKeyId == "" {
|
||||
return nil, fmt.Errorf("new token key version: missing token key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("new token key version: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if rootKeyVersionId == "" {
|
||||
return nil, fmt.Errorf("new token key version: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
|
||||
k := &TokenKeyVersion{
|
||||
TokenKeyVersion: &store.TokenKeyVersion{
|
||||
TokenKeyId: tokenKeyId,
|
||||
RootKeyVersionId: rootKeyVersionId,
|
||||
Key: key,
|
||||
},
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// AllocTokenKeyVersion allocates a key version
|
||||
func AllocTokenKeyVersion() TokenKeyVersion {
|
||||
return TokenKeyVersion{
|
||||
TokenKeyVersion: &store.TokenKeyVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the key version
|
||||
func (k *TokenKeyVersion) Clone() interface{} {
|
||||
cp := proto.Clone(k.TokenKeyVersion)
|
||||
return &TokenKeyVersion{
|
||||
TokenKeyVersion: cp.(*store.TokenKeyVersion),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the key
|
||||
// version before it's written.
|
||||
func (k *TokenKeyVersion) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if k.PrivateId == "" {
|
||||
return fmt.Errorf("token key version vet for write: missing private id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if k.CtKey == nil {
|
||||
return fmt.Errorf("token key version vet for write: missing key: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.TokenKeyId == "" {
|
||||
return fmt.Errorf("token key version vet for write: missing token key id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
if k.RootKeyVersionId == "" {
|
||||
return fmt.Errorf("token key version vet for write: missing root key version id: %w", db.ErrInvalidParameter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (k *TokenKeyVersion) TableName() string {
|
||||
if k.tableName != "" {
|
||||
return k.tableName
|
||||
}
|
||||
return DefaultTokenKeyVersionTableName
|
||||
}
|
||||
|
||||
// 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 *TokenKeyVersion) SetTableName(n string) {
|
||||
k.tableName = n
|
||||
}
|
||||
|
||||
// Encrypt will encrypt the key version's key
|
||||
func (k *TokenKeyVersion) Encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.TokenKeyVersion directly
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, k.TokenKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting kms token key version: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt will decrypt the key version's key
|
||||
func (k *TokenKeyVersion) Decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
// structwrapping doesn't support embedding, so we'll pass in the
|
||||
// store.TokenKeyVersion directly
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, k.TokenKeyVersion, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting kms token 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 TokenKeyVersion attributes are
|
||||
// immutable and those tests are covered by TestTokenKey_ImmutableFields
|
||||
|
||||
func TestTokenKeyVersion_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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
|
||||
type args struct {
|
||||
tokenKeyId string
|
||||
key []byte
|
||||
rootKeyVersionId string
|
||||
opt []kms.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *kms.TokenKeyVersion
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty-id",
|
||||
args: args{
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-key",
|
||||
args: args{
|
||||
tokenKeyId: dk.PrivateId,
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-root-key-version-id",
|
||||
args: args{
|
||||
tokenKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: db.ErrInvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
tokenKeyId: dk.PrivateId,
|
||||
key: []byte("test key"),
|
||||
rootKeyVersionId: rkv.PrivateId,
|
||||
},
|
||||
want: func() *kms.TokenKeyVersion {
|
||||
k := kms.AllocTokenKeyVersion()
|
||||
k.RootKeyVersionId = rkv.PrivateId
|
||||
k.Key = []byte("test key")
|
||||
k.TokenKeyId = 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.AllocTokenKeyVersion()).Error)
|
||||
got, err := kms.NewTokenKeyVersion(tt.args.tokenKeyId, 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.TokenKeyPrefix)
|
||||
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 TestTokenKeyVersion_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)
|
||||
tk := kms.TestTokenKey(t, conn, rk.PrivateId)
|
||||
|
||||
require.NoError(t, conn.Where("1=1").Delete(kms.AllocTokenKeyVersion()).Error)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *kms.TokenKeyVersion
|
||||
wantRowsDeleted int
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
key: kms.TestTokenKeyVersion(t, conn, rkvWrapper, tk.PrivateId, []byte("test key")),
|
||||
wantErr: false,
|
||||
wantRowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
key: func() *kms.TokenKeyVersion {
|
||||
k := kms.AllocTokenKeyVersion()
|
||||
id, err := db.NewPublicId(kms.TokenKeyVersionPrefix)
|
||||
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.AllocTokenKeyVersion()
|
||||
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.AllocTokenKeyVersion()
|
||||
foundKey.PrivateId = tt.key.PrivateId
|
||||
err = rw.LookupById(context.Background(), &foundKey)
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenKeyVersion_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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
kv := kms.TestTokenKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
cp := kv.Clone()
|
||||
assert.True(proto.Equal(cp.(*kms.TokenKeyVersion).TokenKeyVersion, kv.TokenKeyVersion))
|
||||
})
|
||||
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.TestTokenKey(t, conn, rk.PrivateId)
|
||||
k2 := kms.TestTokenKey(t, conn, rk2.PrivateId)
|
||||
kv := kms.TestTokenKeyVersion(t, conn, rkvWrapper, k.PrivateId, []byte("test key"))
|
||||
kv2 := kms.TestTokenKeyVersion(t, conn, rkvWrapper2, k2.PrivateId, []byte("test key 2"))
|
||||
|
||||
cp := kv.Clone()
|
||||
assert.True(!proto.Equal(cp.(*kms.TokenKeyVersion).TokenKeyVersion, kv2.TokenKeyVersion))
|
||||
})
|
||||
}
|
||||
|
||||
func TestTokenKeyVersion_SetTableName(t *testing.T) {
|
||||
t.Parallel()
|
||||
defaultTableName := kms.DefaultTokenKeyVersionTableName
|
||||
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.AllocTokenKeyVersion()
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := kms.AllocTokenKeyVersion()
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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 OplogKey {
|
||||
// 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 OplogKeyVersion {
|
||||
// private_id is used to access the key version via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string private_id = 10;
|
||||
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string oplog_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;
|
||||
}
|
||||
@@ -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 SessionKey {
|
||||
// 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 SessionKeyVersion {
|
||||
// private_id is used to access the key version via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string private_id = 10;
|
||||
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string session_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;
|
||||
}
|
||||
@@ -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 TokenKey {
|
||||
// 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 TokenKeyVersion {
|
||||
// private_id is used to access the key version via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string private_id = 10;
|
||||
|
||||
// id for the key version
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string token_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