feat(credential): Add mapping override to credential library on lookup
This change returns any mapping override set on a credential library when the library is retrieved from the repository using the lookup method.
This commit is contained in:
1 parent
0e637f47b5
commit
ba8188e6d7
4 files changed
+277
-58
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
dbcommon "github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
@@ -214,7 +215,7 @@ func (r *Repository) LookupCredentialLibrary(ctx context.Context, publicId strin
|
||||
if publicId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
l := allocCredentialLibrary()
|
||||
l := allocPublicLibrary()
|
||||
l.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, l); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
@@ -222,9 +223,63 @@ func (r *Repository) LookupCredentialLibrary(ctx context.Context, publicId strin
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
|
||||
}
|
||||
return l, nil
|
||||
return l.toCredentialLibrary(), nil
|
||||
}
|
||||
|
||||
// publicLibrary is a credential library and any of library's credential
|
||||
// mapping overrides. It does not include encrypted data and is safe to
|
||||
// return external to boundary.
|
||||
type publicLibrary struct {
|
||||
PublicId string `gorm:"primary_key"`
|
||||
StoreId string
|
||||
Name string
|
||||
Description string
|
||||
CreateTime *timestamp.Timestamp
|
||||
UpdateTime *timestamp.Timestamp
|
||||
Version uint32
|
||||
VaultPath string
|
||||
HttpMethod string
|
||||
HttpRequestBody []byte
|
||||
CredentialType string
|
||||
UsernameAttribute string
|
||||
PasswordAttribute string
|
||||
}
|
||||
|
||||
func allocPublicLibrary() *publicLibrary {
|
||||
return &publicLibrary{}
|
||||
}
|
||||
|
||||
func (pl *publicLibrary) toCredentialLibrary() *CredentialLibrary {
|
||||
cl := allocCredentialLibrary()
|
||||
cl.PublicId = pl.PublicId
|
||||
cl.StoreId = pl.StoreId
|
||||
cl.Name = pl.Name
|
||||
cl.Description = pl.Description
|
||||
cl.CreateTime = pl.CreateTime
|
||||
cl.UpdateTime = pl.UpdateTime
|
||||
cl.Version = pl.Version
|
||||
cl.VaultPath = pl.VaultPath
|
||||
cl.HttpMethod = pl.HttpMethod
|
||||
cl.HttpRequestBody = pl.HttpRequestBody
|
||||
cl.CredentialLibrary.CredentialType = pl.CredentialType
|
||||
|
||||
if pl.UsernameAttribute != "" || pl.PasswordAttribute != "" {
|
||||
up := allocUserPasswordOverride()
|
||||
up.LibraryId = pl.PublicId
|
||||
up.UsernameAttribute = pl.UsernameAttribute
|
||||
up.PasswordAttribute = pl.PasswordAttribute
|
||||
up.sanitize()
|
||||
cl.MappingOverride = up
|
||||
}
|
||||
return cl
|
||||
}
|
||||
|
||||
// TableName returns the table name for gorm.
|
||||
func (_ *publicLibrary) TableName() string { return "credential_vault_library_public" }
|
||||
|
||||
// GetPublicId returns the public id.
|
||||
func (pl *publicLibrary) GetPublicId() string { return pl.PublicId }
|
||||
|
||||
// DeleteCredentialLibrary deletes publicId from the repository and returns
|
||||
// the number of records deleted.
|
||||
func (r *Repository) DeleteCredentialLibrary(ctx context.Context, scopeId string, publicId string, _ ...Option) (int, error) {
|
||||
|
||||
@@ -212,9 +212,11 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
|
||||
wantErr: errors.VaultInvalidMappingOverride,
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-override",
|
||||
name: "valid-user-password-credential-type-with-username-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(WithOverrideUsernameAttribute("test")),
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
@@ -223,7 +225,61 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(WithOverrideUsernameAttribute("test")),
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-username-and-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
want: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
@@ -1021,64 +1077,151 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
l := TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 1)[0]
|
||||
{
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
|
||||
badId, err := newCredentialLibraryId()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, badId)
|
||||
tests := []struct {
|
||||
name string
|
||||
in *CredentialLibrary
|
||||
}{
|
||||
{
|
||||
name: "valid-no-options",
|
||||
in: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type",
|
||||
in: &CredentialLibrary{
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-username-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-user-password-credential-type-with-username-and-password-override",
|
||||
in: &CredentialLibrary{
|
||||
MappingOverride: NewUserPasswordOverride(
|
||||
WithOverrideUsernameAttribute("utest"),
|
||||
WithOverridePasswordAttribute("ptest"),
|
||||
),
|
||||
CredentialLibrary: &store.CredentialLibrary{
|
||||
StoreId: cs.GetPublicId(),
|
||||
HttpMethod: "GET",
|
||||
VaultPath: "/some/path",
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want *CredentialLibrary
|
||||
wantErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
in: l.GetPublicId(),
|
||||
want: l,
|
||||
},
|
||||
{
|
||||
name: "empty-public-id",
|
||||
in: "",
|
||||
wantErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
in: badId,
|
||||
},
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// setup
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, sche)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
orig, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), tt.in)
|
||||
assert.NoError(err)
|
||||
require.NotEmpty(orig)
|
||||
// test
|
||||
got, err := repo.LookupCredentialLibrary(ctx, orig.GetPublicId())
|
||||
assert.NoError(err)
|
||||
require.NotEmpty(got)
|
||||
assert.Equal(orig.Name, got.Name)
|
||||
assert.Equal(orig.Description, got.Description)
|
||||
assert.Equal(orig.CredentialType(), got.CredentialType())
|
||||
if tt.in.MappingOverride != nil {
|
||||
require.NotNil(got.MappingOverride)
|
||||
assert.IsType(orig.MappingOverride, got.MappingOverride)
|
||||
switch w := orig.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")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, sche)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
t.Run("empty-public-id", func(t *testing.T) {
|
||||
// setup
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, sche)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
// test
|
||||
got, err := repo.LookupCredentialLibrary(ctx, "")
|
||||
wantErr := errors.InvalidParameter
|
||||
assert.Truef(errors.Match(errors.T(wantErr), err), "want err: %q got: %q", wantErr, err)
|
||||
assert.Nil(got)
|
||||
})
|
||||
|
||||
got, err := repo.LookupCredentialLibrary(ctx, tt.in)
|
||||
if tt.wantErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
|
||||
assert.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
switch {
|
||||
case tt.want == nil:
|
||||
assert.Nil(got)
|
||||
case tt.want != nil:
|
||||
assert.NotNil(got)
|
||||
assert.Equal(got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run("not-found", func(t *testing.T) {
|
||||
// setup
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, sche)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
badId, err := newCredentialLibraryId()
|
||||
assert.NoError(err)
|
||||
require.NotNil(badId)
|
||||
// test
|
||||
got, err := repo.LookupCredentialLibrary(ctx, badId)
|
||||
assert.NoError(err)
|
||||
assert.Empty(got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_DeleteCredentialLibrary(t *testing.T) {
|
||||
|
||||
@@ -577,6 +577,7 @@ begin;
|
||||
'credential_vault_store_public is a view where each row contains a credential store. '
|
||||
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
-- Replaced in 21/05_vault_private_library.up.sql
|
||||
create view credential_vault_library_private as
|
||||
select library.public_id as public_id,
|
||||
library.store_id as store_id,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
begin;
|
||||
|
||||
-- Replaces view from 10/04_vault_credential.up.sql
|
||||
drop view credential_vault_library_private;
|
||||
|
||||
create view credential_vault_library_private as
|
||||
@@ -45,4 +46,23 @@ begin;
|
||||
'credential_vault_library_private is a view where each row contains a credential library and the credential library''s data needed to connect to Vault. '
|
||||
'Each row may contain encrypted data. This view should not be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
create view credential_vault_library_public as
|
||||
select public_id,
|
||||
store_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
vault_path,
|
||||
http_method,
|
||||
http_request_body,
|
||||
credential_type,
|
||||
username_attribute,
|
||||
password_attribute
|
||||
from credential_vault_library_private;
|
||||
comment on view credential_vault_library_public is
|
||||
'credential_vault_library_public is a view where each row contains a credential library and any of library''s credential mapping overrides. '
|
||||
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
|
||||
|
||||
commit;
|
||||
Reference in new issue
Block a user