Files
threshold/internal/credential/vault/repository_credential_library_test.go
T

2554 lines
79 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package vault
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/globals"
"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"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestRepository_CreateCredentialLibrary(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
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]
tests := []struct {
name string
in *CredentialLibrary
opts []Option
want *CredentialLibrary
wantErr errors.Code
}{
{
name: "nil-CredentialLibrary",
wantErr: errors.InvalidParameter,
},
{
name: "nil-embedded-CredentialLibrary",
in: &CredentialLibrary{},
wantErr: errors.InvalidParameter,
},
{
name: "invalid-no-store-id",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{},
},
wantErr: errors.InvalidParameter,
},
{
name: "invalid-public-id-set",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
PublicId: "abcd_OOOOOOOOOO",
},
},
wantErr: errors.InvalidParameter,
},
{
name: "invalid-no-vault-path",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
},
},
wantErr: errors.InvalidParameter,
},
{
name: "valid-no-options",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
},
{
name: "valid-with-name",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
Name: "test-name-repo",
VaultPath: "/some/path",
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
Name: "test-name-repo",
VaultPath: "/some/path",
},
},
},
{
name: "valid-with-description",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
Description: "test-description-repo",
VaultPath: "/some/path",
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
Description: "test-description-repo",
VaultPath: "/some/path",
},
},
},
{
name: "valid-POST-method",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "POST",
Description: "test-description-repo",
VaultPath: "/some/path",
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "POST",
Description: "test-description-repo",
VaultPath: "/some/path",
},
},
},
{
name: "valid-POST-http-body",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "POST",
Description: "test-description-repo",
VaultPath: "/some/path",
HttpRequestBody: []byte(`{"common_name":"boundary.com"}`),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "POST",
Description: "test-description-repo",
VaultPath: "/some/path",
HttpRequestBody: []byte(`{"common_name":"boundary.com"}`),
},
},
},
{
name: "valid-username-password-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "unknown-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: unknownMapper(1),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "invalid-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "valid-username-password-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-username-password-credential-type-with-password-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-username-password-credential-type-with-username-and-password-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-password-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
},
{
name: "unknown-password-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: unknownMapper(1),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "invalid-password-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "valid-password-credential-type-with-password-override",
in: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "unknown-ssh-private-key-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: unknownMapper(1),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "invalid-ssh-private-key-mapping-override-type",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("test")),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "valid-ssh-private-key-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-passphrase-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("passtest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("passtest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-keyt-credential-type-with-all-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("pktest"),
WithOverridePrivateKeyPassphraseAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("pktest"),
WithOverridePrivateKeyPassphraseAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
}
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(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
got, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), tt.in, tt.opts...)
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)
assert.Empty(tt.in.PublicId)
require.NotNil(got)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got.GetPublicId())
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 *UsernamePasswordOverride:
g, ok := got.MappingOverride.(*UsernamePasswordOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
// verify it was persisted in the database
override := allocUsernamePasswordOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []any{got.GetPublicId()}))
case *PasswordOverride:
g, ok := got.MappingOverride.(*PasswordOverride)
require.True(ok)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
// verify it was persisted in the database
override := allocPasswordOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []any{got.GetPublicId()}))
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
assert.Equal(w.PrivateKeyPassphraseAttribute, g.PrivateKeyPassphraseAttribute)
// verify it was persisted in the database
override := allocSshPrivateKeyOverride()
assert.NoError(rw.LookupWhere(ctx, &override, "library_id = ?", []any{got.GetPublicId()}))
default:
assert.Fail("Unknown mapping override")
}
}
})
}
t.Run("invalid-duplicate-names", 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(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
in := &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
}
got, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in)
require.NoError(err)
require.NotNil(got)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got.GetPublicId())
assert.NotSame(in, got)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
assert.Equal(got.CreateTime, got.UpdateTime)
got2, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in)
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err: %q got: %q", errors.NotUnique, err)
assert.Nil(got2)
})
t.Run("valid-duplicate-names-diff-stores", 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(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
csA, csB := css[0], css[1]
in := &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
}
in2 := in.clone()
in.StoreId = csA.GetPublicId()
got, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in)
require.NoError(err)
require.NotNil(got)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got.GetPublicId())
assert.NotSame(in, got)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
assert.Equal(got.CreateTime, got.UpdateTime)
in2.StoreId = csB.GetPublicId()
got2, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in2)
require.NoError(err)
require.NotNil(got2)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got2.GetPublicId())
assert.NotSame(in2, got2)
assert.Equal(in2.Name, got2.Name)
assert.Equal(in2.Description, got2.Description)
assert.Equal(got2.CreateTime, got2.UpdateTime)
})
}
func TestRepository_UpdateCredentialLibrary(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
changeHttpRequestBody := func(b []byte) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.HttpRequestBody = b
return l
}
}
changeHttpMethod := func(m Method) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.HttpMethod = string(m)
return l
}
}
makeHttpMethodEmptyString := func() func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.HttpMethod = ""
return l
}
}
changeVaultPath := func(p string) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.VaultPath = p
return l
}
}
changeName := func(n string) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.Name = n
return l
}
}
changeDescription := func(d string) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.Description = d
return l
}
}
changeCredentialType := func(t globals.CredentialType) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.CredentialLibrary.CredentialType = string(t)
return l
}
}
changeMappingOverride := func(m MappingOverride) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.MappingOverride = m
return l
}
}
makeNil := func() func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
return nil
}
}
makeEmbeddedNil := func() func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
return &CredentialLibrary{}
}
}
deletePublicId := func() func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.PublicId = ""
return l
}
}
nonExistentPublicId := func() func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
l.PublicId = "abcd_OOOOOOOOOO"
return l
}
}
combine := func(fns ...func(l *CredentialLibrary) *CredentialLibrary) func(*CredentialLibrary) *CredentialLibrary {
return func(l *CredentialLibrary) *CredentialLibrary {
for _, fn := range fns {
l = fn(l)
}
return l
}
}
tests := []struct {
name string
orig *CredentialLibrary
chgFn func(*CredentialLibrary) *CredentialLibrary
masks []string
want *CredentialLibrary
wantCount int
wantErr errors.Code
}{
{
name: "nil-credential-library",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: makeNil(),
masks: []string{nameField, descriptionField},
wantErr: errors.InvalidParameter,
},
{
name: "nil-embedded-credential-library",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: makeEmbeddedNil(),
masks: []string{nameField, descriptionField},
wantErr: errors.InvalidParameter,
},
{
name: "no-public-id",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: deletePublicId(),
masks: []string{nameField, descriptionField},
wantErr: errors.InvalidPublicId,
},
{
name: "updating-non-existent-credential-library",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")),
masks: []string{nameField},
wantErr: errors.RecordNotFound,
},
{
name: "empty-field-mask",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
wantErr: errors.EmptyFieldMask,
},
{
name: "read-only-fields-in-field-mask",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"PublicId", "CreateTime", "UpdateTime", "StoreId"},
wantErr: errors.InvalidFieldMask,
},
{
name: "unknown-field-in-field-mask",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"Bilbo"},
wantErr: errors.InvalidFieldMask,
},
{
name: "change-name",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{nameField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-update-name-repo",
},
},
wantCount: 1,
},
{
name: "change-description",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Description: "test-description-repo",
},
},
chgFn: changeDescription("test-update-description-repo"),
masks: []string{descriptionField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "change-name-and-description",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-description-repo",
},
},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")),
masks: []string{nameField, descriptionField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-update-name-repo",
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "delete-name",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{nameField},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Description: "test-description-repo",
},
},
wantCount: 1,
},
{
name: "delete-description",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{descriptionField},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
wantCount: 1,
},
{
name: "do-not-delete-name",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{descriptionField},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "do-not-delete-description",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{nameField},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-update-name-repo",
Description: "test-description-repo",
},
},
wantCount: 1,
},
{
name: "change-vault-path",
orig: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("orig-username")),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/old/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeVaultPath("/new/path"),
masks: []string{vaultPathField},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(WithOverrideUsernameAttribute("orig-username")),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/new/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "delete-vault-path",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: changeVaultPath(""),
masks: []string{vaultPathField},
wantErr: errors.NotNull,
},
{
name: "change-http-method",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: changeHttpMethod(MethodPost),
masks: []string{httpMethodField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
},
},
wantCount: 1,
},
{
name: "delete-http-method",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
},
},
chgFn: makeHttpMethodEmptyString(),
masks: []string{httpMethodField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
wantCount: 1,
},
{
name: "add-http-request-body",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
},
},
chgFn: changeHttpRequestBody([]byte("new request body")),
masks: []string{httpRequestBodyField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("new request body"),
},
},
wantCount: 1,
},
{
name: "delete-http-request-body",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("request body"),
},
},
chgFn: changeHttpRequestBody(nil),
masks: []string{httpRequestBodyField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
},
},
wantCount: 1,
},
{
name: "change-http-request-body",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("old request body"),
},
},
chgFn: changeHttpRequestBody([]byte("new request body")),
masks: []string{httpRequestBodyField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("new request body"),
},
},
wantCount: 1,
},
{
name: "change-method-to-GET-leave-request-body",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("old request body"),
},
},
chgFn: changeHttpMethod(MethodGet),
masks: []string{httpMethodField},
wantErr: errors.CheckConstraint,
},
{
name: "change-method-to-POST-add-request-body",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
chgFn: combine(changeHttpRequestBody([]byte("new request body")), changeHttpMethod(MethodPost)),
masks: []string{httpRequestBodyField, httpMethodField},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "POST",
VaultPath: "/some/path",
HttpRequestBody: []byte("new request body"),
},
},
wantCount: 1,
},
{
name: "read-only-credential-type-in-field-mask",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeCredentialType(globals.UnspecifiedCredentialType),
masks: []string{"PublicId", "CreateTime", "UpdateTime", "StoreId", "CredentialType"},
wantErr: errors.InvalidFieldMask,
},
{
name: "username-password-attributes-change-username-attribute",
orig: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "username-password-attributes-change-password-attribute",
orig: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewUsernamePasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "username-password-attributes-change-username-and-password-attributes",
orig: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePasswordAttribute("changed-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "no-mapping-override-change-username-and-password-attributes",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePasswordAttribute("changed-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "username-password-attributes-delete-mapping-override",
orig: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
chgFn: changeMappingOverride(nil),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "set-mapping-override-on-unspecified-credential-type",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
},
chgFn: changeMappingOverride(
NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
wantErr: errors.VaultInvalidMappingOverride,
},
{
name: "password-attribute-change-password-attribute",
orig: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewPasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "no-mapping-override-change-password-attributes",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
chgFn: changeMappingOverride(
NewPasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("changed-password"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "password-attributes-delete-mapping-override",
orig: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("orig-password"),
WithOverridePrivateKeyAttribute("orig-private-key"),
WithOverridePrivateKeyPassphraseAttribute("orig-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
chgFn: changeMappingOverride(nil),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-password-repo",
CredentialType: string(globals.PasswordCredentialType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-username-attribute",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-private-key-attribute",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("orig-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("changed-private-key"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("changed-private-key"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-passphrase-attribute",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("orig-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-change-all-attributes",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePrivateKeyAttribute("orig-private-key"),
WithOverridePrivateKeyPassphraseAttribute("orig-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
{
name: "no-mapping-override-change-all-attributes",
orig: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(
NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("changed-username"),
WithOverridePrivateKeyAttribute("changed-private-key"),
WithOverridePrivateKeyPassphraseAttribute("changed-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
{
name: "ssh-private-key-attributes-delete-mapping-override",
orig: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePrivateKeyAttribute("orig-private-key"),
WithOverridePrivateKeyPassphraseAttribute("orig-passphrase"),
),
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
chgFn: changeMappingOverride(nil),
masks: []string{"MappingOverride"},
want: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-ssh-private-key-repo",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantCount: 1,
},
}
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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
tt.orig.StoreId = cs.GetPublicId()
orig, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), tt.orig)
assert.NoError(err)
require.NotNil(orig)
if tt.chgFn != nil {
orig = tt.chgFn(orig)
}
got, gotCount, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), orig, 1, tt.masks)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Equal(tt.wantCount, gotCount, "row count")
assert.Nil(got)
return
}
require.NoError(err)
assert.Empty(tt.orig.PublicId)
require.NotNil(got)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got.GetPublicId())
assert.Equal(tt.wantCount, gotCount, "row count")
assert.NotSame(tt.orig, got)
assert.Equal(tt.orig.StoreId, got.StoreId)
underlyingDB, err := conn.SqlDB(ctx)
require.NoError(err)
dbassert := dbassert.New(t, underlyingDB)
switch tt.want.Name {
case "":
dbassert.IsNull(got, "name")
default:
assert.Equal(tt.want.Name, got.Name)
}
switch tt.want.Description {
case "":
dbassert.IsNull(got, "description")
default:
assert.Equal(tt.want.Description, got.Description)
}
if tt.wantCount > 0 {
assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
}
switch w := tt.want.MappingOverride.(type) {
case nil:
assert.Nil(got.MappingOverride)
case *UsernamePasswordOverride:
g, ok := got.MappingOverride.(*UsernamePasswordOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *PasswordOverride:
g, ok := got.MappingOverride.(*PasswordOverride)
require.True(ok)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
assert.Equal(w.PrivateKeyPassphraseAttribute, g.PrivateKeyPassphraseAttribute)
default:
assert.Fail("Unknown mapping override")
}
})
}
t.Run("invalid-duplicate-names", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
name := "test-dup-name"
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
libs := TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), globals.UnspecifiedCredentialType, 2)
lA, lB := libs[0], libs[1]
lA.Name = name
got1, gotCount1, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), lA, 1, []string{"name"})
assert.NoError(err)
require.NotNil(got1)
assert.Equal(name, got1.Name)
assert.Equal(1, gotCount1, "row count")
assert.NoError(db.TestVerifyOplog(t, rw, lA.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
lB.Name = name
got2, gotCount2, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), lB, 1, []string{"name"})
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
assert.Nil(got2)
assert.Equal(db.NoRowsAffected, gotCount2, "row count")
err = db.TestVerifyOplog(t, rw, lB.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
assert.True(errors.IsNotFoundError(err))
})
t.Run("valid-duplicate-names-diff-CredentialStores", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
csA, csB := css[0], css[1]
in := &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
}
in2 := in.clone()
in.StoreId = csA.GetPublicId()
got, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in)
assert.NoError(err)
require.NotNil(got)
assertPublicId(t, globals.VaultCredentialLibraryPrefix, got.GetPublicId())
assert.NotSame(in, got)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
in2.StoreId = csB.GetPublicId()
in2.Name = "first-name"
got2, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in2)
assert.NoError(err)
require.NotNil(got2)
got2.Name = got.Name
got3, gotCount3, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), got2, 1, []string{"name"})
assert.NoError(err)
require.NotNil(got3)
assert.NotSame(got2, got3)
assert.Equal(got.Name, got3.Name)
assert.Equal(got2.Description, got3.Description)
assert.Equal(1, gotCount3, "row count")
assert.NoError(db.TestVerifyOplog(t, rw, got2.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
})
t.Run("valid-update-with-expired-store-token", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)
cs := css[0]
in := &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
},
}
in.StoreId = cs.GetPublicId()
got, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), in)
assert.NoError(err)
require.NotNil(got)
// Expire the credential store Vault token
rows, err := rw.Exec(context.Background(),
"update credential_vault_token set status = ? where token_hmac = ?",
[]any{ExpiredToken, cs.Token().TokenHmac})
require.NoError(err)
require.Equal(1, rows)
got.Name = "new-name"
updated, gotCount, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), got, 1, []string{"name"})
assert.NoError(err)
require.NotNil(updated)
assert.Equal("new-name", updated.Name)
assert.Equal(1, gotCount)
})
t.Run("change-project-id", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
csA, csB := css[0], css[1]
lA := TestCredentialLibraries(t, conn, wrapper, csA.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
lB := TestCredentialLibraries(t, conn, wrapper, csB.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
assert.NotEqual(lA.StoreId, lB.StoreId)
orig := lA.clone()
lA.StoreId = lB.StoreId
assert.Equal(lA.StoreId, lB.StoreId)
got1, gotCount1, err := repo.UpdateCredentialLibrary(ctx, prj.GetPublicId(), lA, 1, []string{"name"})
assert.NoError(err)
require.NotNil(got1)
assert.Equal(orig.StoreId, got1.StoreId)
assert.Equal(1, gotCount1, "row count")
assert.NoError(db.TestVerifyOplog(t, rw, lA.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
})
}
func TestRepository_LookupCredentialLibrary(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
{
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
cs := css[0]
csWithExpiredToken := css[1]
rows, err := rw.Exec(context.Background(),
"update credential_vault_token set status = ? where token_hmac = ?",
[]any{ExpiredToken, csWithExpiredToken.Token().TokenHmac})
require.NoError(t, err)
require.Equal(t, 1, rows)
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-with-expired-cred-store-token",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: csWithExpiredToken.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
},
},
},
{
name: "valid-username-password-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-username-password-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-username-password-credential-type-with-password-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-username-password-credential-type-with-username-and-password-override",
in: &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
},
},
{
name: "valid-password-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
},
{
name: "valid-password-credential-type-with-password-override",
in: &CredentialLibrary{
MappingOverride: NewPasswordOverride(
WithOverridePasswordAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.PasswordCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type",
in: &CredentialLibrary{
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-username-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-private-key-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-passphrase-override",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverridePrivateKeyPassphraseAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
{
name: "valid-ssh-private-key-credential-type-with-all-overrides",
in: &CredentialLibrary{
MappingOverride: NewSshPrivateKeyOverride(
WithOverrideUsernameAttribute("utest"),
WithOverridePrivateKeyAttribute("pktest"),
WithOverridePrivateKeyPassphraseAttribute("ptest"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
},
}
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(ctx, 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 *UsernamePasswordOverride:
g, ok := got.MappingOverride.(*UsernamePasswordOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *PasswordOverride:
g, ok := got.MappingOverride.(*PasswordOverride)
require.True(ok)
assert.Equal(w.PasswordAttribute, g.PasswordAttribute)
case *SshPrivateKeyOverride:
g, ok := got.MappingOverride.(*SshPrivateKeyOverride)
require.True(ok)
assert.Equal(w.UsernameAttribute, g.UsernameAttribute)
assert.Equal(w.PrivateKeyAttribute, g.PrivateKeyAttribute)
assert.Equal(w.PrivateKeyPassphraseAttribute, g.PrivateKeyPassphraseAttribute)
default:
assert.Fail("Unknown mapping override")
}
}
})
}
}
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(ctx, 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)
})
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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
badId, err := newCredentialLibraryId(ctx)
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) {
t.Parallel()
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
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(), globals.UnspecifiedCredentialType, 1)[0]
badId, err := newCredentialLibraryId(ctx)
require.NoError(t, err)
require.NotNil(t, badId)
tests := []struct {
name string
in string
want int
wantErr errors.Code
}{
{
name: "found",
in: l.GetPublicId(),
want: 1,
},
{
name: "not-found",
in: badId,
},
{
name: "empty-public-id",
in: "",
wantErr: errors.InvalidParameter,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.DeleteCredentialLibrary(ctx, prj.GetPublicId(), tt.in)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
return
}
assert.NoError(err)
assert.Equal(tt.want, got, "row count")
})
}
}
t.Run("library-with-mapping-overrides", func(t *testing.T) {
// setup
assert, require := assert.New(t), require.New(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
lib := &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
}
orig, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), lib)
assert.NoError(err)
require.NotNil(orig)
// test
got, err := repo.DeleteCredentialLibrary(ctx, prj.GetPublicId(), orig.GetPublicId())
assert.NoError(err)
assert.Equal(1, got)
})
}
func TestRepository_ListCredentialLibraries(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
t.Run("CredentialStore-with-a-library", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
lib := &CredentialLibrary{
MappingOverride: NewUsernamePasswordOverride(
WithOverrideUsernameAttribute("orig-username"),
WithOverridePasswordAttribute("orig-password"),
),
CredentialLibrary: &store.CredentialLibrary{
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(globals.UsernamePasswordCredentialType),
},
}
orig, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), lib)
assert.NoError(err)
require.NotNil(orig)
// test
got, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId())
assert.NoError(err)
require.Len(got, 1)
// Transaction timestamp should be within ~10 seconds of now
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
require.Empty(cmp.Diff(
orig,
got[0],
cmpopts.IgnoreUnexported(
CredentialLibrary{},
store.CredentialLibrary{},
timestamp.Timestamp{},
timestamppb.Timestamp{},
),
cmpopts.IgnoreFields(
CredentialLibrary{},
"MappingOverride",
),
))
})
t.Run("with-no-credential-store-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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
// test
_, _, err = repo.ListLibraries(ctx, "")
wantErr := errors.InvalidParameter
assert.Truef(errors.Match(errors.T(wantErr), err), "want err: %q got: %q", wantErr, err)
})
t.Run("CredentialStore-with-no-libraries", 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(ctx, rw, rw, kms, sche)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
// test
got, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId())
assert.NoError(err)
assert.Empty(got)
// Transaction timestamp should be within ~10 seconds of now
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
})
}
func TestRepository_ListCredentialLibraries_Limits(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
sche := scheduler.TestScheduler(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
const count = 10
libs := TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), globals.UnspecifiedCredentialType, count)
tests := []struct {
name string
repoOpts []Option
listOpts []credential.Option
wantLen int
}{
{
name: "with-no-limits",
wantLen: count,
},
{
name: "with-repo-limit",
repoOpts: []Option{WithLimit(3)},
wantLen: 3,
},
{
name: "with-list-limit",
listOpts: []credential.Option{credential.WithLimit(3)},
wantLen: 3,
},
{
name: "with-repo-smaller-than-list-limit",
repoOpts: []Option{WithLimit(2)},
listOpts: []credential.Option{credential.WithLimit(6)},
wantLen: 6,
},
{
name: "with-repo-larger-than-list-limit",
repoOpts: []Option{WithLimit(6)},
listOpts: []credential.Option{credential.WithLimit(2)},
wantLen: 2,
},
}
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)
repo, err := NewRepository(ctx, rw, rw, kms, sche, tt.repoOpts...)
assert.NoError(err)
require.NotNil(repo)
got, ttime, err := repo.ListLibraries(ctx, libs[0].StoreId, tt.listOpts...)
require.NoError(err)
assert.Len(got, tt.wantLen)
// Transaction timestamp should be within ~10 seconds of now
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
})
}
}
func TestRepository_ListCredentialLibraries_Pagination(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 3)
for _, cs := range css[:2] { // Leave the third store empty
TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), globals.UnspecifiedCredentialType, 5)
}
repo, err := NewRepository(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
for _, cs := range css[:2] {
page1, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId(), credential.WithLimit(2))
require.NoError(err)
require.Len(page1, 2)
// Transaction timestamp should be within ~10 seconds of now
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
page2, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page1[1]))
require.NoError(err)
require.Len(page2, 2)
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
for _, item := range page1 {
assert.NotEqual(item.GetPublicId(), page2[0].GetPublicId())
assert.NotEqual(item.GetPublicId(), page2[1].GetPublicId())
}
page3, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page2[1]))
require.NoError(err)
require.Len(page3, 1)
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
for _, item := range append(page1, page2...) {
assert.NotEqual(item.GetPublicId(), page3[0].GetPublicId())
}
page4, ttime, err := repo.ListLibraries(ctx, cs.GetPublicId(), credential.WithLimit(2), credential.WithStartPageAfterItem(page3[0]))
require.NoError(err)
require.Empty(page4)
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
}
emptyPage, ttime, err := repo.ListLibraries(ctx, css[2].GetPublicId(), credential.WithLimit(2))
require.NoError(err)
assert.True(time.Now().Before(ttime.Add(10 * time.Second)))
assert.True(time.Now().After(ttime.Add(-10 * time.Second)))
require.Empty(emptyPage)
}
func TestRepository_ListDeletedLibraryIds(t *testing.T) {
t.Parallel()
require := require.New(t)
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
libs := TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), globals.UnspecifiedCredentialType, 2)
repo, err := NewRepository(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
// Expect no entries at the start
deletedIds, ttime, err := repo.ListDeletedLibraryIds(ctx, time.Now().AddDate(-1, 0, 0))
require.NoError(err)
require.Empty(deletedIds)
// Transaction timestamp should be within ~10 seconds of now
assert.True(t, time.Now().Before(ttime.Add(10*time.Second)))
assert.True(t, time.Now().After(ttime.Add(-10*time.Second)))
// Delete a vault library
_, err = repo.DeleteCredentialLibrary(ctx, prj.GetPublicId(), libs[0].GetPublicId())
require.NoError(err)
// Expect a single entry
deletedIds, ttime, err = repo.ListDeletedLibraryIds(ctx, time.Now().AddDate(-1, 0, 0))
require.NoError(err)
require.Equal([]string{libs[0].GetPublicId()}, deletedIds)
assert.True(t, time.Now().Before(ttime.Add(10*time.Second)))
assert.True(t, time.Now().After(ttime.Add(-10*time.Second)))
// Try again with the time set to now, expect no entries
deletedIds, ttime, err = repo.ListDeletedLibraryIds(ctx, time.Now())
require.NoError(err)
require.Empty(deletedIds)
assert.True(t, time.Now().Before(ttime.Add(10*time.Second)))
assert.True(t, time.Now().After(ttime.Add(-10*time.Second)))
}
func TestRepository_EstimatedLibraryCount(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
sqlDb, err := conn.SqlDB(ctx)
require.NoError(err)
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
repo, err := NewRepository(ctx, rw, rw, kms, sche)
require.NoError(err)
require.NotNil(repo)
// Check total entries at start, expect 0
numItems, err := repo.EstimatedLibraryCount(ctx)
require.NoError(err)
assert.Equal(0, numItems)
// Create some libraries
libs := TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), globals.UnspecifiedCredentialType, 2)
// Run analyze to update postgres meta tables
_, err = sqlDb.ExecContext(ctx, "analyze")
require.NoError(err)
numItems, err = repo.EstimatedLibraryCount(ctx)
require.NoError(err)
assert.Equal(2, numItems)
// Delete a library
_, err = repo.DeleteCredentialLibrary(ctx, prj.GetPublicId(), libs[0].GetPublicId())
require.NoError(err)
_, err = sqlDb.ExecContext(ctx, "analyze")
require.NoError(err)
numItems, err = repo.EstimatedLibraryCount(ctx)
require.NoError(err)
assert.Equal(1, numItems)
}