* handlers/targets: always return pagination parameters in list * handlers/authmethods: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/accounts: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/authtokens: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/credentiallibraries: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/credentials: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/credentialstores: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/groups: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/host_catalogs: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/host_sets: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/hosts: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/managed_groups: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/roles: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/scopes: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/sessions: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so. * handlers/users: test for pagination parameters Note that this already always returned the pagination parameters, but now we have a test that it does so.
3066 lines
112 KiB
Go
3066 lines
112 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package credentiallibraries
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/auth/password"
|
|
"github.com/hashicorp/boundary/internal/authtoken"
|
|
"github.com/hashicorp/boundary/internal/credential/vault"
|
|
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
|
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/hashicorp/boundary/internal/kms"
|
|
"github.com/hashicorp/boundary/internal/requests"
|
|
"github.com/hashicorp/boundary/internal/scheduler"
|
|
"github.com/hashicorp/boundary/internal/server"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
|
|
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
)
|
|
|
|
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
|
|
|
|
func vaultCredentialLibraryToProto(credLib *vault.CredentialLibrary, project *iam.Scope) *pb.CredentialLibrary {
|
|
return &pb.CredentialLibrary{
|
|
Id: credLib.GetPublicId(),
|
|
CredentialStoreId: credLib.GetStoreId(),
|
|
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
|
|
CreatedTime: credLib.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: credLib.GetUpdateTime().GetTimestamp(),
|
|
Version: credLib.GetVersion(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(credLib.GetVaultPath()),
|
|
HttpMethod: wrapperspb.String(credLib.GetHttpMethod()),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func sshCredentialLibraryToProto(credLib *vault.SSHCertificateCredentialLibrary, project *iam.Scope) *pb.CredentialLibrary {
|
|
return &pb.CredentialLibrary{
|
|
Id: credLib.GetPublicId(),
|
|
CredentialStoreId: credLib.GetStoreId(),
|
|
CredentialType: credLib.GetCredentialType(),
|
|
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
|
|
CreatedTime: credLib.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: credLib.GetUpdateTime().GetTimestamp(),
|
|
Version: credLib.GetVersion(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyType: wrapperspb.String(credLib.GetKeyType()),
|
|
Path: wrapperspb.String(credLib.GetVaultPath()),
|
|
Username: wrapperspb.String(credLib.GetUsername()),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestList(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prjNoLibs := iam.TestScopes(t, iamRepo)
|
|
storeNoLibs := vault.TestCredentialStores(t, conn, wrapper, prjNoLibs.GetPublicId(), 1)[0]
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
|
|
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
var wantLibraries []*pb.CredentialLibrary
|
|
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
|
wantLibraries = append(wantLibraries, vaultCredentialLibraryToProto(l, prj))
|
|
}
|
|
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
|
|
wantLibraries = append(wantLibraries, sshCredentialLibraryToProto(l, prj))
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.ListCredentialLibrariesRequest
|
|
res *pbs.ListCredentialLibrariesResponse
|
|
anonRes *pbs.ListCredentialLibrariesResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "List Many Libraries",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: store.GetPublicId()},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibraries,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibraries,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
},
|
|
{
|
|
name: "List No Libraries",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeNoLibs.GetPublicId()},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "Filter to One Library",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantLibraries[1].GetId())},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibraries[1:2],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 1,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibraries[1:2],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 1,
|
|
},
|
|
},
|
|
{
|
|
name: "Filter to No Libraries",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: store.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "Filter Bad Format",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: store.GetPublicId(), Filter: `"//id/"=="bad"`},
|
|
err: handlers.InvalidArgumentErrorf("bad format", nil),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
// Test non-anonymous listing
|
|
got, gErr := s.ListCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
|
if tc.err != nil {
|
|
require.Error(t, gErr)
|
|
assert.True(t, errors.Is(gErr, tc.err), "ListCredentialLibrary(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(t, gErr)
|
|
sort.Slice(got.Items, func(i, j int) bool {
|
|
return got.Items[i].GetId() < got.Items[j].GetId()
|
|
})
|
|
want := proto.Clone(tc.res).(*pbs.ListCredentialLibrariesResponse)
|
|
sort.Slice(want.Items, func(i, j int) bool {
|
|
return want.Items[i].GetId() < want.Items[j].GetId()
|
|
})
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
t,
|
|
cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Test anonymous listing
|
|
got, gErr = s.ListCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
|
|
require.NoError(t, gErr)
|
|
assert.Len(t, got.Items, len(tc.anonRes.Items))
|
|
for _, item := range got.GetItems() {
|
|
require.Nil(t, item.CreatedTime)
|
|
require.Nil(t, item.UpdatedTime)
|
|
require.Zero(t, item.Version)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestList_Attributes(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
|
|
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
|
|
storeGeneric, storeSSHCertificate := ts[0], ts[1]
|
|
var wantLibrariesGeneric []*pb.CredentialLibrary
|
|
var wantLibrariesSSHCertificate []*pb.CredentialLibrary
|
|
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, storeGeneric.GetPublicId(), 5) {
|
|
wantLibrariesGeneric = append(wantLibrariesGeneric, vaultCredentialLibraryToProto(l, prj))
|
|
}
|
|
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, storeSSHCertificate.GetPublicId(), 10) {
|
|
wantLibrariesSSHCertificate = append(wantLibrariesSSHCertificate, sshCredentialLibraryToProto(l, prj))
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.ListCredentialLibrariesRequest
|
|
res *pbs.ListCredentialLibrariesResponse
|
|
anonRes *pbs.ListCredentialLibrariesResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "Filter on Attribute Generic Library",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeGeneric.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/path"==%q`, wantLibrariesGeneric[2].GetVaultGenericCredentialLibraryAttributes().GetPath().Value)},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibrariesGeneric[2:3],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 1,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
}, // anonymous user does not have access to attributes
|
|
},
|
|
{
|
|
name: "Filter on Attribute SSH Certificate Library",
|
|
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeSSHCertificate.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/path"==%q`, wantLibrariesSSHCertificate[2].GetVaultSshCertificateCredentialLibraryAttributes().GetPath().Value)},
|
|
res: &pbs.ListCredentialLibrariesResponse{
|
|
Items: wantLibrariesSSHCertificate[2:3],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 1,
|
|
},
|
|
anonRes: &pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
}, // anonymous user does not have access to attributes
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
// Test non-anonymous listing
|
|
got, gErr := s.ListCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
|
if tc.err != nil {
|
|
require.Error(t, gErr)
|
|
assert.True(t, errors.Is(gErr, tc.err), "ListCredentialLibrary(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(t, gErr)
|
|
sort.Slice(got.Items, func(i, j int) bool {
|
|
return got.Items[i].GetId() < got.Items[j].GetId()
|
|
})
|
|
want := proto.Clone(tc.res).(*pbs.ListCredentialLibrariesResponse)
|
|
sort.Slice(want.Items, func(i, j int) bool {
|
|
return want.Items[i].GetId() < want.Items[j].GetId()
|
|
})
|
|
assert.Empty(t, cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
))
|
|
|
|
// Test anonymous listing
|
|
got, gErr = s.ListCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
|
|
require.NoError(t, gErr)
|
|
assert.Empty(
|
|
t,
|
|
cmp.Diff(
|
|
got,
|
|
tc.anonRes,
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreate(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
defaultCL := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
|
defaultCreated := defaultCL.GetCreateTime().GetTimestamp()
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.CreateCredentialLibraryRequest
|
|
res *pbs.CreateCredentialLibraryResponse
|
|
idPrefix string
|
|
err error
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "missing vault path",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Can't specify Id",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Id: globals.VaultCredentialLibraryPrefix + "_notallowed",
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Created Time",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CreatedTime: timestamppb.Now(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Update Time",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
UpdatedTime: timestamppb.Now(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Type and parent id must match",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: "static",
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "parent id must be included",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Cannot specify a http request body when http method is get",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpRequestBody: wrapperspb.String("foo"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid Method",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("PATCH"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Request Body With GET Method",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpRequestBody: wrapperspb.String("foo"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid credential type",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialType: "fake-type",
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid username_password mapping",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
"invalid": "invalid-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Using POST method",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("post"),
|
|
HttpRequestBody: wrapperspb.String("foo"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("POST"),
|
|
HttpRequestBody: wrapperspb.String("foo"),
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Name: &wrapperspb.StringValue{Value: "name"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Name: &wrapperspb.StringValue{Value: "name"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary username_password type",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary username_password type with username mapping",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary username_password type with username/password mapping",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
passwordAttribute: "pass-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
passwordAttribute: "pass-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary ssh_private_key type",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary ssh_private_key type with mapping",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
privateKeyAttribute: "pk-test",
|
|
pkPassphraseAttribute: "pass-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user-test",
|
|
privateKeyAttribute: "pk-test",
|
|
pkPassphraseAttribute: "pass-test",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid vault CredentialLibrary with the 'vault' subtype",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(err)
|
|
got, gErr := s.CreateCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
|
if tc.wantErr || tc.err != nil {
|
|
require.Error(gErr)
|
|
if tc.err != nil {
|
|
assert.True(errors.Is(gErr, tc.err), "CreateCredentialLibrary(...) got error %v, wanted %v", gErr, tc.err)
|
|
}
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
if tc.res == nil {
|
|
require.Nil(got)
|
|
}
|
|
if got != nil {
|
|
assert.Contains(got.GetUri(), tc.res.Uri)
|
|
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
|
|
gotCreateTime := got.GetItem().GetCreatedTime()
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
|
|
|
// Verify it is a credential store created after the test setup's default credential store
|
|
assert.True(gotCreateTime.AsTime().After(defaultCreated.AsTime()), "New credential store should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, defaultCreated)
|
|
assert.True(gotUpdateTime.AsTime().After(defaultCreated.AsTime()), "New credential store should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, defaultCreated)
|
|
|
|
// Clear all values which are hard to compare against.
|
|
got.Uri, tc.res.Uri = "", ""
|
|
got.Item.Id, tc.res.Item.Id = "", ""
|
|
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
protocmp.SortRepeatedFields(got),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "CreateCredentialLibrary(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
|
|
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
unspecifiedLib := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
repo, err := repoFn()
|
|
require.NoError(t, err)
|
|
|
|
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("user"),
|
|
vault.WithOverridePasswordAttribute("pass"),
|
|
)))
|
|
require.NoError(t, err)
|
|
userPassLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib)
|
|
require.NoError(t, err)
|
|
|
|
lib1, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path/ssh",
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("user"),
|
|
vault.WithOverridePrivateKeyAttribute("pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("pass"),
|
|
)))
|
|
require.NoError(t, err)
|
|
sshPkLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib1)
|
|
require.NoError(t, err)
|
|
|
|
lib2, err := vault.NewSSHCertificateCredentialLibrary(store.GetPublicId(), "/ssh/sign/foo", "username")
|
|
require.NoError(t, err)
|
|
sshCertLib, err := repo.CreateSSHCertificateCredentialLibrary(context.Background(), prj.GetPublicId(), lib2)
|
|
require.NoError(t, err)
|
|
|
|
cases := []struct {
|
|
name string
|
|
id string
|
|
res *pbs.GetCredentialLibraryResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "success",
|
|
id: unspecifiedLib.GetPublicId(),
|
|
res: &pbs.GetCredentialLibraryResponse{
|
|
Item: &pb.CredentialLibrary{
|
|
Id: unspecifiedLib.GetPublicId(),
|
|
CredentialStoreId: unspecifiedLib.GetStoreId(),
|
|
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CreatedTime: unspecifiedLib.CreateTime.GetTimestamp(),
|
|
UpdatedTime: unspecifiedLib.UpdateTime.GetTimestamp(),
|
|
Version: 1,
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(unspecifiedLib.GetVaultPath()),
|
|
HttpMethod: wrapperspb.String(unspecifiedLib.GetHttpMethod()),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "success-UsernamePassword",
|
|
id: userPassLib.GetPublicId(),
|
|
res: &pbs.GetCredentialLibraryResponse{
|
|
Item: &pb.CredentialLibrary{
|
|
Id: userPassLib.GetPublicId(),
|
|
CredentialStoreId: userPassLib.GetStoreId(),
|
|
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CreatedTime: userPassLib.CreateTime.GetTimestamp(),
|
|
UpdatedTime: userPassLib.UpdateTime.GetTimestamp(),
|
|
Version: 1,
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(userPassLib.GetVaultPath()),
|
|
HttpMethod: wrapperspb.String(userPassLib.GetHttpMethod()),
|
|
},
|
|
},
|
|
CredentialType: "username_password",
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user",
|
|
passwordAttribute: "pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "success-ssh-private-key",
|
|
id: sshPkLib.GetPublicId(),
|
|
res: &pbs.GetCredentialLibraryResponse{
|
|
Item: &pb.CredentialLibrary{
|
|
Id: sshPkLib.GetPublicId(),
|
|
CredentialStoreId: sshPkLib.GetStoreId(),
|
|
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CreatedTime: sshPkLib.CreateTime.GetTimestamp(),
|
|
UpdatedTime: sshPkLib.UpdateTime.GetTimestamp(),
|
|
Version: 1,
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(sshPkLib.GetVaultPath()),
|
|
HttpMethod: wrapperspb.String(sshPkLib.GetHttpMethod()),
|
|
},
|
|
},
|
|
CredentialType: "ssh_private_key",
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "user",
|
|
privateKeyAttribute: "pk",
|
|
pkPassphraseAttribute: "pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "success-ssh-certificate",
|
|
id: sshCertLib.GetPublicId(),
|
|
res: &pbs.GetCredentialLibraryResponse{
|
|
Item: &pb.CredentialLibrary{
|
|
Id: sshCertLib.GetPublicId(),
|
|
CredentialStoreId: sshCertLib.GetStoreId(),
|
|
CredentialType: sshCertLib.GetCredentialType(),
|
|
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CreatedTime: sshCertLib.CreateTime.GetTimestamp(),
|
|
UpdatedTime: sshCertLib.UpdateTime.GetTimestamp(),
|
|
Version: 1,
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyType: wrapperspb.String(sshCertLib.GetKeyType()),
|
|
Path: wrapperspb.String(sshCertLib.GetVaultPath()),
|
|
Username: wrapperspb.String(sshCertLib.GetUsername()),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "not found error",
|
|
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
|
err: handlers.NotFoundError(),
|
|
},
|
|
{
|
|
name: "bad prefix",
|
|
id: fmt.Sprintf("%s_1234567890", globals.StaticHostPrefix),
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.GetCredentialLibraryRequest{Id: tc.id}
|
|
// Test non-anonymous get
|
|
got, gErr := s.GetCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), req)
|
|
if tc.err != nil {
|
|
require.Error(t, gErr)
|
|
assert.True(t, errors.Is(gErr, tc.err))
|
|
return
|
|
}
|
|
require.NoError(t, gErr)
|
|
assert.Empty(t, cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
))
|
|
|
|
// Test anonymous get
|
|
got, gErr = s.GetCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), req)
|
|
require.NoError(t, gErr)
|
|
require.Nil(t, got.Item.CreatedTime)
|
|
require.Nil(t, got.Item.UpdatedTime)
|
|
require.Zero(t, got.Item.Version)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
|
|
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
vl := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
|
vl2 := vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
cases := []struct {
|
|
name string
|
|
id string
|
|
err error
|
|
res *pbs.DeleteCredentialLibraryResponse
|
|
}{
|
|
{
|
|
name: "success",
|
|
id: vl.GetPublicId(),
|
|
},
|
|
{
|
|
name: "success-ssh-cert",
|
|
id: vl2.GetPublicId(),
|
|
},
|
|
{
|
|
name: "not found error",
|
|
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
|
|
err: handlers.NotFoundError(),
|
|
},
|
|
{
|
|
name: "bad prefix",
|
|
id: fmt.Sprintf("%s_1234567890", globals.StaticHostPrefix),
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, gErr := s.DeleteCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialLibraryRequest{Id: tc.id})
|
|
assert.EqualValuesf(t, tc.res, got, "DeleteCredentialLibrary got response %q, wanted %q", got, tc.res)
|
|
if tc.err != nil {
|
|
require.Error(t, gErr)
|
|
assert.True(t, errors.Is(gErr, tc.err))
|
|
return
|
|
}
|
|
require.NoError(t, gErr)
|
|
g, err := s.GetCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.GetCredentialLibraryRequest{Id: tc.id})
|
|
assert.Nil(t, g)
|
|
assert.True(t, errors.Is(err, handlers.NotFoundError()))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
testCtx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(testCtx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
|
|
|
s, err := NewService(testCtx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
cs := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
|
|
store, diffStore := cs[0], cs[1]
|
|
|
|
freshLibrary := func(opt ...vault.Option) (*vault.CredentialLibrary, func()) {
|
|
repo, err := repoFn()
|
|
require.NoError(t, err)
|
|
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path", opt...)
|
|
require.NoError(t, err)
|
|
|
|
vl, err := repo.CreateCredentialLibrary(ctx, prj.GetPublicId(), lib)
|
|
require.NoError(t, err)
|
|
clean := func() {
|
|
_, err := s.DeleteCredentialLibrary(ctx, &pbs.DeleteCredentialLibraryRequest{Id: vl.GetPublicId()})
|
|
require.NoError(t, err)
|
|
}
|
|
return vl, clean
|
|
}
|
|
|
|
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
|
|
return &fieldmaskpb.FieldMask{Paths: paths}
|
|
}
|
|
|
|
v := vault.NewTestVaultServer(t, vault.WithTestVaultTLS(vault.TestClientTLS))
|
|
_, token := v.CreateToken(t)
|
|
_ = token
|
|
|
|
usernameAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, usernameAttribute)
|
|
passwordAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, passwordAttribute)
|
|
privateKeyAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, privateKeyAttribute)
|
|
passphraseAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, pkPassphraseAttribute)
|
|
|
|
successCases := []struct {
|
|
name string
|
|
opts []vault.Option
|
|
req *pbs.UpdateCredentialLibraryRequest
|
|
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
|
|
}{
|
|
{
|
|
name: "name and description",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("name", "description"),
|
|
Item: &pb.CredentialLibrary{
|
|
Name: wrapperspb.String("basic"),
|
|
Description: wrapperspb.String("basic"),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.Name = wrapperspb.String("basic")
|
|
out.Description = wrapperspb.String("basic")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update method",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(httpMethodField),
|
|
Item: &pb.CredentialLibrary{
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
HttpMethod: wrapperspb.String("pOsT"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultGenericCredentialLibraryAttributes().Path = wrapperspb.String("vault/path")
|
|
out.GetVaultGenericCredentialLibraryAttributes().HttpMethod = wrapperspb.String("POST")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update request body and method",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(httpRequestBodyField, httpMethodField),
|
|
Item: &pb.CredentialLibrary{
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
HttpMethod: wrapperspb.String("pOsT"),
|
|
HttpRequestBody: wrapperspb.String("body"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultGenericCredentialLibraryAttributes().Path = wrapperspb.String("vault/path")
|
|
out.GetVaultGenericCredentialLibraryAttributes().HttpMethod = wrapperspb.String("POST")
|
|
out.GetVaultGenericCredentialLibraryAttributes().HttpRequestBody = wrapperspb.String("body")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update path",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(vaultPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultGenericCredentialLibraryAttributes().Path = wrapperspb.String("something")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "username-password-attributes-change-username-attribute",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePasswordAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "changed-user",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "username-password-attributes-change-password-attribute",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePasswordAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passwordAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
passwordAttribute: "changed-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[passwordAttribute] = structpb.NewStringValue("changed-pass")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "username-password-attributes-change-username-and-password-attributes",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePasswordAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "changed-user",
|
|
passwordAttribute: "changed-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
|
|
out.CredentialMappingOverrides.Fields[passwordAttribute] = structpb.NewStringValue("changed-pass")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "no-mapping-override-change-username-and-password-attributes",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "new-user",
|
|
passwordAttribute: "new-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
v := map[string]any{
|
|
usernameAttribute: "new-user",
|
|
passwordAttribute: "new-pass",
|
|
}
|
|
var err error
|
|
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "username-password-attributes-delete-mapping-override",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePasswordAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(credentialMappingPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: nil,
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "no-mapping-override-delete-mapping-override",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(credentialMappingPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: nil,
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "username-password-attributes-delete-mapping-override-field-specific",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
vault.WithMappingOverride(
|
|
vault.NewUsernamePasswordOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePasswordAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: nil,
|
|
passwordAttribute: nil,
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "no-mapping-override-delete-mapping-override-field-specific",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("username_password"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: nil,
|
|
passwordAttribute: nil,
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-change-username-attribute",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "changed-user",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-change-private-key-attribute",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(privateKeyAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
privateKeyAttribute: "changed-pk",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[privateKeyAttribute] = structpb.NewStringValue("changed-pk")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-change-passphrase-attribute",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(passphraseAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
pkPassphraseAttribute: "changed-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[pkPassphraseAttribute] = structpb.NewStringValue("changed-pass")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-change-all-attributes",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "changed-user",
|
|
privateKeyAttribute: "changed-pk",
|
|
pkPassphraseAttribute: "changed-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides.Fields[usernameAttribute] = structpb.NewStringValue("changed-user")
|
|
out.CredentialMappingOverrides.Fields[privateKeyAttribute] = structpb.NewStringValue("changed-pk")
|
|
out.CredentialMappingOverrides.Fields[pkPassphraseAttribute] = structpb.NewStringValue("changed-pass")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "no-mapping-override-change-all-attributes",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: "new-user",
|
|
privateKeyAttribute: "new-pk",
|
|
pkPassphraseAttribute: "new-pass",
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
v := map[string]any{
|
|
usernameAttribute: "new-user",
|
|
privateKeyAttribute: "new-pk",
|
|
pkPassphraseAttribute: "new-pass",
|
|
}
|
|
var err error
|
|
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-delete-mapping-override",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(credentialMappingPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: nil,
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-no-mapping-override-delete-mapping-override",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(credentialMappingPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: nil,
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-attributes-delete-mapping-override-field-specific",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
vault.WithMappingOverride(
|
|
vault.NewSshPrivateKeyOverride(
|
|
vault.WithOverrideUsernameAttribute("orig-user"),
|
|
vault.WithOverridePrivateKeyAttribute("orig-pk"),
|
|
vault.WithOverridePrivateKeyPassphraseAttribute("orig-pass"),
|
|
)),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField, passphraseAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: nil,
|
|
privateKeyAttribute: nil,
|
|
pkPassphraseAttribute: nil,
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "ssh-private-key-no-mapping-override-delete-mapping-override-field-specific",
|
|
opts: []vault.Option{
|
|
vault.WithCredentialType("ssh_private_key"),
|
|
},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(privateKeyAttrField, usernameAttrField),
|
|
Item: &pb.CredentialLibrary{
|
|
CredentialMappingOverrides: func() *structpb.Struct {
|
|
v := map[string]any{
|
|
usernameAttribute: nil,
|
|
privateKeyAttribute: nil,
|
|
}
|
|
ret, err := structpb.NewStruct(v)
|
|
require.NoError(t, err)
|
|
return ret
|
|
}(),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.CredentialMappingOverrides = nil
|
|
return out
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range successCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
st, cleanup := freshLibrary(tc.opts...)
|
|
defer cleanup()
|
|
|
|
if tc.req.Item.GetVersion() == 0 {
|
|
tc.req.Item.Version = 1
|
|
}
|
|
if tc.req.GetId() == "" {
|
|
tc.req.Id = st.GetPublicId()
|
|
}
|
|
resToChange, err := s.GetCredentialLibrary(ctx, &pbs.GetCredentialLibraryRequest{Id: st.GetPublicId()})
|
|
require.NoError(err)
|
|
want := &pbs.UpdateCredentialLibraryResponse{Item: tc.res(resToChange.GetItem())}
|
|
|
|
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
|
|
require.NoError(gErr)
|
|
require.NotNil(got)
|
|
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
|
created := st.GetCreateTime().GetTimestamp()
|
|
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
|
|
|
want.Item.UpdatedTime = got.Item.UpdatedTime
|
|
|
|
assert.EqualValues(2, got.Item.Version)
|
|
want.Item.Version = 2
|
|
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
))
|
|
})
|
|
}
|
|
|
|
vl, cleanup := freshLibrary()
|
|
defer cleanup()
|
|
|
|
errCases := []struct {
|
|
name string
|
|
path string
|
|
item *pb.CredentialLibrary
|
|
}{
|
|
{
|
|
name: "read only type",
|
|
path: "type",
|
|
item: &pb.CredentialLibrary{Type: "something"},
|
|
},
|
|
{
|
|
name: "read only store_id",
|
|
path: "store_id",
|
|
item: &pb.CredentialLibrary{CredentialStoreId: diffStore.GetPublicId()},
|
|
},
|
|
{
|
|
name: "read only updated_time",
|
|
path: "updated_time",
|
|
item: &pb.CredentialLibrary{UpdatedTime: timestamppb.Now()},
|
|
},
|
|
{
|
|
name: "read only created_time",
|
|
path: "created_time",
|
|
item: &pb.CredentialLibrary{UpdatedTime: timestamppb.Now()},
|
|
},
|
|
{
|
|
name: "read only authorized actions",
|
|
path: "authorized actions",
|
|
item: &pb.CredentialLibrary{AuthorizedActions: append(testAuthorizedActions, "another")},
|
|
},
|
|
{
|
|
name: "read only credential type",
|
|
path: "credential_type",
|
|
item: &pb.CredentialLibrary{CredentialType: string(globals.UsernamePasswordCredentialType)},
|
|
},
|
|
}
|
|
for _, tc := range errCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
Item: tc.item,
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
|
}
|
|
req.Item.Version = vl.Version
|
|
|
|
got, gErr := s.UpdateCredentialLibrary(ctx, req)
|
|
assert.Error(t, gErr)
|
|
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
|
assert.Nil(t, got)
|
|
})
|
|
}
|
|
|
|
t.Run("request body and method interactions", func(t *testing.T) {
|
|
vl, cleanup := freshLibrary()
|
|
defer cleanup()
|
|
require.Equal(t, "GET", vl.GetHttpMethod())
|
|
|
|
// Cannot set request body on GET request
|
|
cl, err := s.UpdateCredentialLibrary(ctx, &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{httpRequestBodyField}},
|
|
Item: &pb.CredentialLibrary{
|
|
Version: vl.GetVersion(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
HttpRequestBody: wrapperspb.String("body"),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
require.Error(t, err)
|
|
require.Nil(t, cl)
|
|
|
|
// Can set POST when there is no request body
|
|
cl, err = s.UpdateCredentialLibrary(ctx, &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{httpMethodField}},
|
|
Item: &pb.CredentialLibrary{
|
|
Version: vl.GetVersion(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
HttpMethod: wrapperspb.String("POST"),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cl)
|
|
require.Equal(t, "POST", cl.GetItem().GetVaultGenericCredentialLibraryAttributes().GetHttpMethod().Value)
|
|
|
|
// Can set request body on POST
|
|
cl, err = s.UpdateCredentialLibrary(ctx, &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{httpRequestBodyField}},
|
|
Item: &pb.CredentialLibrary{
|
|
Version: cl.Item.GetVersion(),
|
|
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
|
|
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
|
|
HttpRequestBody: wrapperspb.String("body"),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cl)
|
|
require.Equal(t, "POST", cl.GetItem().GetVaultGenericCredentialLibraryAttributes().GetHttpMethod().Value)
|
|
require.Equal(t, "body", cl.GetItem().GetVaultGenericCredentialLibraryAttributes().GetHttpRequestBody().Value)
|
|
|
|
// Cannot unset POST method (defaulting to GET) when request body is set
|
|
_, err = s.UpdateCredentialLibrary(ctx, &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{httpMethodField}},
|
|
Item: &pb.CredentialLibrary{
|
|
Version: cl.GetItem().GetVersion(),
|
|
},
|
|
})
|
|
require.Error(t, err)
|
|
|
|
// Can clear request body and method (defaulting to GET) in the same request
|
|
cl, err = s.UpdateCredentialLibrary(ctx, &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{httpMethodField, httpRequestBodyField}},
|
|
Item: &pb.CredentialLibrary{
|
|
Version: cl.GetItem().GetVersion(),
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
require.NotNil(t, cl)
|
|
assert.Equal(t, "GET", cl.GetItem().GetVaultGenericCredentialLibraryAttributes().GetHttpMethod().Value)
|
|
assert.Nil(t, cl.GetItem().GetVaultGenericCredentialLibraryAttributes().GetHttpRequestBody())
|
|
})
|
|
}
|
|
|
|
func TestCreate_SSHCertificateCredentialLibrary(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
defaultCL := vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
|
|
defaultCreated := defaultCL.GetCreateTime().GetTimestamp()
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.CreateCredentialLibraryRequest
|
|
res *pbs.CreateCredentialLibraryResponse
|
|
idPrefix string
|
|
err error
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "missing vault path",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultSshCertificateCredentialLibraryPrefix + "_",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing username",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultSshCertificateCredentialLibraryPrefix + "_",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Can't specify Id",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Id: globals.VaultSshCertificateCredentialLibraryPrefix + "_notallowed",
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Created Time",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
CreatedTime: timestamppb.Now(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Update Time",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
UpdatedTime: timestamppb.Now(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Type and parent id must match",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: "static",
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "parent id must be included",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid credential type",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
CredentialType: "fake-type",
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid key type key bits combination",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("something"),
|
|
Username: wrapperspb.String("username"),
|
|
KeyType: wrapperspb.String(vault.KeyTypeEd25519),
|
|
KeyBits: wrapperspb.UInt32(256),
|
|
},
|
|
},
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Create a valid vault SSHCertificateCredentialLibrary",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Name: &wrapperspb.StringValue{Value: "name1"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultSshCertificateCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultSshCertificateCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Name: &wrapperspb.StringValue{Value: "name1"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
KeyType: wrapperspb.String(vault.KeyTypeEd25519),
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CredentialType: string(globals.SshCertificateCredentialType),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Seting key type ed25119 with key bits nil",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Name: &wrapperspb.StringValue{Value: "name2"},
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
KeyType: wrapperspb.String(vault.KeyTypeEd25519),
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultSshCertificateCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultSshCertificateCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Name: &wrapperspb.StringValue{Value: "name2"},
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
KeyType: wrapperspb.String(vault.KeyTypeEd25519),
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CredentialType: string(globals.SshCertificateCredentialType),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "valid vault SSHCertificateCredentialLibrary with AdditionalValidPrincipals",
|
|
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Name: &wrapperspb.StringValue{Value: "name"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
AdditionalValidPrincipals: []*wrapperspb.StringValue{wrapperspb.String("testprincipal")},
|
|
},
|
|
},
|
|
}},
|
|
idPrefix: globals.VaultSshCertificateCredentialLibraryPrefix + "_",
|
|
res: &pbs.CreateCredentialLibraryResponse{
|
|
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultSshCertificateCredentialLibraryPrefix),
|
|
Item: &pb.CredentialLibrary{
|
|
Id: store.GetPublicId(),
|
|
CredentialStoreId: store.GetPublicId(),
|
|
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
|
Name: &wrapperspb.StringValue{Value: "name"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
|
Version: 1,
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/sign/foo"),
|
|
Username: wrapperspb.String("username"),
|
|
KeyType: wrapperspb.String(vault.KeyTypeEd25519),
|
|
AdditionalValidPrincipals: []*wrapperspb.StringValue{wrapperspb.String("testprincipal")},
|
|
},
|
|
},
|
|
AuthorizedActions: testAuthorizedActions,
|
|
CredentialType: string(globals.SshCertificateCredentialType),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(err)
|
|
got, gErr := s.CreateCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
|
if tc.wantErr || tc.err != nil {
|
|
require.Error(gErr)
|
|
if tc.err != nil {
|
|
assert.True(errors.Is(gErr, tc.err), "CreateCredentialLibrary(...) got error %v, wanted %v", gErr, tc.err)
|
|
}
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
if tc.res == nil {
|
|
require.Nil(got)
|
|
}
|
|
if got != nil {
|
|
assert.Contains(got.GetUri(), tc.res.Uri)
|
|
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
|
|
gotCreateTime := got.GetItem().GetCreatedTime()
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
|
|
|
// Verify it is a credential store created after the test setup's default credential store
|
|
assert.True(gotCreateTime.AsTime().After(defaultCreated.AsTime()), "New credential store should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, defaultCreated)
|
|
assert.True(gotUpdateTime.AsTime().After(defaultCreated.AsTime()), "New credential store should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, defaultCreated)
|
|
|
|
// Clear all values which are hard to compare against.
|
|
got.Uri, tc.res.Uri = "", ""
|
|
got.Item.Id, tc.res.Item.Id = "", ""
|
|
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
protocmp.SortRepeatedFields(got),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "CreateCredentialLibrary(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdate_SSHCertificateCredentialLibrary(t *testing.T) {
|
|
testCtx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(testCtx, rw, rw, kms, sche)
|
|
}
|
|
|
|
_, prj := iam.TestScopes(t, iamRepo)
|
|
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
|
|
|
s, err := NewService(testCtx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(t, err)
|
|
cs := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
|
|
store, diffStore := cs[0], cs[1]
|
|
|
|
freshLibrary := func(opt ...vault.Option) (*vault.SSHCertificateCredentialLibrary, func()) {
|
|
repo, err := repoFn()
|
|
require.NoError(t, err)
|
|
lib, err := vault.NewSSHCertificateCredentialLibrary(store.GetPublicId(), "/ssh/sign/foo", "username", opt...)
|
|
require.NoError(t, err)
|
|
|
|
vl, err := repo.CreateSSHCertificateCredentialLibrary(ctx, prj.GetPublicId(), lib)
|
|
require.NoError(t, err)
|
|
clean := func() {
|
|
_, err := s.DeleteCredentialLibrary(ctx, &pbs.DeleteCredentialLibraryRequest{Id: vl.GetPublicId()})
|
|
require.NoError(t, err)
|
|
}
|
|
return vl, clean
|
|
}
|
|
|
|
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
|
|
return &fieldmaskpb.FieldMask{Paths: paths}
|
|
}
|
|
|
|
v := vault.NewTestVaultServer(t, vault.WithTestVaultTLS(vault.TestClientTLS))
|
|
_, token := v.CreateToken(t)
|
|
_ = token
|
|
|
|
testExtensionBytes, _ := json.Marshal(map[string]string{"permit-pty": ""})
|
|
testCriticalOptionsBytes, _ := json.Marshal(map[string]string{"option-a": "set-a"})
|
|
|
|
successCases := []struct {
|
|
name string
|
|
opts []vault.Option
|
|
req *pbs.UpdateCredentialLibraryRequest
|
|
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
|
|
}{
|
|
{
|
|
name: "name and description",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("name", "description"),
|
|
Item: &pb.CredentialLibrary{
|
|
Name: wrapperspb.String("basic"),
|
|
Description: wrapperspb.String("basic"),
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.Name = wrapperspb.String("basic")
|
|
out.Description = wrapperspb.String("basic")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update username",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(sshCertUsernameField),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Username: wrapperspb.String("changed-username"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Username = wrapperspb.String("changed-username")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update key type and key bits",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("attributes.key_type", "attributes.key_bits"),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyType: wrapperspb.String(vault.KeyTypeRsa),
|
|
KeyBits: wrapperspb.UInt32(2048),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().KeyType = wrapperspb.String("rsa")
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().KeyBits = wrapperspb.UInt32(2048)
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update path",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(vaultPathField),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("/ssh/issue/foo"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Path = wrapperspb.String("/ssh/issue/foo")
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "update other attr fields",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("attributes.ttl", "attributes.key_id", "attributes.critical_options.some", "attributes.extensions.permity-pty"),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyId: wrapperspb.String("id"),
|
|
CriticalOptions: map[string]string{"some": "option"},
|
|
Extensions: map[string]string{"permit-pty": ""},
|
|
Ttl: wrapperspb.String("2m"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Ttl = wrapperspb.String("2m")
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().KeyId = wrapperspb.String("id")
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().CriticalOptions = map[string]string{"some": "option"}
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Extensions = map[string]string{"permit-pty": ""}
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "clear critical options, extensions fields",
|
|
opts: []vault.Option{vault.WithExtensions(string(testExtensionBytes)), vault.WithCriticalOptions(string(testCriticalOptionsBytes))},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("attributes.critical_options", "attributes.extensions.permit-pty"),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
CriticalOptions: nil,
|
|
Extensions: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().CriticalOptions = nil
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Extensions = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "set critical options while clearing extensions",
|
|
opts: []vault.Option{vault.WithExtensions(string(testExtensionBytes)), vault.WithCriticalOptions(string(testCriticalOptionsBytes))},
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("attributes.critical_options.option-b", "attributes.extensions"),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
CriticalOptions: map[string]string{"option-b": "set-b"},
|
|
Extensions: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().CriticalOptions = map[string]string{"option-b": "set-b"}
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Extensions = nil
|
|
return out
|
|
},
|
|
},
|
|
{
|
|
name: "set multiple critical options, extensions",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask("attributes.critical_options.option-a", "attributes.critical_options.option-b", "attributes.critical_options.option-c",
|
|
"attributes.extensions.permit-pty", "attributes.extensions.permit-port-forwarding", "attributes.extensions.permit-X11-forwarding"),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
CriticalOptions: map[string]string{"option-a": "set-a", "option-b": "set-b", "option-c": "set-c"},
|
|
Extensions: map[string]string{"permit-pty": "", "permit-port-forwarding": "", "permit-X11-forwarding": ""},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
|
|
out := proto.Clone(in).(*pb.CredentialLibrary)
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().CriticalOptions = map[string]string{"option-a": "set-a", "option-b": "set-b", "option-c": "set-c"}
|
|
out.GetVaultSshCertificateCredentialLibraryAttributes().Extensions = map[string]string{"permit-pty": "", "permit-port-forwarding": "", "permit-X11-forwarding": ""}
|
|
return out
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range successCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
st, cleanup := freshLibrary(tc.opts...)
|
|
defer cleanup()
|
|
|
|
if tc.req.Item.GetVersion() == 0 {
|
|
tc.req.Item.Version = 1
|
|
}
|
|
if tc.req.GetId() == "" {
|
|
tc.req.Id = st.GetPublicId()
|
|
}
|
|
resToChange, err := s.GetCredentialLibrary(ctx, &pbs.GetCredentialLibraryRequest{Id: st.GetPublicId()})
|
|
require.NoError(err)
|
|
want := &pbs.UpdateCredentialLibraryResponse{Item: tc.res(resToChange.GetItem())}
|
|
|
|
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
|
|
require.NoError(gErr)
|
|
require.NotNil(got)
|
|
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
|
created := st.GetCreateTime().GetTimestamp()
|
|
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
|
|
|
want.Item.UpdatedTime = got.Item.UpdatedTime
|
|
|
|
assert.EqualValues(2, got.Item.Version)
|
|
want.Item.Version = 2
|
|
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
))
|
|
})
|
|
}
|
|
|
|
vl, cleanup := freshLibrary()
|
|
defer cleanup()
|
|
|
|
errCases := []struct {
|
|
name string
|
|
path string
|
|
item *pb.CredentialLibrary
|
|
}{
|
|
{
|
|
name: "read only type",
|
|
path: "type",
|
|
item: &pb.CredentialLibrary{Type: "something"},
|
|
},
|
|
{
|
|
name: "read only store_id",
|
|
path: "store_id",
|
|
item: &pb.CredentialLibrary{CredentialStoreId: diffStore.GetPublicId()},
|
|
},
|
|
{
|
|
name: "read only updated_time",
|
|
path: "updated_time",
|
|
item: &pb.CredentialLibrary{UpdatedTime: timestamppb.Now()},
|
|
},
|
|
{
|
|
name: "read only created_time",
|
|
path: "created_time",
|
|
item: &pb.CredentialLibrary{UpdatedTime: timestamppb.Now()},
|
|
},
|
|
{
|
|
name: "read only authorized actions",
|
|
path: "authorized actions",
|
|
item: &pb.CredentialLibrary{AuthorizedActions: append(testAuthorizedActions, "another")},
|
|
},
|
|
{
|
|
name: "read only credential type",
|
|
path: "credential_type",
|
|
item: &pb.CredentialLibrary{CredentialType: string(globals.UsernamePasswordCredentialType)},
|
|
},
|
|
}
|
|
for _, tc := range errCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.UpdateCredentialLibraryRequest{
|
|
Id: vl.GetPublicId(),
|
|
Item: tc.item,
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
|
}
|
|
req.Item.Version = vl.Version
|
|
|
|
got, gErr := s.UpdateCredentialLibrary(ctx, req)
|
|
assert.Error(t, gErr)
|
|
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
|
assert.Nil(t, got)
|
|
})
|
|
}
|
|
vErrs := []struct {
|
|
name string
|
|
req *pbs.UpdateCredentialLibraryRequest
|
|
errContains string
|
|
}{
|
|
{
|
|
name: "invalid key type key bits combination",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(keyBitsField, keyTypeField),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyType: wrapperspb.String(vault.KeyTypeEcdsa),
|
|
KeyBits: wrapperspb.UInt32(2048),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
errContains: "Invalid bit size 2048 for key type ecdsa",
|
|
},
|
|
{
|
|
name: "invalid key type",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(keyTypeField),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyType: wrapperspb.String("unknown-type"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
errContains: "If set, value must be 'ed25519', 'ecdsa', or 'rsa'.",
|
|
},
|
|
{
|
|
name: "invalid key bits",
|
|
req: &pbs.UpdateCredentialLibraryRequest{
|
|
UpdateMask: fieldmask(keyBitsField),
|
|
Item: &pb.CredentialLibrary{
|
|
Type: vault.SSHCertificateLibrarySubtype.String(),
|
|
Attrs: &pb.CredentialLibrary_VaultSshCertificateCredentialLibraryAttributes{
|
|
VaultSshCertificateCredentialLibraryAttributes: &pb.VaultSSHCertificateCredentialLibraryAttributes{
|
|
KeyBits: wrapperspb.UInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
errContains: "Invalid bit size 1234",
|
|
},
|
|
}
|
|
for _, tc := range vErrs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
scl, cleanup := freshLibrary()
|
|
defer cleanup()
|
|
|
|
if tc.req.Item.GetVersion() == 0 {
|
|
tc.req.Item.Version = 1
|
|
}
|
|
if tc.req.GetId() == "" {
|
|
tc.req.Id = scl.GetPublicId()
|
|
}
|
|
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
|
|
if gErr != nil {
|
|
assert.Contains(t, gErr.Error(), tc.errContains)
|
|
assert.Nil(t, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListPagination(t *testing.T) {
|
|
// Set database read timeout to avoid duplicates in response
|
|
oldReadTimeout := globals.RefreshReadLookbackDuration
|
|
globals.RefreshReadLookbackDuration = 0
|
|
t.Cleanup(func() {
|
|
globals.RefreshReadLookbackDuration = oldReadTimeout
|
|
})
|
|
assert, require := assert.New(t), require.New(t)
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
sqlDB, err := conn.SqlDB(ctx)
|
|
require.NoError(err)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
rw := db.New(conn)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
repoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
tokenRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
vaultRepo, err := repoFn()
|
|
require.NoError(err)
|
|
tokenRepo, err := tokenRepoFn()
|
|
require.NoError(err)
|
|
|
|
_, prjNoLibs := iam.TestScopes(t, iamRepo)
|
|
storeNoLibs := vault.TestCredentialStores(t, conn, wrapper, prjNoLibs.GetPublicId(), 1)[0]
|
|
o, prj := iam.TestScopes(t, iamRepo)
|
|
|
|
credStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
|
var allCredentialLibraries []*pb.CredentialLibrary
|
|
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
|
allCredentialLibraries = append(allCredentialLibraries, vaultCredentialLibraryToProto(l, prj))
|
|
}
|
|
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
|
|
allCredentialLibraries = append(allCredentialLibraries, sshCredentialLibraryToProto(l, prj))
|
|
}
|
|
|
|
// Reverse as we return items sorted by create_time desceding (newest first)
|
|
slices.Reverse(allCredentialLibraries)
|
|
|
|
// Run analyze to update postgres meta tables
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(err)
|
|
|
|
authMethod := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0]
|
|
// auth account is only used to join auth method to user.
|
|
// We don't do anything else with the auth account in the test setup.
|
|
acct := password.TestAccount(t, conn, authMethod.GetPublicId(), "test_user")
|
|
u := iam.TestUser(t, iamRepo, o.GetPublicId(), iam.WithAccountIds(acct.PublicId))
|
|
role1 := iam.TestRole(t, conn, prj.GetPublicId())
|
|
iam.TestRoleGrant(t, conn, role1.GetPublicId(), "ids=*;type=*;actions=*")
|
|
iam.TestUserRole(t, conn, role1.GetPublicId(), u.GetPublicId())
|
|
role2 := iam.TestRole(t, conn, prjNoLibs.GetPublicId())
|
|
iam.TestRoleGrant(t, conn, role2.GetPublicId(), "ids=*;type=*;actions=*")
|
|
iam.TestUserRole(t, conn, role2.GetPublicId(), u.GetPublicId())
|
|
at, err := tokenRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
|
|
require.NoError(err)
|
|
|
|
// Test without anon user
|
|
requestInfo := authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
Token: at.GetToken(),
|
|
}
|
|
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
|
|
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
|
|
require.NoError(err)
|
|
// Start paginating, recursively
|
|
req := &pbs.ListCredentialLibrariesRequest{
|
|
CredentialStoreId: credStore.PublicId, // Use store with libraries first
|
|
Filter: "",
|
|
ListToken: "",
|
|
PageSize: 2,
|
|
}
|
|
got, err := s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 2)
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: allCredentialLibraries[0:2],
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Request second page
|
|
req.ListToken = got.ListToken
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 2)
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: allCredentialLibraries[2:4],
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Request rest of results
|
|
req.ListToken = got.ListToken
|
|
req.PageSize = 10
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 6)
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: allCredentialLibraries[4:],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Create another credential library
|
|
newCredLib := vault.TestCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 1)[0]
|
|
pbNewCredLib := vaultCredentialLibraryToProto(newCredLib, prj)
|
|
// Prepend since this is the newest library
|
|
allCredentialLibraries = append([]*pb.CredentialLibrary{pbNewCredLib}, allCredentialLibraries...)
|
|
|
|
// Delete one of the other credential libraries
|
|
_, err = vaultRepo.DeleteCredentialLibrary(ctx, prj.PublicId, allCredentialLibraries[len(allCredentialLibraries)-1].Id)
|
|
require.NoError(err)
|
|
deletedCredLib := allCredentialLibraries[len(allCredentialLibraries)-1]
|
|
allCredentialLibraries = allCredentialLibraries[:len(allCredentialLibraries)-1]
|
|
|
|
// Update one of the other stores
|
|
allCredentialLibraries[1].Name = wrapperspb.String("new-name")
|
|
allCredentialLibraries[1].Version = 2
|
|
updatedLibrary := &vault.SSHCertificateCredentialLibrary{
|
|
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{
|
|
PublicId: allCredentialLibraries[1].GetId(),
|
|
StoreId: allCredentialLibraries[1].GetCredentialStoreId(),
|
|
Name: allCredentialLibraries[1].Name.Value,
|
|
},
|
|
}
|
|
cred, _, err := vaultRepo.UpdateSSHCertificateCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
|
|
require.NoError(err)
|
|
allCredentialLibraries[1].UpdatedTime = cred.UpdateTime.GetTimestamp()
|
|
allCredentialLibraries[1].Version = cred.Version
|
|
// Add to the front since it's most recently updated
|
|
allCredentialLibraries = append(
|
|
[]*pb.CredentialLibrary{allCredentialLibraries[1]},
|
|
append(
|
|
[]*pb.CredentialLibrary{allCredentialLibraries[0]},
|
|
allCredentialLibraries[2:]...,
|
|
)...,
|
|
)
|
|
|
|
// Run analyze to update postgres meta tables
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(err)
|
|
|
|
// Request updated results
|
|
req.ListToken = got.ListToken
|
|
req.PageSize = 1
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 1)
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: []*pb.CredentialLibrary{allCredentialLibraries[0]},
|
|
ResponseType: "delta",
|
|
SortBy: "updated_time",
|
|
SortDir: "desc",
|
|
// Should contain the deleted library
|
|
RemovedIds: []string{deletedCredLib.Id},
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Get next page
|
|
req.ListToken = got.ListToken
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 1)
|
|
// Compare without comparing the list token
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: []*pb.CredentialLibrary{allCredentialLibraries[1]},
|
|
ResponseType: "complete",
|
|
SortBy: "updated_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Request new page with filter requiring looping
|
|
// to fill the page.
|
|
req.ListToken = ""
|
|
req.PageSize = 1
|
|
req.Filter = fmt.Sprintf(`"/item/id"==%q or "/item/id"==%q`, allCredentialLibraries[len(allCredentialLibraries)-2].Id, allCredentialLibraries[len(allCredentialLibraries)-1].Id)
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 1)
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: []*pb.CredentialLibrary{allCredentialLibraries[len(allCredentialLibraries)-2]},
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
// Should be empty again
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
req.ListToken = got.ListToken
|
|
// Get the second page
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 1)
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
Items: []*pb.CredentialLibrary{allCredentialLibraries[len(allCredentialLibraries)-1]},
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 10,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListCredentialLibrariesResponse{}, "list_token"),
|
|
),
|
|
)
|
|
req.ListToken = got.ListToken
|
|
|
|
// List items in the empty store
|
|
req = &pbs.ListCredentialLibrariesRequest{
|
|
CredentialStoreId: storeNoLibs.PublicId,
|
|
Filter: "",
|
|
ListToken: "",
|
|
PageSize: 2,
|
|
}
|
|
got, err = s.ListCredentialLibraries(ctx, req)
|
|
require.NoError(err)
|
|
require.Len(got.GetItems(), 0)
|
|
assert.Empty(
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListCredentialLibrariesResponse{
|
|
ResponseType: "complete",
|
|
ListToken: "", // Should return no token
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
),
|
|
)
|
|
|
|
// Create unauthenticated user
|
|
unauthAt := authtoken.TestAuthToken(t, conn, kms, o.GetPublicId())
|
|
unauthR := iam.TestRole(t, conn, prj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, unauthR.GetPublicId(), unauthAt.GetIamUserId())
|
|
|
|
// Make a request with the unauthenticated user,
|
|
// ensure the response is 403 forbidden.
|
|
requestInfo = authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: unauthAt.GetPublicId(),
|
|
Token: unauthAt.GetToken(),
|
|
}
|
|
requestContext = context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
|
|
_, err = s.ListCredentialLibraries(ctx, &pbs.ListCredentialLibrariesRequest{
|
|
CredentialStoreId: credStore.PublicId,
|
|
})
|
|
require.Error(err)
|
|
assert.Equal(handlers.ForbiddenError(), err)
|
|
}
|