feat(handlers): Implement CRUDL for vault generic Password credential (#6161)

This commit is contained in:
Justin Nguyen authored and Bharath Gajjala committed 2025-11-13 12:28:04 -08:00
1 parent 4a8009fec4
commit 82745e16be
8 files changed
+546 -504

No files matched your search

+2 -2
View File
@@ -143,9 +143,9 @@ type UsernamePasswordDomain interface {
Domain() string
}
// PasswordCredential is a credential containing a username and a password.
// PasswordOnly is a credential containing a password.
// Does not follow naming convention to avoid conflict with existing Password type.
type PasswordCredential interface {
type PasswordOnly interface {
Credential
Password() Password
}
+1 -1
View File
@@ -169,7 +169,7 @@ func baseToUsrPassDomain(ctx context.Context, bc *baseCred) (*usrPassDomainCred,
}, nil
}
var _ credential.PasswordCredential = (*passCred)(nil)
var _ credential.PasswordOnly = (*passCred)(nil)
type passCred struct {
*baseCred
@@ -83,6 +83,7 @@ var (
globals.SshPrivateKeyCredentialType,
globals.UnspecifiedCredentialType,
globals.UsernamePasswordDomainCredentialType,
globals.PasswordCredentialType,
}
validKeyTypes = []string{
@@ -851,6 +852,11 @@ func toProto(ctx context.Context, in credential.Library, opt ...handlers.Option)
m[domainAttribute] = mapping.DomainAttribute
}
case *vault.PasswordOverride:
if mapping.PasswordAttribute != "" {
m[passwordAttribute] = mapping.PasswordAttribute
}
case *vault.SshPrivateKeyOverride:
if mapping.UsernameAttribute != "" {
m[usernameAttribute] = mapping.UsernameAttribute
@@ -1006,6 +1012,17 @@ func toStorageVaultLibrary(ctx context.Context, storeId string, in *pb.Credentia
opts = append(opts, vault.WithMappingOverride(vault.NewUsernamePasswordDomainOverride(mapOpts...)))
}
case globals.PasswordCredentialType:
opts = append(opts, vault.WithCredentialType(credentialType))
overrides := in.CredentialMappingOverrides.AsMap()
var mapOpts []vault.Option
if password := overrides[passwordAttribute]; password != nil {
mapOpts = append(mapOpts, vault.WithOverridePasswordAttribute(password.(string)))
}
if len(mapOpts) > 0 {
opts = append(opts, vault.WithMappingOverride(vault.NewPasswordOverride(mapOpts...)))
}
case globals.SshPrivateKeyCredentialType:
opts = append(opts, vault.WithCredentialType(credentialType))
overrides := in.CredentialMappingOverrides.AsMap()
@@ -1340,6 +1357,8 @@ func validateMapping(badFields map[string]string, credentialType globals.Credent
validFields[usernameAttribute] = true
validFields[passwordAttribute] = true
validFields[domainAttribute] = true
case globals.PasswordCredentialType:
validFields[passwordAttribute] = true
default:
badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", credentialType)
return
@@ -1457,6 +1476,18 @@ func getMappingUpdates(credentialType globals.CredentialType, current vault.Mapp
default:
ret[domainAttribute] = currentDomain
}
case globals.PasswordCredentialType:
var currentPass any
if overrides, ok := current.(*vault.PasswordOverride); ok {
currentPass = overrides.PasswordAttribute
}
switch {
case masks[passwordAttribute]:
ret[passwordAttribute] = new[passwordAttribute]
default:
ret[passwordAttribute] = currentPass
}
case globals.SshPrivateKeyCredentialType:
var currentUser, currentpPass, currentPk any
if overrides, ok := current.(*vault.SshPrivateKeyOverride); ok {
@@ -14,7 +14,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
@@ -87,25 +86,6 @@ func sshCredentialLibraryToProto(credLib *vault.SSHCertificateCredentialLibrary,
}
}
func ldapCredentialLibraryToProto(credLib *vault.LdapCredentialLibrary, 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.LdapCredentialLibrarySubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String(credLib.GetVaultPath()),
},
},
}
}
func TestList(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
@@ -134,9 +114,6 @@ func TestList(t *testing.T) {
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
wantLibraries = append(wantLibraries, sshCredentialLibraryToProto(l, prj))
}
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 10) {
wantLibraries = append(wantLibraries, ldapCredentialLibraryToProto(l, prj))
}
cases := []struct {
name string
@@ -154,7 +131,7 @@ func TestList(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 30,
EstItemCount: 20,
},
anonRes: &pbs.ListCredentialLibrariesResponse{
Items: wantLibraries,
@@ -162,7 +139,7 @@ func TestList(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 30,
EstItemCount: 20,
},
},
{
@@ -287,20 +264,16 @@ func TestList_Attributes(t *testing.T) {
_, prj := iam.TestScopes(t, iamRepo)
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 3)
storeGeneric, storeSSHCertificate, storeLdap := ts[0], ts[1], ts[2]
ts := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
storeGeneric, storeSSHCertificate := ts[0], ts[1]
var wantLibrariesGeneric []*pb.CredentialLibrary
var wantLibrariesSSHCertificate []*pb.CredentialLibrary
var wantLibrariesLdap []*pb.CredentialLibrary
for _, l := range vault.TestCredentialLibraries(t, conn, wrapper, storeGeneric.GetPublicId(), globals.UnspecifiedCredentialType, 5) {
wantLibrariesGeneric = append(wantLibrariesGeneric, vaultCredentialLibraryToProto(l, prj))
}
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, storeSSHCertificate.GetPublicId(), 10) {
wantLibrariesSSHCertificate = append(wantLibrariesSSHCertificate, sshCredentialLibraryToProto(l, prj))
}
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, storeLdap.GetPublicId(), 10) {
wantLibrariesLdap = append(wantLibrariesLdap, ldapCredentialLibraryToProto(l, prj))
}
cases := []struct {
name string
@@ -345,24 +318,6 @@ func TestList_Attributes(t *testing.T) {
RemovedIds: nil,
}, // anonymous user does not have access to attributes
},
{
name: "Filter on Attribute Ldap Library",
req: &pbs.ListCredentialLibrariesRequest{CredentialStoreId: storeLdap.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/path"==%q`, wantLibrariesLdap[2].GetVaultLdapCredentialLibraryAttributes().GetPath().Value)},
res: &pbs.ListCredentialLibrariesResponse{
Items: wantLibrariesLdap[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) {
@@ -632,47 +587,26 @@ func TestCreate(t *testing.T) {
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "No Vault LDAP path",
req: &pbs.CreateCredentialLibraryRequest{
Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Type: vault.LdapCredentialLibrarySubtype.String(),
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String(""),
},
name: "Invalid 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"),
},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid Vault LDAP credential type",
req: &pbs.CreateCredentialLibraryRequest{
Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Type: vault.LdapCredentialLibrarySubtype.String(),
CredentialType: string(globals.SshPrivateKeyCredentialType),
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("ldap/invalid/path"),
},
},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid credential library type",
req: &pbs.CreateCredentialLibraryRequest{
Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Type: "bla-invalid",
CredentialType: "bla-invalid",
},
},
CredentialType: string(globals.PasswordCredentialType),
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
passwordAttribute: "password-test",
"invalid": "invalid-test",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
@@ -1073,6 +1007,90 @@ func TestCreate(t *testing.T) {
},
},
},
{
name: "Create a valid vault CredentialLibrary 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.PasswordCredentialType),
}},
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.PasswordCredentialType),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Create a valid vault CredentialLibrary password type with 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{
passwordAttribute: "password-test",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
CredentialType: string(globals.PasswordCredentialType),
}},
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.PasswordCredentialType),
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
passwordAttribute: "password-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{
@@ -1193,40 +1211,6 @@ func TestCreate(t *testing.T) {
},
},
},
{
name: "Create a valid Vault LDAP Credential library",
req: &pbs.CreateCredentialLibraryRequest{
Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Type: vault.LdapCredentialLibrarySubtype.String(),
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("ldap/creds/hi"),
},
},
},
},
idPrefix: globals.VaultLdapCredentialLibraryPrefix + "_",
res: &pbs.CreateCredentialLibraryResponse{
Uri: fmt.Sprintf("credential-libraries/%s_", globals.VaultLdapCredentialLibraryPrefix),
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.LdapCredentialLibrarySubtype.String(),
CredentialType: string(globals.UsernamePasswordDomainCredentialType),
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("ldap/creds/hi"),
},
},
AuthorizedActions: testAuthorizedActions,
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
@@ -1324,6 +1308,17 @@ func TestGet(t *testing.T) {
userPassDomainLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), libDomain)
require.NoError(t, err)
libPassword, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
vault.WithCredentialType("password"),
vault.WithMappingOverride(
vault.NewPasswordOverride(
vault.WithOverridePasswordAttribute("pass"),
)))
require.NoError(t, err)
passwordLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), libPassword)
require.NoError(t, err)
lib1, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path/ssh",
vault.WithCredentialType("ssh_private_key"),
vault.WithMappingOverride(
@@ -1341,13 +1336,6 @@ func TestGet(t *testing.T) {
sshCertLib, err := repo.CreateSSHCertificateCredentialLibrary(context.Background(), prj.GetPublicId(), lib2)
require.NoError(t, err)
libLdap1, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/test")
require.NoError(t, err)
require.NotNil(t, libLdap1)
libLdap1, err = repo.CreateLdapCredentialLibrary(t.Context(), prj.GetPublicId(), libLdap1)
require.NoError(t, err)
require.NotNil(t, libLdap1)
cases := []struct {
name string
id string
@@ -1441,6 +1429,37 @@ func TestGet(t *testing.T) {
},
},
},
{
name: "success-password",
id: passwordLib.GetPublicId(),
res: &pbs.GetCredentialLibraryResponse{
Item: &pb.CredentialLibrary{
Id: passwordLib.GetPublicId(),
CredentialStoreId: passwordLib.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.GenericLibrarySubtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: passwordLib.CreateTime.GetTimestamp(),
UpdatedTime: passwordLib.UpdateTime.GetTimestamp(),
Version: 1,
Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{
VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(passwordLib.GetVaultPath()),
HttpMethod: wrapperspb.String(passwordLib.GetHttpMethod()),
},
},
CredentialType: "password",
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
passwordAttribute: "pass",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
},
{
name: "success-ssh-private-key",
id: sshPkLib.GetPublicId(),
@@ -1498,28 +1517,6 @@ func TestGet(t *testing.T) {
},
},
},
{
name: "success-ldap",
id: libLdap1.GetPublicId(),
res: &pbs.GetCredentialLibraryResponse{
Item: &pb.CredentialLibrary{
Id: libLdap1.GetPublicId(),
CredentialStoreId: libLdap1.GetStoreId(),
CredentialType: libLdap1.GetCredentialType(),
Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.LdapCredentialLibrarySubtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: libLdap1.CreateTime.GetTimestamp(),
UpdatedTime: libLdap1.UpdateTime.GetTimestamp(),
Version: 1,
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String(libLdap1.GetVaultPath()),
},
},
},
},
},
{
name: "not found error",
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
@@ -1582,7 +1579,6 @@ func TestDelete(t *testing.T) {
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
vl := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), globals.UnspecifiedCredentialType, 1)[0]
vl2 := vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
vl3 := vault.TestLdapCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
require.NoError(t, err)
cases := []struct {
@@ -1599,10 +1595,6 @@ func TestDelete(t *testing.T) {
name: "success-ssh-cert",
id: vl2.GetPublicId(),
},
{
name: "success-ldap",
id: vl3.GetPublicId(),
},
{
name: "not found error",
id: fmt.Sprintf("%s_1234567890", globals.VaultCredentialLibraryPrefix),
@@ -2118,6 +2110,84 @@ func TestUpdate(t *testing.T) {
return out
},
},
{
name: "password-attribute-change-password-attribute",
opts: []vault.Option{
vault.WithCredentialType("password"),
vault.WithMappingOverride(
vault.NewPasswordOverride(
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: "password-no-mapping-override-change-password-attribute",
opts: []vault.Option{
vault.WithCredentialType("password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
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{
passwordAttribute: "new-pass",
}
var err error
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
require.NoError(t, err)
return out
},
},
{
name: "password-attribute-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("password"),
vault.WithMappingOverride(
vault.NewPasswordOverride(
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{
@@ -2166,7 +2236,7 @@ func TestUpdate(t *testing.T) {
},
},
{
name: "username-password-attributes-delete-mapping-override-field-specific",
name: "username-password-domain-attributes-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("username_password_domain"),
vault.WithMappingOverride(
@@ -2248,6 +2318,75 @@ func TestUpdate(t *testing.T) {
return out
},
},
{
name: "password-no-mapping-override-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("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: "password-no-mapping-override-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
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: "password-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("password"),
vault.WithMappingOverride(
vault.NewPasswordOverride(
vault.WithOverridePasswordAttribute("orig-pass"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]any{
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{
@@ -3402,287 +3541,6 @@ func TestUpdate_SSHCertificateCredentialLibrary(t *testing.T) {
}
}
func TestUpdate_LdapCredentialLibrary(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(), 1)
store := cs[0]
freshLibrary := func(opt ...vault.Option) (*vault.LdapCredentialLibrary, func()) {
repo, err := repoFn()
require.NoError(t, err)
lib, err := vault.NewLdapCredentialLibrary(store.GetPublicId(), "ldap/creds/foo", opt...)
require.NoError(t, err)
vl, err := repo.CreateLdapCredentialLibrary(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
}
tests := []struct {
name string
req *pbs.UpdateCredentialLibraryRequest
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
wantErr bool
wantErrStatus int
wantErrStr string
}{
{
name: "updateNonExistentField",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"doesntExist"}},
Item: &pb.CredentialLibrary{
Name: wrapperspb.String("updateNonExistentField_test"),
},
},
wantErr: true,
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
wantErrStr: "No valid fields included in the update mask.",
},
{
name: "updateImmutableField",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"storeId"}},
Item: &pb.CredentialLibrary{
CredentialStoreId: "immutable",
},
},
wantErr: true,
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
wantErrStr: "No valid fields included in the update mask.",
},
{
name: "updateValidFieldWithInvalidData",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
Item: &pb.CredentialLibrary{
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("invalid_path"),
},
},
},
},
wantErr: true,
wantErrStr: "vault_path_must_have_staticcred_or_creds constraint failed",
},
{
name: "updateImmutableCredentialTypeField",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"credentialType"}},
Item: &pb.CredentialLibrary{
CredentialType: string(globals.SshPrivateKeyCredentialType),
},
},
wantErr: true,
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
wantErrStr: "Cannot modify credential type.",
},
{
name: "updateImmutableTypeField",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"type"}},
Item: &pb.CredentialLibrary{
Type: string(globals.SshCertificateCredentialType),
},
},
wantErr: true,
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
wantErrStr: "Cannot modify resource type.",
},
{
name: "unsetVaultPath",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
Item: &pb.CredentialLibrary{
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String(""),
},
},
},
},
wantErr: true,
wantErrStatus: runtime.HTTPStatusFromCode(codes.InvalidArgument),
wantErrStr: "attributes.path\", desc: \"This is a required field and cannot be set to empty.",
},
{
name: "updateName",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}},
Item: &pb.CredentialLibrary{
Name: wrapperspb.String("updated_name"),
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.Name = wrapperspb.String("updated_name")
return cl
},
},
{
name: "updateDescription",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"description"}},
Item: &pb.CredentialLibrary{
Description: wrapperspb.String("updated_description"),
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.Description = wrapperspb.String("updated_description")
return cl
},
},
{
name: "updateValidVaultPath",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"attributes.path"}},
Item: &pb.CredentialLibrary{
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("ldap/static-cred/updated/path"),
},
},
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/static-cred/updated/path")
return cl
},
},
{
name: "updateMultipleOneNonExistent",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "doesntExist"}},
Item: &pb.CredentialLibrary{
Name: wrapperspb.String("updated_name"),
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.Name = wrapperspb.String("updated_name")
return cl
},
},
{
name: "updateMultipleOneImmutable",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime.
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "credentialStoreId"}},
Item: &pb.CredentialLibrary{
Name: wrapperspb.String("updated_name"),
CredentialStoreId: "immutable",
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.Name = wrapperspb.String("updated_name")
return cl
},
},
{
name: "updateMultiple",
req: &pbs.UpdateCredentialLibraryRequest{
Id: "", // Set at test runtime
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "attributes.path"}},
Item: &pb.CredentialLibrary{
Name: wrapperspb.String("updated_name"),
Attrs: &pb.CredentialLibrary_VaultLdapCredentialLibraryAttributes{
VaultLdapCredentialLibraryAttributes: &pb.VaultLdapCredentialLibraryAttributes{
Path: wrapperspb.String("ldap/creds/updated/path"),
},
},
},
},
res: func(cl *pb.CredentialLibrary) *pb.CredentialLibrary {
cl.Name = wrapperspb.String("updated_name")
cl.GetVaultLdapCredentialLibraryAttributes().Path = wrapperspb.String("ldap/creds/updated/path")
return cl
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
st, cleanup := freshLibrary()
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)
got, gErr := s.UpdateCredentialLibrary(ctx, tc.req)
if tc.wantErr {
require.ErrorContains(gErr, tc.wantErrStr)
gApiErr, ok := gErr.(*handlers.ApiError)
if ok {
// We don't always return status.
require.NotNil(gApiErr)
require.EqualValues(tc.wantErrStatus, gApiErr.Status)
}
require.Nil(got)
return
}
require.NoError(gErr)
require.NotNil(got)
want := &pbs.UpdateCredentialLibraryResponse{Item: tc.res(resToChange.GetItem())}
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
}),
))
})
}
}
func TestListPagination(t *testing.T) {
// Set database read timeout to avoid duplicates in response
oldReadTimeout := globals.RefreshReadLookbackDuration
@@ -3731,9 +3589,6 @@ func TestListPagination(t *testing.T) {
for _, l := range vault.TestSSHCertificateCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
allCredentialLibraries = append(allCredentialLibraries, sshCredentialLibraryToProto(l, prj))
}
for _, l := range vault.TestLdapCredentialLibraries(t, conn, wrapper, credStore.GetPublicId(), 5) {
allCredentialLibraries = append(allCredentialLibraries, ldapCredentialLibraryToProto(l, prj))
}
// Reverse as we return items sorted by create_time desceding (newest first)
slices.Reverse(allCredentialLibraries)
@@ -3787,7 +3642,7 @@ func TestListPagination(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3812,7 +3667,7 @@ func TestListPagination(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3824,10 +3679,10 @@ func TestListPagination(t *testing.T) {
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 15
req.PageSize = 10
got, err = s.ListCredentialLibraries(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 11)
require.Len(got.GetItems(), 6)
// Compare without comparing the list token
assert.Empty(
cmp.Diff(
@@ -3838,7 +3693,7 @@ func TestListPagination(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3863,14 +3718,14 @@ func TestListPagination(t *testing.T) {
// Update one of the other stores
allCredentialLibraries[1].Name = wrapperspb.String("new-name")
allCredentialLibraries[1].Version = 2
updatedLibrary := &vault.LdapCredentialLibrary{
LdapCredentialLibrary: &store.LdapCredentialLibrary{
updatedLibrary := &vault.SSHCertificateCredentialLibrary{
SSHCertificateCredentialLibrary: &store.SSHCertificateCredentialLibrary{
PublicId: allCredentialLibraries[1].GetId(),
StoreId: allCredentialLibraries[1].GetCredentialStoreId(),
Name: allCredentialLibraries[1].Name.Value,
},
}
cred, _, err := vaultRepo.UpdateLdapCredentialLibrary(ctx, prj.PublicId, updatedLibrary, 1, []string{"name"})
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
@@ -3904,7 +3759,7 @@ func TestListPagination(t *testing.T) {
SortDir: "desc",
// Should contain the deleted library
RemovedIds: []string{deletedCredLib.Id},
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3929,7 +3784,7 @@ func TestListPagination(t *testing.T) {
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3957,7 +3812,7 @@ func TestListPagination(t *testing.T) {
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -3980,7 +3835,7 @@ func TestListPagination(t *testing.T) {
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
@@ -137,6 +137,17 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
case credential.PasswordOnly:
credData, err = handlers.ProtoToStruct(
ctx,
&pb.PasswordCredential{
Password: string(c.Password()),
},
)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
case credential.SshPrivateKey:
credData, err = handlers.ProtoToStruct(
ctx,
@@ -4170,7 +4170,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "username_password",
CredentialType: string(globals.UsernamePasswordCredentialType),
}})
require.NoError(t, err)
@@ -4203,7 +4203,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "username_password",
CredentialType: string(globals.UsernamePasswordCredentialType),
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
"username_attribute": structpb.NewStringValue("non-default-user"),
"password_attribute": structpb.NewStringValue("non-default-pass"),
@@ -4211,6 +4211,44 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
}})
require.NoError(t, err)
defaultPassword := v.CreateKVSecret(t, "default-password", []byte(`{"data": {"password": "my-default-password"}}`))
require.NotNil(t, defaultPassword)
nonDefaultPassword := v.CreateKVSecret(t, "non-default-password", []byte(`{"data": {"non-default-password": "my-non-default-password"}}`))
require.NotNil(t, nonDefaultPassword)
clsRespPassword, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
CredentialStoreId: vaultStore.GetPublicId(),
Name: wrapperspb.String("Password Library"),
Description: wrapperspb.String("Password Library Description"),
Type: vault.GenericLibrarySubtype.String(),
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(path.Join("secret", "data", "default-password")),
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: string(globals.PasswordCredentialType),
}})
require.NoError(t, err)
clsRespPasswordWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
CredentialStoreId: vaultStore.GetPublicId(),
Name: wrapperspb.String("Password Mapping Library"),
Description: wrapperspb.String("Password Mapping Library Description"),
Type: vault.GenericLibrarySubtype.String(),
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(path.Join("secret", "data", "non-default-password")),
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: string(globals.PasswordCredentialType),
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
"password_attribute": structpb.NewStringValue("non-default-password"),
}},
}})
require.NoError(t, err)
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
credService, err := credentials.NewService(ctx, iamRepoFn, staticCredRepoFn, 1000)
require.NoError(t, err)
@@ -4291,7 +4329,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
CredentialType: string(globals.SshPrivateKeyCredentialType),
}})
require.NoError(t, err)
@@ -4310,7 +4348,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
CredentialType: string(globals.SshPrivateKeyCredentialType),
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
"username_attribute": structpb.NewStringValue("non-default-user"),
"private_key_attribute": structpb.NewStringValue("non-default-pk"),
@@ -4333,7 +4371,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
CredentialType: string(globals.SshPrivateKeyCredentialType),
}})
require.NoError(t, err)
require.NotNil(t, clsRespSshPrivateKeyWithPass)
@@ -4353,7 +4391,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: "ssh_private_key",
CredentialType: string(globals.SshPrivateKeyCredentialType),
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
"username_attribute": structpb.NewStringValue("/data/non-default-user"),
"private_key_attribute": structpb.NewStringValue("/data/non-default-pk"),
@@ -4443,6 +4481,58 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
},
wantedConnectionLimit: 100,
},
{
name: "vault-password",
hostSourceId: shs.GetPublicId(),
credSourceId: clsRespPassword.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: clsRespPassword.GetItem().GetId(),
Name: clsRespPassword.GetItem().GetName().GetValue(),
Description: clsRespPassword.GetItem().GetDescription().GetValue(),
CredentialStoreId: vaultStore.GetPublicId(),
Type: vault.GenericLibrarySubtype.String(),
CredentialType: string(globals.PasswordCredentialType),
},
Credential: func() *structpb.Struct {
data := map[string]any{
"password": "my-default-password",
}
st, err := structpb.NewStruct(data)
require.NoError(t, err)
return st
}(),
},
wantedConnectionLimit: 10,
},
{
name: "vault-password-with-mapping",
hostSourceId: shs.GetPublicId(),
credSourceId: clsRespPasswordWithMapping.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
wantedCred: &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: clsRespPasswordWithMapping.GetItem().GetId(),
Name: clsRespPasswordWithMapping.GetItem().GetName().GetValue(),
Description: clsRespPasswordWithMapping.GetItem().GetDescription().GetValue(),
CredentialStoreId: vaultStore.GetPublicId(),
Type: vault.GenericLibrarySubtype.String(),
CredentialType: string(globals.PasswordCredentialType),
},
Credential: func() *structpb.Struct {
data := map[string]any{
"password": "my-non-default-password",
}
st, err := structpb.NewStruct(data)
require.NoError(t, err)
return st
}(),
},
wantedConnectionLimit: 100,
},
{
name: "static-UsernamePassword",
hostSourceId: shs.GetPublicId(),
@@ -475,6 +475,12 @@ message UsernamePasswordDomainCredential {
string domain = 3; // @gotags: `class:"public"`
}
// The layout of the struct for "credential" field in SessionCredential for a password credential type.
message PasswordCredential {
// Password of the credential
string password = 1; // @gotags: `class:"secret"`
}
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
message SshPrivateKeyCredential {
// Username of the credential
@@ -1487,6 +1487,52 @@ func (x *UsernamePasswordDomainCredential) GetDomain() string {
return ""
}
// The layout of the struct for "credential" field in SessionCredential for a password credential type.
type PasswordCredential struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Password of the credential
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty" class:"secret"` // @gotags: `class:"secret"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PasswordCredential) Reset() {
*x = PasswordCredential{}
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PasswordCredential) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PasswordCredential) ProtoMessage() {}
func (x *PasswordCredential) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PasswordCredential.ProtoReflect.Descriptor instead.
func (*PasswordCredential) Descriptor() ([]byte, []int) {
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{16}
}
func (x *PasswordCredential) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
// The layout of the struct for "credential" field in SessionCredential for a ssh_private_key credential type.
type SshPrivateKeyCredential struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -1502,7 +1548,7 @@ type SshPrivateKeyCredential struct {
func (x *SshPrivateKeyCredential) Reset() {
*x = SshPrivateKeyCredential{}
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1514,7 +1560,7 @@ func (x *SshPrivateKeyCredential) String() string {
func (*SshPrivateKeyCredential) ProtoMessage() {}
func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[16]
mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1527,7 +1573,7 @@ func (x *SshPrivateKeyCredential) ProtoReflect() protoreflect.Message {
// Deprecated: Use SshPrivateKeyCredential.ProtoReflect.Descriptor instead.
func (*SshPrivateKeyCredential) Descriptor() ([]byte, []int) {
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{16}
return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{17}
}
func (x *SshPrivateKeyCredential) GetUsername() string {
@@ -1709,7 +1755,9 @@ const file_controller_api_resources_targets_v1_target_proto_rawDesc = "" +
" UsernamePasswordDomainCredential\x12\x1a\n" +
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
"\bpassword\x18\x02 \x01(\tR\bpassword\x12\x16\n" +
"\x06domain\x18\x03 \x01(\tR\x06domain\"\x8c\x01\n" +
"\x06domain\x18\x03 \x01(\tR\x06domain\"0\n" +
"\x12PasswordCredential\x12\x1a\n" +
"\bpassword\x18\x01 \x01(\tR\bpassword\"\x8c\x01\n" +
"\x17SshPrivateKeyCredential\x12\x1a\n" +
"\busername\x18\x01 \x01(\tR\busername\x12\x1f\n" +
"\vprivate_key\x18\x02 \x01(\tR\n" +
@@ -1728,7 +1776,7 @@ func file_controller_api_resources_targets_v1_target_proto_rawDescGZIP() []byte
return file_controller_api_resources_targets_v1_target_proto_rawDescData
}
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
var file_controller_api_resources_targets_v1_target_proto_goTypes = []any{
(*Alias)(nil), // 0: controller.api.resources.targets.v1.Alias
(*TargetAliasAttributes)(nil), // 1: controller.api.resources.targets.v1.TargetAliasAttributes
@@ -1746,57 +1794,58 @@ var file_controller_api_resources_targets_v1_target_proto_goTypes = []any{
(*SessionAuthorization)(nil), // 13: controller.api.resources.targets.v1.SessionAuthorization
(*UsernamePasswordCredential)(nil), // 14: controller.api.resources.targets.v1.UsernamePasswordCredential
(*UsernamePasswordDomainCredential)(nil), // 15: controller.api.resources.targets.v1.UsernamePasswordDomainCredential
(*SshPrivateKeyCredential)(nil), // 16: controller.api.resources.targets.v1.SshPrivateKeyCredential
(*structpb.Struct)(nil), // 17: google.protobuf.Struct
(*scopes.ScopeInfo)(nil), // 18: controller.api.resources.scopes.v1.ScopeInfo
(*wrapperspb.StringValue)(nil), // 19: google.protobuf.StringValue
(*timestamppb.Timestamp)(nil), // 20: google.protobuf.Timestamp
(*wrapperspb.UInt32Value)(nil), // 21: google.protobuf.UInt32Value
(*wrapperspb.Int32Value)(nil), // 22: google.protobuf.Int32Value
(*wrapperspb.BoolValue)(nil), // 23: google.protobuf.BoolValue
(*PasswordCredential)(nil), // 16: controller.api.resources.targets.v1.PasswordCredential
(*SshPrivateKeyCredential)(nil), // 17: controller.api.resources.targets.v1.SshPrivateKeyCredential
(*structpb.Struct)(nil), // 18: google.protobuf.Struct
(*scopes.ScopeInfo)(nil), // 19: controller.api.resources.scopes.v1.ScopeInfo
(*wrapperspb.StringValue)(nil), // 20: google.protobuf.StringValue
(*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp
(*wrapperspb.UInt32Value)(nil), // 22: google.protobuf.UInt32Value
(*wrapperspb.Int32Value)(nil), // 23: google.protobuf.Int32Value
(*wrapperspb.BoolValue)(nil), // 24: google.protobuf.BoolValue
}
var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{
1, // 0: controller.api.resources.targets.v1.Alias.attributes:type_name -> controller.api.resources.targets.v1.TargetAliasAttributes
2, // 1: controller.api.resources.targets.v1.TargetAliasAttributes.authorize_session_arguments:type_name -> controller.api.resources.targets.v1.AuthorizeSessionArguments
17, // 2: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
18, // 2: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct
4, // 3: controller.api.resources.targets.v1.SessionCredential.credential_source:type_name -> controller.api.resources.targets.v1.CredentialSource
5, // 4: controller.api.resources.targets.v1.SessionCredential.secret:type_name -> controller.api.resources.targets.v1.SessionSecret
17, // 5: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
18, // 6: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
19, // 7: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
19, // 8: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
20, // 9: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
20, // 10: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
18, // 5: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct
19, // 6: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
20, // 7: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue
20, // 8: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue
21, // 9: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp
21, // 10: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp
3, // 11: controller.api.resources.targets.v1.Target.host_sources:type_name -> controller.api.resources.targets.v1.HostSource
21, // 12: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
22, // 13: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
19, // 14: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
19, // 15: controller.api.resources.targets.v1.Target.egress_worker_filter:type_name -> google.protobuf.StringValue
19, // 16: controller.api.resources.targets.v1.Target.ingress_worker_filter:type_name -> google.protobuf.StringValue
22, // 12: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value
23, // 13: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value
20, // 14: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue
20, // 15: controller.api.resources.targets.v1.Target.egress_worker_filter:type_name -> google.protobuf.StringValue
20, // 16: controller.api.resources.targets.v1.Target.ingress_worker_filter:type_name -> google.protobuf.StringValue
4, // 17: controller.api.resources.targets.v1.Target.brokered_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
4, // 18: controller.api.resources.targets.v1.Target.injected_application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource
17, // 19: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
18, // 19: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
8, // 20: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes
9, // 21: controller.api.resources.targets.v1.Target.ssh_target_attributes:type_name -> controller.api.resources.targets.v1.SshTargetAttributes
10, // 22: controller.api.resources.targets.v1.Target.rdp_target_attributes:type_name -> controller.api.resources.targets.v1.RdpTargetAttributes
19, // 23: controller.api.resources.targets.v1.Target.address:type_name -> google.protobuf.StringValue
20, // 23: controller.api.resources.targets.v1.Target.address:type_name -> google.protobuf.StringValue
0, // 24: controller.api.resources.targets.v1.Target.aliases:type_name -> controller.api.resources.targets.v1.Alias
0, // 25: controller.api.resources.targets.v1.Target.with_aliases:type_name -> controller.api.resources.targets.v1.Alias
21, // 26: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
21, // 27: controller.api.resources.targets.v1.TcpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
21, // 28: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
21, // 29: controller.api.resources.targets.v1.SshTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
19, // 30: controller.api.resources.targets.v1.SshTargetAttributes.storage_bucket_id:type_name -> google.protobuf.StringValue
23, // 31: controller.api.resources.targets.v1.SshTargetAttributes.enable_session_recording:type_name -> google.protobuf.BoolValue
21, // 32: controller.api.resources.targets.v1.RdpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
21, // 33: controller.api.resources.targets.v1.RdpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
18, // 34: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
20, // 35: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
20, // 36: controller.api.resources.targets.v1.SessionAuthorizationData.expiration:type_name -> google.protobuf.Timestamp
22, // 26: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
22, // 27: controller.api.resources.targets.v1.TcpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
22, // 28: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
22, // 29: controller.api.resources.targets.v1.SshTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
20, // 30: controller.api.resources.targets.v1.SshTargetAttributes.storage_bucket_id:type_name -> google.protobuf.StringValue
24, // 31: controller.api.resources.targets.v1.SshTargetAttributes.enable_session_recording:type_name -> google.protobuf.BoolValue
22, // 32: controller.api.resources.targets.v1.RdpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
22, // 33: controller.api.resources.targets.v1.RdpTargetAttributes.default_client_port:type_name -> google.protobuf.UInt32Value
19, // 34: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
21, // 35: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
21, // 36: controller.api.resources.targets.v1.SessionAuthorizationData.expiration:type_name -> google.protobuf.Timestamp
11, // 37: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo
18, // 38: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
20, // 39: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
20, // 40: controller.api.resources.targets.v1.SessionAuthorization.expiration:type_name -> google.protobuf.Timestamp
19, // 38: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
21, // 39: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
21, // 40: controller.api.resources.targets.v1.SessionAuthorization.expiration:type_name -> google.protobuf.Timestamp
6, // 41: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential
42, // [42:42] is the sub-list for method output_type
42, // [42:42] is the sub-list for method input_type
@@ -1822,7 +1871,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controller_api_resources_targets_v1_target_proto_rawDesc), len(file_controller_api_resources_targets_v1_target_proto_rawDesc)),
NumEnums: 0,
NumMessages: 17,
NumMessages: 18,
NumExtensions: 0,
NumServices: 0,
},