feat(credential): add credential type to credential library on create

This change adds a credential type attribute to credential library and
allows it to be set when creating a credential library.

Specifying the type of credential returned from a library allows for new
features and additional error checking to be added over time. A
User Password credential type and an Unspecified credential type are
included with the expectation that additional credential types will be
added in the future. This change also adds the credential type attribute
to existing credential libraries and sets it to Unspecified.

Known Limitations:

- Credential type is only supported on the repository create method.
  Support for the other CRUD methods will be added in the future.
This commit is contained in:
Michael Gaffney authored and Timothy Messier committed 2021-12-16 09:43:32 -05:00
1 parent f4b9b83caf
commit c317901db4
26 files changed
+1033 -107

No files matched your search

+12
View File
@@ -18,11 +18,23 @@ type Store interface {
GetScopeId() string
}
// Type is the type of credential provided by a library.
type Type string
// Credential type values.
const (
UnspecifiedType Type = "unspecified"
UserPasswordType Type = "user_password"
)
// A Library is a resource that provides credentials that are of the same
// type and access level from a single store.
type Library interface {
boundary.Resource
GetStoreId() string
// TODO(mgaffney) 10/2021: Add method for returning the credential type
// of the library
}
// Purpose is the purpose of the credential.
+30 -17
View File
@@ -92,6 +92,10 @@ type Library struct {
// The store_id of the owning store and must be set.
// @inject_tag: `gorm:"not_null"`
StoreId string `protobuf:"bytes,2,opt,name=store_id,json=storeId,proto3" json:"store_id,omitempty" gorm:"not_null"`
// credential_type is optional. If set, it indicates the type of
// credential the library returns.
// @inject_tag: `gorm:"default:null"`
CredentialType string `protobuf:"bytes,3,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty" gorm:"default:null"`
}
func (x *Library) Reset() {
@@ -140,6 +144,13 @@ func (x *Library) GetStoreId() string {
return ""
}
func (x *Library) GetCredentialType() string {
if x != nil {
return x.CredentialType
}
return ""
}
type Credential struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -320,27 +331,29 @@ var file_controller_storage_credential_store_v1_credential_proto_rawDesc = []byt
0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x49, 0x64, 0x22, 0x41, 0x0a, 0x07, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a,
0x49, 0x64, 0x22, 0x6a, 0x0a, 0x07, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a,
0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x49, 0x64, 0x22, 0x29, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64,
0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e,
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x29,
0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x22, 0x40, 0x0a, 0x06, 0x53, 0x74, 0x61,
0x74, 0x69, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64,
0x22, 0x40, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x49, 0x64, 0x22, 0x45, 0x0a, 0x07, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x1b, 0x0a,
0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69,
0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x42, 0x3f, 0x5a, 0x3d, 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, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x73,
0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x12, 0x19, 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x22, 0x45, 0x0a, 0x07, 0x44,
0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
0x49, 0x64, 0x42, 0x3f, 0x5a, 0x3d, 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, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1,8 +1,11 @@
package vault
import (
"context"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/oplog"
"google.golang.org/protobuf/proto"
)
@@ -21,17 +24,20 @@ const (
type CredentialLibrary struct {
*store.CredentialLibrary
tableName string `gorm:"-"`
MappingOverride MappingOverride `gorm:"-"`
}
// NewCredentialLibrary creates a new in memory CredentialLibrary
// for a Vault backend at vaultPath assigned to storeId.
// Name, description, method, and request body are the only valid options.
// All other options are ignored.
// Name, description, method, request body, credential type, and mapping
// override are the only valid options. All other options are ignored.
func NewCredentialLibrary(storeId string, vaultPath string, opt ...Option) (*CredentialLibrary, error) {
const op = "vault.NewCredentialLibrary"
opts := getOpts(opt...)
l := &CredentialLibrary{
MappingOverride: opts.withMappingOverride,
CredentialLibrary: &store.CredentialLibrary{
StoreId: storeId,
Name: opts.withName,
@@ -39,12 +45,21 @@ func NewCredentialLibrary(storeId string, vaultPath string, opt ...Option) (*Cre
VaultPath: vaultPath,
HttpRequestBody: opts.withRequestBody,
HttpMethod: string(opts.withMethod),
CredentialType: string(opts.withCredentialType),
},
}
return l, nil
}
func (l *CredentialLibrary) validate(ctx context.Context, caller errors.Op) error {
switch {
case !validMappingOverride(l.MappingOverride, l.CredentialType()):
return errors.New(ctx, errors.VaultInvalidMappingOverride, caller, "invalid credential type for mapping override")
}
return nil
}
func allocCredentialLibrary() *CredentialLibrary {
return &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{},
@@ -52,12 +67,25 @@ func allocCredentialLibrary() *CredentialLibrary {
}
func (l *CredentialLibrary) clone() *CredentialLibrary {
var m MappingOverride
if l.MappingOverride != nil {
m = l.MappingOverride.clone()
}
cp := proto.Clone(l.CredentialLibrary)
return &CredentialLibrary{
MappingOverride: m,
CredentialLibrary: cp.(*store.CredentialLibrary),
}
}
func (l *CredentialLibrary) setId(i string) {
l.PublicId = i
if l.MappingOverride != nil {
l.MappingOverride.setLibraryId(i)
}
}
// TableName returns the table name.
func (l *CredentialLibrary) TableName() string {
if l.tableName != "" {
@@ -83,4 +111,14 @@ func (l *CredentialLibrary) oplog(op oplog.OpType) oplog.Metadata {
return metadata
}
// CredentialType returns the type of credential the library retrieves.
func (l *CredentialLibrary) CredentialType() credential.Type {
switch ct := l.GetCredentialType(); ct {
case "":
return credential.UnspecifiedType
default:
return credential.Type(ct)
}
}
var _ credential.Library = (*CredentialLibrary)(nil)
@@ -4,6 +4,7 @@ import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
@@ -37,7 +38,6 @@ func TestCredentialLibrary_New(t *testing.T) {
name string
args args
want *CredentialLibrary
wantErr bool
wantCreateErr bool
}{
{
@@ -190,6 +190,42 @@ func TestCredentialLibrary_New(t *testing.T) {
},
wantCreateErr: true,
},
{
name: "minimum-actually-valid",
args: args{
storeId: cs.PublicId,
vaultPath: "vault/path",
opts: []Option{
WithMethod(MethodGet),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.PublicId,
VaultPath: "vault/path",
HttpMethod: string(MethodGet),
},
},
},
{
name: "credential-type",
args: args{
storeId: cs.PublicId,
vaultPath: "vault/path",
opts: []Option{
WithMethod(MethodGet),
WithCredentialType(credential.UserPasswordType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.PublicId,
VaultPath: "vault/path",
HttpMethod: string(MethodGet),
CredentialType: string(credential.UserPasswordType),
},
},
},
}
for _, tt := range tests {
@@ -198,17 +234,21 @@ func TestCredentialLibrary_New(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
got, err := NewCredentialLibrary(tt.args.storeId, tt.args.vaultPath, tt.args.opts...)
if tt.wantErr {
assert.Error(err)
require.Nil(got)
return
}
require.NoError(err)
require.NotNil(got)
assert.Emptyf(got.PublicId, "PublicId set")
assert.Equal(tt.want, got)
switch ct := tt.want.GetCredentialType(); ct {
case string(credential.UserPasswordType):
assert.Equal(credential.UserPasswordType, got.CredentialType())
case string(credential.UnspecifiedType), "":
assert.Equal(credential.UnspecifiedType, got.CredentialType())
default:
assert.Failf("Unknown credential type", "%s", ct)
}
id, err := newCredentialLibraryId()
assert.NoError(err)
@@ -0,0 +1,98 @@
package vault
import (
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/db/sanitize"
"github.com/hashicorp/boundary/internal/db/sentinel"
"google.golang.org/protobuf/proto"
)
// validMappingOverride reports whether the given mapping override is valid
// for the given credential type.
func validMappingOverride(m MappingOverride, ct credential.Type) bool {
switch m.(type) {
case nil:
return true // it is always valid to not specify a mapping override
case *UserPasswordOverride:
return ct == credential.UserPasswordType
default:
return false // an unknown mapping override type is never valid
}
}
// A MappingOverride is an interface holding one of the mapping override
// types: UserPasswordOverride.
type MappingOverride interface {
clone() MappingOverride
setLibraryId(i string)
// sanitize replaces all sentinel values in the MappingOverride with
// zero values. It is called before a MappingOverride is returned from
// the domain layer when the MappingOverride has been loaded from the
// database.
sanitize()
}
// A UserPasswordOverride contains optional values for overriding the
// default mappings used to map a Vault secret to a UserPassword credential
// type for the credential library that owns it.
type UserPasswordOverride struct {
*store.UserPasswordOverride
tableName string `gorm:"-"`
}
var _ MappingOverride = (*UserPasswordOverride)(nil)
// NewUserPasswordOverride creates a new in memory UserPasswordOverride.
// WithOverrideUsernameAttribute and WithOverridePasswordAttribute are the
// only valid options. All other options are ignored.
func NewUserPasswordOverride(opt ...Option) *UserPasswordOverride {
opts := getOpts(opt...)
o := &UserPasswordOverride{
UserPasswordOverride: &store.UserPasswordOverride{
UsernameAttribute: sanitize.String(opts.withOverrideUsernameAttribute),
PasswordAttribute: sanitize.String(opts.withOverridePasswordAttribute),
},
}
return o
}
func allocUserPasswordOverride() *UserPasswordOverride {
return &UserPasswordOverride{
UserPasswordOverride: &store.UserPasswordOverride{},
}
}
func (o *UserPasswordOverride) clone() MappingOverride {
cp := proto.Clone(o.UserPasswordOverride)
return &UserPasswordOverride{
UserPasswordOverride: cp.(*store.UserPasswordOverride),
}
}
func (o *UserPasswordOverride) setLibraryId(i string) {
o.LibraryId = i
}
func (o *UserPasswordOverride) sanitize() {
if sentinel.Is(o.UsernameAttribute) {
o.UsernameAttribute = ""
}
if sentinel.Is(o.PasswordAttribute) {
o.PasswordAttribute = ""
}
}
// TableName returns the table name.
func (o *UserPasswordOverride) TableName() string {
if o.tableName != "" {
return o.tableName
}
return "credential_vault_library_user_password_mapping_override"
}
// SetTableName sets the table name.
func (o *UserPasswordOverride) SetTableName(n string) {
o.tableName = n
}
@@ -0,0 +1,68 @@
package vault
import (
"fmt"
"testing"
"github.com/hashicorp/boundary/internal/credential"
"github.com/stretchr/testify/assert"
)
type unknownMapper int
func (u unknownMapper) clone() MappingOverride { return u }
func (u unknownMapper) setLibraryId(_ string) {}
func (u unknownMapper) sanitize() {}
var _ MappingOverride = unknownMapper(0)
func TestValidMappingOverrides(t *testing.T) {
tests := []struct {
m MappingOverride
ct credential.Type
want bool
}{
{
m: nil,
ct: "invalid",
want: true,
},
{
m: nil,
ct: credential.UnspecifiedType,
want: true,
},
{
m: nil,
ct: credential.UserPasswordType,
want: true,
},
{
m: unknownMapper(1),
ct: credential.UnspecifiedType,
want: false,
},
{
m: unknownMapper(1),
ct: credential.UserPasswordType,
want: false,
},
{
m: allocUserPasswordOverride(),
ct: credential.UnspecifiedType,
want: false,
},
{
m: allocUserPasswordOverride(),
ct: credential.UserPasswordType,
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf("%T-%s", tt.m, tt.ct), func(t *testing.T) {
got := validMappingOverride(tt.m, tt.ct)
assert.Equal(t, tt.want, got)
})
}
}
+49 -10
View File
@@ -1,5 +1,7 @@
package vault
import "github.com/hashicorp/boundary/internal/credential"
// getOpts - iterate the inbound Options and return a struct
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
@@ -14,16 +16,21 @@ type Option func(*options)
// options = how options are represented
type options struct {
withName string
withDescription string
withLimit int
withCACert []byte
withNamespace string
withTlsServerName string
withTlsSkipVerify bool
withClientCert *ClientCertificate
withMethod Method
withRequestBody []byte
withName string
withDescription string
withLimit int
withCACert []byte
withNamespace string
withTlsServerName string
withTlsSkipVerify bool
withClientCert *ClientCertificate
withMethod Method
withRequestBody []byte
withCredentialType credential.Type
withOverrideUsernameAttribute string
withOverridePasswordAttribute string
withMappingOverride MappingOverride
}
func getDefaultOptions() options {
@@ -109,3 +116,35 @@ func WithRequestBody(b []byte) Option {
o.withRequestBody = b
}
}
// WithCredentialType provides an optional credential type to associate
// with a credential library.
func WithCredentialType(t credential.Type) Option {
return func(o *options) {
o.withCredentialType = t
}
}
// WithOverrideUsernameAttribute provides the name of an attribute in the
// Data field of a Vault api.Secret that maps to a username value.
func WithOverrideUsernameAttribute(s string) Option {
return func(o *options) {
o.withOverrideUsernameAttribute = s
}
}
// WithOverridePasswordAttribute provides the name of an attribute in the
// Data field of a Vault api.Secret that maps to a password value.
func WithOverridePasswordAttribute(s string) Option {
return func(o *options) {
o.withOverridePasswordAttribute = s
}
}
// WithMappingOverride provides an optional mapping override to use for
// mapping the Data fields of a Vault api.Secret to a credential.
func WithMappingOverride(m MappingOverride) Option {
return func(o *options) {
o.withMappingOverride = m
}
}
+25
View File
@@ -3,6 +3,7 @@ package vault
import (
"testing"
"github.com/hashicorp/boundary/internal/credential"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -83,4 +84,28 @@ func Test_GetOpts(t *testing.T) {
testOpts.withRequestBody = []byte("body")
assert.Equal(t, opts, testOpts)
})
t.Run("WithCredentialType", func(t *testing.T) {
opts := getOpts(WithCredentialType(credential.UserPasswordType))
testOpts := getDefaultOptions()
testOpts.withCredentialType = credential.UserPasswordType
assert.Equal(t, opts, testOpts)
})
t.Run("WithOverrideUsernameAttribute", func(t *testing.T) {
opts := getOpts(WithOverrideUsernameAttribute("test"))
testOpts := getDefaultOptions()
testOpts.withOverrideUsernameAttribute = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithOverridePasswordAttribute", func(t *testing.T) {
opts := getOpts(WithOverridePasswordAttribute("test"))
testOpts := getDefaultOptions()
testOpts.withOverridePasswordAttribute = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithMappingOverride", func(t *testing.T) {
opts := getOpts(WithMappingOverride(unknownMapper(1)))
testOpts := getDefaultOptions()
testOpts.withMappingOverride = unknownMapper(1)
assert.Equal(t, opts, testOpts)
})
}
@@ -48,11 +48,15 @@ func (r *Repository) CreateCredentialLibrary(ctx context.Context, scopeId string
l.HttpMethod = string(MethodGet)
}
if err := l.validate(ctx, op); err != nil {
return nil, err // intentionally not wrapped.
}
id, err := newCredentialLibraryId()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
l.PublicId = id
l.setId(id)
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
@@ -62,11 +66,34 @@ func (r *Repository) CreateCredentialLibrary(ctx context.Context, scopeId string
var newCredentialLibrary *CredentialLibrary
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
newCredentialLibrary = l.clone()
err := w.Create(ctx, newCredentialLibrary, db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_CREATE)))
var msgs []*oplog.Message
ticket, err := w.GetTicket(l)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
// insert credential library
newCredentialLibrary = l.clone()
var lOplogMsg oplog.Message
if err := w.Create(ctx, newCredentialLibrary, db.NewOplogMsg(&lOplogMsg)); err != nil {
return errors.Wrap(ctx, err, op)
}
msgs = append(msgs, &lOplogMsg)
// insert mapper override (if exists)
if l.MappingOverride != nil {
var msg oplog.Message
if err := w.Create(ctx, newCredentialLibrary.MappingOverride, db.NewOplogMsg(&msg)); err != nil {
return errors.Wrap(ctx, err, op)
}
newCredentialLibrary.MappingOverride.sanitize()
msgs = append(msgs, &msg)
}
metadata := l.oplog(oplog.OpType_OP_TYPE_CREATE)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
return nil
},
)
@@ -7,6 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
@@ -166,6 +167,71 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
},
},
},
{
name: "valid-user-password-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
},
},
},
{
name: "unknown-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: unknownMapper(1),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "invalid-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: NewUserPasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "valid-user-password-credential-type-with-override",
in: &CredentialLibrary{
MappingOverride: NewUserPasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
},
},
want: &CredentialLibrary{
MappingOverride: NewUserPasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
},
},
},
}
for _, tt := range tests {
@@ -191,8 +257,28 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
assert.NotSame(tt.in, got)
assert.Equal(tt.want.Name, got.Name)
assert.Equal(tt.want.Description, got.Description)
assert.Equal(tt.want.CredentialType(), got.CredentialType())
assert.Equal(got.CreateTime, got.UpdateTime)
assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
if tt.in.MappingOverride != nil {
require.NotNil(got.MappingOverride)
assert.IsType(tt.want.MappingOverride, got.MappingOverride)
switch w := tt.want.MappingOverride.(type) {
case *UserPasswordOverride:
g, ok := got.MappingOverride.(*UserPasswordOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
default:
assert.Fail("Unknown mapping override")
}
// verify it was persisted in the database
override := allocUserPasswordOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", got.GetPublicId()))
}
})
}
+181 -64
View File
@@ -502,6 +502,10 @@ type CredentialLibrary struct {
// Can only be set if http_method is POST.
// @inject_tag: `gorm:"default:null"`
HttpRequestBody []byte `protobuf:"bytes,10,opt,name=http_request_body,json=httpRequestBody,proto3" json:"http_request_body,omitempty" gorm:"default:null"`
// credential_type is optional. If set, it indicates the type of
// credential the library returns.
// @inject_tag: `gorm:"default:null"`
CredentialType string `protobuf:"bytes,11,opt,name=credential_type,json=credentialType,proto3" json:"credential_type,omitempty" gorm:"default:null"`
}
func (x *CredentialLibrary) Reset() {
@@ -606,6 +610,13 @@ func (x *CredentialLibrary) GetHttpRequestBody() []byte {
return nil
}
func (x *CredentialLibrary) GetCredentialType() string {
if x != nil {
return x.CredentialType
}
return ""
}
type Credential struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -776,6 +787,87 @@ func (x *Credential) GetStatus() string {
return ""
}
type UserPasswordOverride struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// library_id of the owning vault credential library.
// @inject_tag: `gorm:"primary_key"`
LibraryId string `protobuf:"bytes,1,opt,name=library_id,json=libraryId,proto3" json:"library_id,omitempty" gorm:"primary_key"`
// username_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a username.
// If set, it overrides any default attribute names the system uses to
// find a username attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
UsernameAttribute string `protobuf:"bytes,2,opt,name=username_attribute,json=usernameAttribute,proto3" json:"username_attribute,omitempty" gorm:"default:null"`
// password_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a password.
// If set, it overrides any default attribute names the system uses to
// find a password attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
PasswordAttribute string `protobuf:"bytes,3,opt,name=password_attribute,json=passwordAttribute,proto3" json:"password_attribute,omitempty" gorm:"default:null"`
}
func (x *UserPasswordOverride) Reset() {
*x = UserPasswordOverride{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UserPasswordOverride) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserPasswordOverride) ProtoMessage() {}
func (x *UserPasswordOverride) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5]
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 UserPasswordOverride.ProtoReflect.Descriptor instead.
func (*UserPasswordOverride) Descriptor() ([]byte, []int) {
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP(), []int{5}
}
func (x *UserPasswordOverride) GetLibraryId() string {
if x != nil {
return x.LibraryId
}
return ""
}
func (x *UserPasswordOverride) GetUsernameAttribute() string {
if x != nil {
return x.UsernameAttribute
}
return ""
}
func (x *UserPasswordOverride) GetPasswordAttribute() string {
if x != nil {
return x.PasswordAttribute
}
return ""
}
var File_controller_storage_credential_vault_store_v1_vault_proto protoreflect.FileDescriptor
var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []byte{
@@ -897,7 +989,7 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []by
0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x48, 0x6d, 0x61, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x6b,
0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79,
0x49, 0x64, 0x22, 0xd4, 0x04, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x49, 0x64, 0x22, 0xfd, 0x04, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f,
@@ -934,48 +1026,60 @@ var file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc = []by
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x1c, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x52, 0x0f, 0x68, 0x74, 0x74, 0x70, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61,
0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61,
0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d,
0x61, 0x63, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
0x65, 0x18, 0x05, 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, 0x12,
0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06,
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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f,
0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 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, 0x0f,
0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12,
0x53, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69,
0x6d, 0x65, 0x18, 0x0a, 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, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77,
0x61, 0x62, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65,
0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42,
0x45, 0x5a, 0x43, 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, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79,
0x70, 0x65, 0x22, 0xc3, 0x04, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d,
0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a,
0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x12, 0x4b, 0x0a, 0x0b, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 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, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 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, 0x75, 0x70, 0x64, 0x61, 0x74,
0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64,
0x12, 0x56, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 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, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6e,
0x65, 0x77, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x53, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 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, 0x0e, 0x65,
0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a,
0x0c, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0b, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x52, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x62, 0x6c, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x14, 0x55, 0x73, 0x65,
0x72, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64,
0x12, 0x2d, 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73,
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12,
0x2d, 0x0a, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x42, 0x45,
0x5a, 0x43, 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, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -990,29 +1094,30 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_rawDescGZIP()
return file_controller_storage_credential_vault_store_v1_vault_proto_rawDescData
}
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_controller_storage_credential_vault_store_v1_vault_proto_goTypes = []interface{}{
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
(*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate
(*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary
(*Credential)(nil), // 4: controller.storage.credential.vault.store.v1.Credential
(*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp
(*CredentialStore)(nil), // 0: controller.storage.credential.vault.store.v1.CredentialStore
(*Token)(nil), // 1: controller.storage.credential.vault.store.v1.Token
(*ClientCertificate)(nil), // 2: controller.storage.credential.vault.store.v1.ClientCertificate
(*CredentialLibrary)(nil), // 3: controller.storage.credential.vault.store.v1.CredentialLibrary
(*Credential)(nil), // 4: controller.storage.credential.vault.store.v1.Credential
(*UserPasswordOverride)(nil), // 5: controller.storage.credential.vault.store.v1.UserPasswordOverride
(*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_credential_vault_store_v1_vault_proto_depIdxs = []int32{
5, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 0: controller.storage.credential.vault.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 1: controller.storage.credential.vault.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 2: controller.storage.credential.vault.store.v1.CredentialStore.delete_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 3: controller.storage.credential.vault.store.v1.Token.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 4: controller.storage.credential.vault.store.v1.Token.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 5: controller.storage.credential.vault.store.v1.Token.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 6: controller.storage.credential.vault.store.v1.Token.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 7: controller.storage.credential.vault.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 8: controller.storage.credential.vault.store.v1.CredentialLibrary.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 9: controller.storage.credential.vault.store.v1.Credential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 10: controller.storage.credential.vault.store.v1.Credential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 11: controller.storage.credential.vault.store.v1.Credential.last_renewal_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 12: controller.storage.credential.vault.store.v1.Credential.expiration_time:type_name -> controller.storage.timestamp.v1.Timestamp
13, // [13:13] is the sub-list for method output_type
13, // [13:13] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
@@ -1086,6 +1191,18 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
return nil
}
}
file_controller_storage_credential_vault_store_v1_vault_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UserPasswordOverride); 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{
@@ -1093,7 +1210,7 @@ func file_controller_storage_credential_vault_store_v1_vault_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_credential_vault_store_v1_vault_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},
@@ -0,0 +1,17 @@
begin;
create function wt_to_sentinel(string text)
returns text
as $$
select concat(
u&'\fffe',
trim(trailing u&'\ffff' from trim(leading u&'\fffe ' from string)),
u&'\ffff'
);
$$ language sql
immutable
returns null on null input;
comment on function wt_to_sentinel is
'wt_to_sentinel takes a string and returns it as a wt_sentinel';
commit;
@@ -0,0 +1,77 @@
begin;
create table credential_type_enm (
name text primary key
constraint only_predefined_credential_types_allowed
check (
name in (
'unspecified',
'user_password'
)
)
);
comment on table credential_type_enm is
'credential_type_enm is an enumeration table for the types of credentials a credential library can provide.';
insert into credential_type_enm (name)
values
('unspecified'),
('user_password');
-- Add a credential_type column to the base credential_library table and the
-- credential_vault_library subtype table.
alter table credential_library
add column credential_type text not null default 'unspecified'
constraint credential_type_enm_fkey
references credential_type_enm (name)
on delete restrict
on update cascade,
add constraint credential_library_store_id_public_id_credential_type_uq
unique(store_id, public_id, credential_type)
;
alter table credential_vault_library
add column credential_type text not null default 'unspecified'
constraint credential_type_enm_fkey
references credential_type_enm (name)
on delete restrict
on update cascade,
drop constraint credential_library_fkey,
add constraint credential_library_fkey
foreign key (store_id, public_id, credential_type)
references credential_library (store_id, public_id, credential_type)
on delete cascade
on update cascade
;
-- This drops a constraint that is no longer needed.
-- It could not be dropped until after the credential_library_fkey constraint was
-- dropped and then recreated.
alter table credential_library
drop constraint credential_library_store_id_public_id_uq
;
-- Drop and recreate the insert trigger function for credential_library subtypes.
drop trigger insert_credential_library_subtype on credential_vault_library;
drop function insert_credential_library_subtype();
-- insert_credential_library_subtype() is a before insert trigger
-- function for subtypes of credential_library
create function insert_credential_library_subtype()
returns trigger
as $$
begin
insert into credential_library
(public_id, store_id, credential_type)
values
(new.public_id, new.store_id, new.credential_type);
return new;
end;
$$ language plpgsql;
create trigger insert_credential_library_subtype before insert on credential_vault_library
for each row execute procedure insert_credential_library_subtype();
commit;
@@ -0,0 +1,40 @@
begin;
create table credential_vault_library_mapping_override (
library_id wt_public_id primary key
constraint credential_vault_library_fkey
references credential_vault_library (public_id)
on delete cascade
on update cascade
);
comment on table credential_vault_library_mapping_override is
'credential_vault_library_mapping_override is a base table for the vault library mapping override type. '
'Each row is owned by a single vault library and maps 1-to-1 to a row in one of the vault library mapping override subtype tables.';
-- insert_credential_vault_library_mapping_override_subtype() is a before insert trigger
-- function for subtypes of credential_vault_library_mapping_override
create function insert_credential_vault_library_mapping_override_subtype()
returns trigger
as $$
begin
insert into credential_vault_library_mapping_override
(library_id)
values
(new.library_id);
return new;
end;
$$ language plpgsql;
-- delete_credential_vault_library_mapping_override_subtype() is an after delete trigger
-- function for subtypes of credential_vault_library_mapping_override
create function delete_credential_vault_library_mapping_override_subtype()
returns trigger
as $$
begin
delete from credential_vault_library_mapping_override
where library_id = old.library_id;
return null; -- result is ignored since this is an after trigger
end;
$$ language plpgsql;
commit;
@@ -0,0 +1,32 @@
begin;
create table credential_vault_library_user_password_mapping_override (
library_id wt_public_id primary key
constraint credential_vault_library_fkey
references credential_vault_library (public_id)
on delete cascade
on update cascade
constraint credential_vault_library_mapping_override_fkey
references credential_vault_library_mapping_override (library_id)
on delete cascade
on update cascade,
username_attribute wt_sentinel
default wt_to_sentinel('no override')
not null,
password_attribute wt_sentinel
default wt_to_sentinel('no override')
not null
);
comment on table credential_vault_library_user_password_mapping_override is
'credential_vault_library_user_password_mapping_override is a table '
'where each row represents a mapping that overrides the default mapping '
'from a generic vault secret to a user password credential type '
'for a vault credential library.';
create trigger insert_credential_vault_library_mapping_override_subtype before insert on credential_vault_library_user_password_mapping_override
for each row execute procedure insert_credential_vault_library_mapping_override_subtype();
create trigger delete_credential_vault_library_mapping_override_subtype after delete on credential_vault_library_user_password_mapping_override
for each row execute procedure delete_credential_vault_library_mapping_override_subtype();
commit;
+3 -1
View File
@@ -18,7 +18,9 @@ endif
PROVE_OPTS ?=
TESTS ?= tests/setup/*.sql \
tests/wh/*/*.sql
tests/wh/*/*.sql \
tests/sentinel/*.sql \
tests/credential/*/*.sql
POSTGRES_DOCKER_IMAGE_BASE ?= postgres
@@ -314,11 +314,35 @@ begin;
('p____bwidget', 'vs_______wvs', 'widget vault store', 'None', 'https://vault.widget', 'default');
insert into credential_vault_library
(store_id, public_id, name, description, vault_path, http_method)
(store_id, public_id, name, description, vault_path, http_method, credential_type)
values
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET'),
('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET'),
('vs_______wvs', 'vl______wvl3', 'widget vault kv', 'None', '/secrets/kv', 'GET');
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'user_password');
insert into credential_vault_library_user_password_mapping_override
(library_id)
values
('vl______wvl4');
insert into credential_vault_library_user_password_mapping_override
(library_id, username_attribute)
values
('vl______wvl5', 'my_username');
insert into credential_vault_library_user_password_mapping_override
(library_id, password_attribute)
values
('vl______wvl6', 'my_password');
insert into credential_vault_library_user_password_mapping_override
(library_id, username_attribute, password_attribute)
values
('vl______wvl7', 'my_username', 'my_password');
insert into target_credential_library
(target_id, credential_library_id, credential_purpose)
@@ -0,0 +1,39 @@
-- credential_vault_library tests:
-- the following triggers
-- insert_credential_library_subtype
-- delete_credential_library_subtype
begin;
select plan(10);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
-- validate the setup data
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'user_password';
-- validate the insert triggers
prepare insert_vault_library as
insert into credential_vault_library
(store_id, public_id, vault_path, http_method, credential_type)
values
('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'user_password');
select lives_ok('insert_vault_library');
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'user_password';
-- validate the delete triggers
prepare delete_vault_library as
delete
from credential_vault_library
where public_id = 'vl_______tt1';
select lives_ok('delete_vault_library');
select is(count(*), 0::bigint) from credential_vault_library where public_id = 'vl_______tt1';
select is(count(*), 0::bigint) from credential_library where public_id = 'vl_______tt1';
select * from finish();
rollback;
@@ -0,0 +1,44 @@
-- credential_vault_library_user_password_mapping_override tests:
-- the following triggers
-- insert_credential_vault_library_mapping_override_subtype
-- delete_credential_vault_library_mapping_override_subtype
begin;
select plan(10);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
-- validate the setup data
select is(count(*), 4::bigint)
from credential_vault_library_user_password_mapping_override
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7');
select is(count(*), 4::bigint)
from credential_vault_library_mapping_override
where library_id in ('vl______wvl4', 'vl______wvl5', 'vl______wvl6', 'vl______wvl7');
-- validate the insert triggers
select is(count(*), 0::bigint) from credential_vault_library_user_password_mapping_override where library_id = 'vl______wvl3';
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
prepare insert_credential_vault_library_user_password_mapping_override as
insert into credential_vault_library_user_password_mapping_override
(library_id, username_attribute, password_attribute)
values
('vl______wvl3', 'my_username', 'my_password');
select lives_ok('insert_credential_vault_library_user_password_mapping_override');
select is(count(*), 1::bigint) from credential_vault_library_user_password_mapping_override where library_id = 'vl______wvl3';
select is(count(*), 1::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
-- validate the delete triggers
prepare delete_credential_vault_library_user_password_mapping_override as
delete
from credential_vault_library_user_password_mapping_override
where library_id = 'vl______wvl3';
select lives_ok('delete_credential_vault_library_user_password_mapping_override');
select is(count(*), 0::bigint) from credential_vault_library_user_password_mapping_override where library_id = 'vl______wvl3';
select is(count(*), 0::bigint) from credential_vault_library_mapping_override where library_id = 'vl______wvl3';
select * from finish();
rollback;
@@ -0,0 +1,21 @@
-- is_sentinel tests:
-- wt_is_sentinel function
begin;
select plan(10);
select has_domain('wt_sentinel');
select has_function('wt_is_sentinel', array['text']);
select ok(wt_is_sentinel(u&'\fffe' || 'default' || u&'\ffff'), 'basic sentinel');
select ok(wt_is_sentinel(u&'\fffe' || u&'\ffff'), '0 length sentinel');
select ok(wt_is_sentinel(u&'\fffe' || ' ' || u&'\ffff'), 'empty sentinel');
select ok(not wt_is_sentinel('default'), 'a word is not a sentinel');
select ok(not wt_is_sentinel(''), '0 length string is not a sentinel');
select ok(not wt_is_sentinel(' '), 'empty string is not a sentinel');
select ok(not wt_is_sentinel(u&'\fffe' || 'default'), 'missing suffix of ffff is not a sentinel');
select ok(not wt_is_sentinel('default' || u&'\ffff'), 'missing prefix of fffe is not a sentinel');
select * from finish();
rollback;
@@ -0,0 +1,21 @@
-- to_sentinel tests:
-- wt_to_sentinel function
begin;
select plan(10);
select has_domain('wt_sentinel');
select has_function('wt_to_sentinel', array['text']);
select is(wt_to_sentinel('default'), u&'\fffe' || 'default' || u&'\ffff', 'create basic sentinel');
select is(wt_to_sentinel(u&'\fffe' || 'default' || u&'\ffff'), u&'\fffe' || 'default' || u&'\ffff', 'no changes to existing sentinel');
select is(wt_to_sentinel(''), u&'\fffe' || u&'\ffff', '0 length sentinel');
select is(wt_to_sentinel(' '), u&'\fffe' || u&'\ffff', 'empty string becomes 0 length sentinel');
select is(wt_to_sentinel(u&'\fffe' || u&'\ffff'), u&'\fffe' || u&'\ffff', '0 length sentinel to 0 length sentinel');
select is(wt_to_sentinel(u&'\fffe'), u&'\fffe' || u&'\ffff', 'prefix to 0 length sentinel');
select is(wt_to_sentinel(u&'\ffff'), u&'\fffe' || u&'\ffff', 'suffix to 0 length sentinel');
select is(wt_to_sentinel(NULL), NULL, 'null to null');
select * from finish();
rollback;
+1
View File
@@ -108,6 +108,7 @@ const (
VaultTokenMissingCapabilities Code = 3013 // VaultTokenMissingCapabilities represents an error for a Vault token that is missing capabilities
VaultCredentialRequest Code = 3014 // VaultCredentialRequest represents an error returned from Vault when retrieving a credential
VaultEmptySecret Code = 3015 // VaultEmptySecret represents a empty secret was returned from Vault without error
VaultInvalidMappingOverride Code = 3016 // VaultInvalidMappingOverride represents an error returned when a credential mapping is unknown or does not match a credential type
// OIDC authentication provided errors
OidcProviderCallbackError Code = 4000 // OidcProviderCallbackError represents an error that is passed by the OIDC provider to the callback endpoint
+5
View File
@@ -287,6 +287,11 @@ func TestCode_Both_String_Info(t *testing.T) {
c: VaultEmptySecret,
want: VaultEmptySecret,
},
{
name: "VaultInvalidMappingOverride",
c: VaultInvalidMappingOverride,
want: VaultInvalidMappingOverride,
},
{
name: "OidcProviderCallbackError",
c: OidcProviderCallbackError,
+4
View File
@@ -232,6 +232,10 @@ var errorCodeInfo = map[Code]Info{
Message: "vault secret is empty",
Kind: Integrity,
},
VaultInvalidMappingOverride: {
Message: "invalid credential mapping override",
Kind: Parameter,
},
OidcProviderCallbackError: {
Message: "oidc provider callback error",
Kind: External,
@@ -22,6 +22,11 @@ message Library {
// The store_id of the owning store and must be set.
// @inject_tag: `gorm:"not_null"`
string store_id = 2;
// credential_type is optional. If set, it indicates the type of
// credential the library returns.
// @inject_tag: `gorm:"default:null"`
string credential_type = 3;
}
message Credential {
@@ -205,6 +205,11 @@ message CredentialLibrary {
// Can only be set if http_method is POST.
// @inject_tag: `gorm:"default:null"`
bytes http_request_body = 10 [(custom_options.v1.mask_mapping) = {this:"HttpRequestBody" that: "attributes.http_request_body"}];
// credential_type is optional. If set, it indicates the type of
// credential the library returns.
// @inject_tag: `gorm:"default:null"`
string credential_type = 11;
}
message Credential {
@@ -267,3 +272,29 @@ message Credential {
// @inject_tag: `gorm:"not_null"`
string status = 12;
}
message UserPasswordOverride {
// library_id of the owning vault credential library.
// @inject_tag: `gorm:"primary_key"`
string library_id = 1;
// username_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a username.
// If set, it overrides any default attribute names the system uses to
// find a username attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
string username_attribute = 2;
// password_attribute is the name of the attribute in the Data field of a
// Vault api.Secret that maps to a password.
// If set, it overrides any default attribute names the system uses to
// find a password attribute.
//
// See https://github.com/hashicorp/vault/blob/5e505ec039177e8212cbbab74ccb644c46e62e63/api/secret.go#L25
//
// @inject_tag: `gorm:"default:null"`
string password_attribute = 3;
}