diff --git a/internal/credential/vault/repository_credential_library.go b/internal/credential/vault/repository_credential_library.go index 727c06882e..9227c5b136 100644 --- a/internal/credential/vault/repository_credential_library.go +++ b/internal/credential/vault/repository_credential_library.go @@ -363,6 +363,7 @@ type listLookupLibrary struct { CredentialType string UsernameAttribute string PasswordAttribute string + DomainAttribute string PrivateKeyAttribute string PrivateKeyPassphraseAttribute string } @@ -395,6 +396,16 @@ func (pl *listLookupLibrary) toCredentialLibrary() *CredentialLibrary { up.sanitize() cl.MappingOverride = up } + case string(globals.UsernamePasswordDomainCredentialType): + if pl.UsernameAttribute != "" || pl.PasswordAttribute != "" || pl.DomainAttribute != "" { + upd := allocUsernamePasswordDomainOverride() + upd.LibraryId = pl.PublicId + upd.UsernameAttribute = pl.UsernameAttribute + upd.PasswordAttribute = pl.PasswordAttribute + upd.DomainAttribute = pl.DomainAttribute + upd.sanitize() + cl.MappingOverride = upd + } case string(globals.SshPrivateKeyCredentialType): if pl.UsernameAttribute != "" || pl.PrivateKeyAttribute != "" || pl.PrivateKeyPassphraseAttribute != "" { pk := allocSshPrivateKeyOverride() diff --git a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go index 7251fa3d58..3c785c55e9 100644 --- a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go +++ b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go @@ -52,6 +52,7 @@ const ( const ( usernameAttribute string = "username_attribute" passwordAttribute string = "password_attribute" + domainAttribute string = "domain_attribute" privateKeyAttribute string = "private_key_attribute" pkPassphraseAttribute string = "private_key_passphrase_attribute" ) @@ -80,6 +81,7 @@ var ( globals.UsernamePasswordCredentialType, globals.SshPrivateKeyCredentialType, globals.UnspecifiedCredentialType, + globals.UsernamePasswordDomainCredentialType, } validKeyTypes = []string{ @@ -760,6 +762,17 @@ func toProto(ctx context.Context, in credential.Library, opt ...handlers.Option) m[passwordAttribute] = mapping.PasswordAttribute } + case *vault.UsernamePasswordDomainOverride: + if mapping.UsernameAttribute != "" { + m[usernameAttribute] = mapping.UsernameAttribute + } + if mapping.PasswordAttribute != "" { + m[passwordAttribute] = mapping.PasswordAttribute + } + if mapping.DomainAttribute != "" { + m[domainAttribute] = mapping.DomainAttribute + } + case *vault.SshPrivateKeyOverride: if mapping.UsernameAttribute != "" { m[usernameAttribute] = mapping.UsernameAttribute @@ -884,6 +897,23 @@ func toStorageVaultLibrary(ctx context.Context, storeId string, in *pb.Credentia opts = append(opts, vault.WithMappingOverride(vault.NewUsernamePasswordOverride(mapOpts...))) } + case globals.UsernamePasswordDomainCredentialType: + opts = append(opts, vault.WithCredentialType(credentialType)) + overrides := in.CredentialMappingOverrides.AsMap() + var mapOpts []vault.Option + if username := overrides[usernameAttribute]; username != nil { + mapOpts = append(mapOpts, vault.WithOverrideUsernameAttribute(username.(string))) + } + if password := overrides[passwordAttribute]; password != nil { + mapOpts = append(mapOpts, vault.WithOverridePasswordAttribute(password.(string))) + } + if domain := overrides[domainAttribute]; domain != nil { + mapOpts = append(mapOpts, vault.WithOverrideDomainAttribute(domain.(string))) + } + if len(mapOpts) > 0 { + opts = append(opts, vault.WithMappingOverride(vault.NewUsernamePasswordDomainOverride(mapOpts...))) + } + case globals.SshPrivateKeyCredentialType: opts = append(opts, vault.WithCredentialType(credentialType)) overrides := in.CredentialMappingOverrides.AsMap() @@ -1183,6 +1213,10 @@ func validateMapping(badFields map[string]string, credentialType globals.Credent validFields[usernameAttribute] = true validFields[privateKeyAttribute] = true validFields[pkPassphraseAttribute] = true + case globals.UsernamePasswordDomainCredentialType: + validFields[usernameAttribute] = true + validFields[passwordAttribute] = true + validFields[domainAttribute] = true default: badFields[globals.CredentialTypeField] = fmt.Sprintf("Unknown credential type %q", credentialType) return @@ -1272,7 +1306,34 @@ func getMappingUpdates(credentialType globals.CredentialType, current vault.Mapp default: ret[passwordAttribute] = currentPass } + case globals.UsernamePasswordDomainCredentialType: + var currentUser, currentPass, currentDomain any + if overrides, ok := current.(*vault.UsernamePasswordDomainOverride); ok { + currentUser = overrides.UsernameAttribute + currentPass = overrides.PasswordAttribute + currentDomain = overrides.DomainAttribute + } + + switch { + case masks[usernameAttribute]: + ret[usernameAttribute] = new[usernameAttribute] + default: + ret[usernameAttribute] = currentUser + } + switch { + case masks[passwordAttribute]: + ret[passwordAttribute] = new[passwordAttribute] + default: + ret[passwordAttribute] = currentPass + } + + switch { + case masks[domainAttribute]: + ret[domainAttribute] = new[domainAttribute] + default: + ret[domainAttribute] = currentDomain + } case globals.SshPrivateKeyCredentialType: var currentUser, currentpPass, currentPk any if overrides, ok := current.(*vault.SshPrivateKeyOverride); ok { diff --git a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go index 139df50914..2c7b289c7a 100644 --- a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go +++ b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go @@ -561,6 +561,31 @@ func TestCreate(t *testing.T) { res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, + { + name: "Invalid username_password_domain 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.UsernamePasswordDomainCredentialType), + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: "user-test", + domainAttribute: "domain-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{ @@ -768,6 +793,196 @@ func TestCreate(t *testing.T) { }, }, }, + { + name: "Create a valid vault CredentialLibrary username_password_domain 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.UsernamePasswordDomainCredentialType), + }}, + 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.UsernamePasswordDomainCredentialType), + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Create a valid vault CredentialLibrary username_password_domain 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.UsernamePasswordDomainCredentialType), + }}, + 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.UsernamePasswordDomainCredentialType), + 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.UsernamePasswordDomainCredentialType), + }}, + 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.UsernamePasswordDomainCredentialType), + 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 username_password_domain type with username/password/domain 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", + domainAttribute: "domain-test", + } + ret, err := structpb.NewStruct(v) + require.NoError(t, err) + return ret + }(), + CredentialType: string(globals.UsernamePasswordDomainCredentialType), + }}, + 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.UsernamePasswordDomainCredentialType), + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: "user-test", + passwordAttribute: "pass-test", + domainAttribute: "domain-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{ @@ -967,10 +1182,24 @@ func TestGet(t *testing.T) { vault.WithOverrideUsernameAttribute("user"), vault.WithOverridePasswordAttribute("pass"), ))) + require.NoError(t, err) userPassLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib) require.NoError(t, err) + libDomain, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path", + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("user"), + vault.WithOverridePasswordAttribute("pass"), + vault.WithOverrideDomainAttribute("domain"), + ))) + + require.NoError(t, err) + userPassDomainLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), libDomain) + require.NoError(t, err) + lib1, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path/ssh", vault.WithCredentialType("ssh_private_key"), vault.WithMappingOverride( @@ -1048,6 +1277,39 @@ func TestGet(t *testing.T) { }, }, }, + { + name: "success-UsernamePasswordDomain", + id: userPassDomainLib.GetPublicId(), + res: &pbs.GetCredentialLibraryResponse{ + Item: &pb.CredentialLibrary{ + Id: userPassDomainLib.GetPublicId(), + CredentialStoreId: userPassDomainLib.GetStoreId(), + Scope: &scopepb.ScopeInfo{Id: store.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + Type: vault.GenericLibrarySubtype.String(), + AuthorizedActions: testAuthorizedActions, + CreatedTime: userPassDomainLib.CreateTime.GetTimestamp(), + UpdatedTime: userPassDomainLib.UpdateTime.GetTimestamp(), + Version: 1, + Attrs: &pb.CredentialLibrary_VaultGenericCredentialLibraryAttributes{ + VaultGenericCredentialLibraryAttributes: &pb.VaultCredentialLibraryAttributes{ + Path: wrapperspb.String(userPassDomainLib.GetVaultPath()), + HttpMethod: wrapperspb.String(userPassDomainLib.GetHttpMethod()), + }, + }, + CredentialType: "username_password_domain", + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: "user", + passwordAttribute: "pass", + domainAttribute: "domain", + } + ret, err := structpb.NewStruct(v) + require.NoError(t, err) + return ret + }(), + }, + }, + }, { name: "success-ssh-private-key", id: sshPkLib.GetPublicId(), @@ -1260,6 +1522,7 @@ func TestUpdate(t *testing.T) { usernameAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, usernameAttribute) passwordAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, passwordAttribute) + domainAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, domainAttribute) privateKeyAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, privateKeyAttribute) passphraseAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, pkPassphraseAttribute) @@ -1485,6 +1748,218 @@ func TestUpdate(t *testing.T) { return out }, }, + { + name: "username-password-domain-attributes-change-username-attribute", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + 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-domain-attributes-change-password-attribute", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + 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-domain-attributes-change-domain-attribute", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + req: &pbs.UpdateCredentialLibraryRequest{ + UpdateMask: fieldmask(domainAttrField), + Item: &pb.CredentialLibrary{ + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + domainAttribute: "changed-domain", + } + 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[domainAttribute] = structpb.NewStringValue("changed-domain") + return out + }, + }, + { + name: "username-password-domain-attributes-change-username-and-password-attributes", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + 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: "username-password-domain-attributes-change-username-password-and-domain-attributes", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + req: &pbs.UpdateCredentialLibraryRequest{ + UpdateMask: fieldmask(passwordAttrField, usernameAttrField, domainAttrField), + Item: &pb.CredentialLibrary{ + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: "changed-user", + passwordAttribute: "changed-pass", + domainAttribute: "changed-domain", + } + 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") + out.CredentialMappingOverrides.Fields[domainAttribute] = structpb.NewStringValue("changed-domain") + return out + }, + }, + { + name: "no-mapping-override-change-username-password-and-domain-attributes", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + }, + req: &pbs.UpdateCredentialLibraryRequest{ + UpdateMask: fieldmask(passwordAttrField, usernameAttrField, domainAttrField), + Item: &pb.CredentialLibrary{ + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: "new-user", + passwordAttribute: "new-pass", + domainAttribute: "new-domain", + } + 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", + domainAttribute: "new-domain", + } + var err error + out.CredentialMappingOverrides, err = structpb.NewStruct(v) + require.NoError(t, err) + return out + }, + }, + { + name: "username-password-domain-attributes-delete-mapping-override", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + 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{ @@ -1532,6 +2007,38 @@ func TestUpdate(t *testing.T) { return out }, }, + { + name: "username-password-attributes-delete-mapping-override-field-specific", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + vault.WithMappingOverride( + vault.NewUsernamePasswordDomainOverride( + vault.WithOverrideUsernameAttribute("orig-user"), + vault.WithOverridePasswordAttribute("orig-pass"), + vault.WithOverrideDomainAttribute("orig-domain"), + )), + }, + req: &pbs.UpdateCredentialLibraryRequest{ + UpdateMask: fieldmask(passwordAttrField, usernameAttrField, domainAttrField), + Item: &pb.CredentialLibrary{ + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: nil, + passwordAttribute: nil, + domainAttribute: 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{ @@ -1557,6 +2064,32 @@ func TestUpdate(t *testing.T) { return out }, }, + { + name: "no-mapping-override-delete-mapping-override-field-specific", + opts: []vault.Option{ + vault.WithCredentialType("username_password_domain"), + }, + req: &pbs.UpdateCredentialLibraryRequest{ + UpdateMask: fieldmask(passwordAttrField, usernameAttrField, domainAttrField), + Item: &pb.CredentialLibrary{ + CredentialMappingOverrides: func() *structpb.Struct { + v := map[string]any{ + usernameAttribute: nil, + passwordAttribute: nil, + domainAttribute: 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{ diff --git a/internal/daemon/controller/handlers/targets/credentials.go b/internal/daemon/controller/handlers/targets/credentials.go index 83b3295334..2fc4c539d4 100644 --- a/internal/daemon/controller/handlers/targets/credentials.go +++ b/internal/daemon/controller/handlers/targets/credentials.go @@ -102,6 +102,19 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (* credType = string(l.CredentialType()) switch c := cred.(type) { + case credential.UsernamePasswordDomain: + credData, err = handlers.ProtoToStruct( + ctx, + &pb.UsernamePasswordDomainCredential{ + Username: c.Username(), + Password: string(c.Password()), + Domain: c.Domain(), + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential")) + } + case credential.UsernamePassword: credData, err = handlers.ProtoToStruct( ctx,