feat(credential): Add mapping override update for API/SDK/CLI

This change adds support for updating a credential mapping override on a
credential library.
pull/1796/head
Louis Ruch 5 years ago committed by Timothy Messier
parent da20a8b434
commit e75f1d2f73
No known key found for this signature in database
GPG Key ID: EFD2F184F7600572

@ -37,8 +37,13 @@ func extraVaultActionsFlagsMapFuncImpl() map[string][]string {
credentialTypeFlagName,
credentialMappingFlagName,
},
"update": {
pathFlagName,
httpMethodFlagName,
httpRequestBodyFlagName,
credentialMappingFlagName,
},
}
flags["update"] = flags["create"]
return flags
}
@ -122,7 +127,7 @@ func extraVaultFlagHandlingFuncImpl(c *VaultCommand, _ *base.FlagSets, opts *[]c
mappings := make(map[string]interface{}, len(c.flagCredentialMapping))
for _, mapping := range c.flagCredentialMapping {
switch {
case len(mapping.Keys) != 1 || mapping.Keys[0] == "":
case len(mapping.Keys) != 1 || mapping.Keys[0] == "" || mapping.Value == "":
// mapping override does not support key segments (e.g. 'x.y=z')
c.UI.Error("Credential mapping override must be in the format 'key=value', 'key=null' to clear field or 'null' to clear all.")
return false

@ -18,5 +18,7 @@ const (
tlsSkipVerifyField = "TlsSkipVerify"
tokenField = "Token"
mappingOverrideField = "MappingOverride"
// MappingOverrideField represents the field mask indicating a mapping override
// update has been requested.
MappingOverrideField = "MappingOverride"
)

@ -149,7 +149,7 @@ func (r *Repository) UpdateCredentialLibrary(ctx context.Context, scopeId string
case strings.EqualFold(vaultPathField, f):
case strings.EqualFold(httpMethodField, f):
case strings.EqualFold(httpRequestBodyField, f):
case strings.EqualFold(mappingOverrideField, f):
case strings.EqualFold(MappingOverrideField, f):
updateMappingOverride = true
default:
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
@ -163,7 +163,7 @@ func (r *Repository) UpdateCredentialLibrary(ctx context.Context, scopeId string
vaultPathField: l.VaultPath,
httpMethodField: l.HttpMethod,
httpRequestBodyField: l.HttpRequestBody,
mappingOverrideField: l.MappingOverride,
MappingOverrideField: l.MappingOverride,
},
fieldMaskPaths,
nil,
@ -198,14 +198,14 @@ func (r *Repository) UpdateCredentialLibrary(ctx context.Context, scopeId string
var filteredDbMask, filteredNullFields []string
for _, f := range dbMask {
switch {
case strings.EqualFold(mappingOverrideField, f):
case strings.EqualFold(MappingOverrideField, f):
default:
filteredDbMask = append(filteredDbMask, f)
}
}
for _, f := range nullFields {
switch {
case strings.EqualFold(mappingOverrideField, f):
case strings.EqualFold(MappingOverrideField, f):
default:
filteredNullFields = append(filteredNullFields, f)
}

@ -20,6 +20,8 @@ import (
"github.com/hashicorp/boundary/internal/servers/worker"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
_ "github.com/hashicorp/boundary/internal/servers/controller/handlers/targets/tcp"
)
func TestSetupSleepyDevEnvironment(t *testing.T) {

@ -21,14 +21,16 @@ import (
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
"github.com/hashicorp/go-secure-stdlib/strutil"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const (
vaultPathField = "attributes.path"
httpMethodField = "attributes.http_method"
httpRequestBodyField = "attributes.http_request_body"
vaultPathField = "attributes.path"
httpMethodField = "attributes.http_method"
httpRequestBodyField = "attributes.http_request_body"
credentialMappingPathField = "credential_mapping_overrides"
)
// Credential mapping override attributes
@ -227,14 +229,24 @@ func (s Service) CreateCredentialLibrary(ctx context.Context, req *pbs.CreateCre
func (s Service) UpdateCredentialLibrary(ctx context.Context, req *pbs.UpdateCredentialLibraryRequest) (*pbs.UpdateCredentialLibraryResponse, error) {
const op = "credentiallibraries.(Service).UpdateCredentialLibrary"
if err := validateUpdateRequest(req); err != nil {
repo, err := s.repoFn()
if err != nil {
return nil, err
}
cur, err := repo.LookupCredentialLibrary(ctx, req.Id)
if err != nil {
return nil, err
}
currentCredentialType := credential.Type(cur.GetCredentialType())
if err := validateUpdateRequest(req, currentCredentialType); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Update)
if authResults.Error != nil {
return nil, authResults.Error
}
cl, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
cl, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem(), currentCredentialType, cur.MappingOverride)
if err != nil {
return nil, err
}
@ -326,23 +338,46 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Cred
return out, nil
}
func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.CredentialLibrary) (credential.Library, error) {
func (s Service) updateInRepo(
ctx context.Context,
projId, id string,
masks []string,
in *pb.CredentialLibrary,
currentCredentialType credential.Type,
currentMapping vault.MappingOverride) (credential.Library, error) {
const op = "credentiallibraries.(Service).updateInRepo"
var dbMasks []string
item := proto.Clone(in).(*pb.CredentialLibrary)
item.CredentialType = string(currentCredentialType)
mapping, update := getMappingUpdates(currentCredentialType, currentMapping, item.GetCredentialMappingOverrides().AsMap(), masks)
if update {
// got mapping update, append mapping override db field mask
dbMasks = append(dbMasks, vault.MappingOverrideField)
mappingStruct, err := structpb.NewStruct(mapping)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
item.CredentialMappingOverrides = mappingStruct
}
cl, err := toStorageVaultLibrary(item.GetCredentialStoreId(), item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cl.PublicId = id
dbMask := maskManager.Translate(mask)
if len(dbMask) == 0 {
dbMasks = append(dbMasks, maskManager.Translate(masks)...)
if len(dbMasks) == 0 {
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
}
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, rowsUpdated, err := repo.UpdateCredentialLibrary(ctx, projId, cl, item.GetVersion(), dbMask)
out, rowsUpdated, err := repo.UpdateCredentialLibrary(ctx, projId, cl, item.GetVersion(), dbMasks)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential library"))
}
@ -596,7 +631,7 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
if b := attrs.GetHttpRequestBody(); b != nil && strings.ToUpper(attrs.GetHttpMethod().GetValue()) != "POST" {
badFields[httpRequestBodyField] = fmt.Sprintf("Field can only be set if %q is set to the value 'POST'.", httpMethodField)
}
validateMapping(badFields, req.GetItem().GetCredentialType(), req.GetItem().CredentialMappingOverrides.AsMap())
validateMapping(badFields, credential.Type(req.GetItem().GetCredentialType()), req.GetItem().CredentialMappingOverrides.AsMap())
default:
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
}
@ -604,7 +639,7 @@ func validateCreateRequest(req *pbs.CreateCredentialLibraryRequest) error {
})
}
func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest) error {
func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest, currentCredentialType credential.Type) error {
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
switch credential.SubtypeFromId(req.GetId()) {
@ -612,6 +647,9 @@ func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest) error {
if req.GetItem().GetType() != "" && credential.SubtypeFromType(req.GetItem().GetType()) != vault.Subtype {
badFields[globals.TypeField] = "Cannot modify resource type."
}
if req.GetItem().GetCredentialType() != "" && req.GetItem().GetCredentialType() != string(currentCredentialType) {
badFields[globals.CredentialTypeField] = "Cannot modify credential type."
}
attrs := &pb.VaultCredentialLibraryAttributes{}
if err := handlers.StructToProto(req.GetItem().GetAttributes(), attrs); err != nil {
badFields[globals.AttributesField] = "Attribute fields do not match the expected format."
@ -626,6 +664,7 @@ func validateUpdateRequest(req *pbs.UpdateCredentialLibraryRequest) error {
if b := attrs.GetHttpRequestBody(); b != nil && strings.ToUpper(attrs.GetHttpMethod().GetValue()) == "GET" {
badFields[httpRequestBodyField] = fmt.Sprintf("Field can only be set if %q is set to the value 'POST'.", httpMethodField)
}
validateMapping(badFields, currentCredentialType, req.GetItem().CredentialMappingOverrides.AsMap())
}
return badFields
}, vault.CredentialLibraryPrefix)
@ -649,10 +688,10 @@ func validateListRequest(req *pbs.ListCredentialLibrariesRequest) error {
return nil
}
func validateMapping(badFields map[string]string, credentialType string, overrides map[string]interface{}) {
func validateMapping(badFields map[string]string, credentialType credential.Type, overrides map[string]interface{}) {
validFields := make(map[string]bool)
switch credential.Type(credentialType) {
case "":
switch credentialType {
case "", credential.UnspecifiedType:
if len(overrides) > 0 {
badFields[globals.CredentialMappingOverridesField] = fmt.Sprintf("This field can only be set if %q is set", globals.CredentialTypeField)
}
@ -675,3 +714,49 @@ func validateMapping(badFields map[string]string, credentialType string, overrid
}
}
}
func getMappingUpdates(credentialType credential.Type, current vault.MappingOverride, new map[string]interface{}, apiMasks []string) (map[string]interface{}, bool) {
ret := make(map[string]interface{})
masks := make(map[string]bool)
for _, m := range apiMasks {
if m == credentialMappingPathField {
// got top level credential mapping change request, this mask
// can only be provided when clearing the entire override.
return nil, true
}
credMappingPrefix := fmt.Sprintf("%v.", credentialMappingPathField)
if s := strings.SplitN(m, credMappingPrefix, 2); len(s) == 2 {
masks[s[1]] = true
}
}
if len(masks) == 0 {
// no mapping updates
return nil, false
}
switch credentialType {
case credential.UserPasswordType:
var currentUser, currentPass interface{}
if overrides, ok := current.(*vault.UserPasswordOverride); ok {
currentUser = overrides.UsernameAttribute
currentPass = overrides.PasswordAttribute
}
switch {
case masks[usernameAttribute]:
ret[usernameAttribute] = new[usernameAttribute]
default:
ret[usernameAttribute] = currentUser
}
switch {
case masks[passwordAttribute]:
ret[passwordAttribute] = new[passwordAttribute]
default:
ret[passwordAttribute] = currentPass
}
}
return ret, true
}

@ -1,6 +1,7 @@
package credentiallibraries
import (
"context"
"fmt"
"sort"
"strings"
@ -648,10 +649,23 @@ func TestGet(t *testing.T) {
_, 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]
unspecifiedLib := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
s, err := NewService(repoFn, iamRepoFn)
require.NoError(t, err)
repo, err := repoFn()
require.NoError(t, err)
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("user"),
vault.WithOverridePasswordAttribute("pass"),
)))
require.NoError(t, err)
userPassLib, err := repo.CreateCredentialLibrary(context.Background(), prj.GetPublicId(), lib)
require.NoError(t, err)
cases := []struct {
name string
id string
@ -660,25 +674,59 @@ func TestGet(t *testing.T) {
}{
{
name: "success",
id: vl.GetPublicId(),
id: unspecifiedLib.GetPublicId(),
res: &pbs.GetCredentialLibraryResponse{
Item: &pb.CredentialLibrary{
Id: unspecifiedLib.GetPublicId(),
CredentialStoreId: unspecifiedLib.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: unspecifiedLib.CreateTime.GetTimestamp(),
UpdatedTime: unspecifiedLib.UpdateTime.GetTimestamp(),
Version: 1,
Attributes: func() *structpb.Struct {
attrs, err := handlers.ProtoToStruct(&pb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(unspecifiedLib.GetVaultPath()),
HttpMethod: wrapperspb.String(unspecifiedLib.GetHttpMethod()),
})
require.NoError(t, err)
return attrs
}(),
},
},
},
{
name: "success-userpassword",
id: userPassLib.GetPublicId(),
res: &pbs.GetCredentialLibraryResponse{
Item: &pb.CredentialLibrary{
Id: vl.GetPublicId(),
CredentialStoreId: vl.GetStoreId(),
Id: userPassLib.GetPublicId(),
CredentialStoreId: userPassLib.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: vl.CreateTime.GetTimestamp(),
UpdatedTime: vl.UpdateTime.GetTimestamp(),
CreatedTime: userPassLib.CreateTime.GetTimestamp(),
UpdatedTime: userPassLib.UpdateTime.GetTimestamp(),
Version: 1,
Attributes: func() *structpb.Struct {
attrs, err := handlers.ProtoToStruct(&pb.VaultCredentialLibraryAttributes{
Path: wrapperspb.String(vl.GetVaultPath()),
HttpMethod: wrapperspb.String(vl.GetHttpMethod()),
Path: wrapperspb.String(userPassLib.GetVaultPath()),
HttpMethod: wrapperspb.String(userPassLib.GetHttpMethod()),
})
require.NoError(t, err)
return attrs
}(),
CredentialType: "user_password",
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user",
passwordAttribute: "pass",
}
ret, err := structpb.NewStruct(v)
require.NoError(t, err)
return ret
}(),
},
},
},
@ -799,8 +847,14 @@ func TestUpdate(t *testing.T) {
cs := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)
store, diffStore := cs[0], cs[1]
freshLibrary := func() (*vault.CredentialLibrary, func()) {
vl := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 1)[0]
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)
@ -816,8 +870,12 @@ func TestUpdate(t *testing.T) {
_, token := v.CreateToken(t)
_ = token
usernameAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, usernameAttribute)
passwordAttrField := fmt.Sprintf("%v.%v", credentialMappingPathField, passwordAttribute)
successCases := []struct {
name string
opts []vault.Option
req *pbs.UpdateCredentialLibraryRequest
res func(*pb.CredentialLibrary) *pb.CredentialLibrary
}{
@ -853,7 +911,7 @@ func TestUpdate(t *testing.T) {
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.Attributes.Fields["path"] = structpb.NewStringValue("vault/path0")
out.Attributes.Fields["path"] = structpb.NewStringValue("vault/path")
out.Attributes.Fields["http_method"] = structpb.NewStringValue("POST")
return out
},
@ -875,7 +933,7 @@ func TestUpdate(t *testing.T) {
},
res: func(in *pb.CredentialLibrary) *pb.CredentialLibrary {
out := proto.Clone(in).(*pb.CredentialLibrary)
out.Attributes.Fields["path"] = structpb.NewStringValue("vault/path0")
out.Attributes.Fields["path"] = structpb.NewStringValue("vault/path")
out.Attributes.Fields["http_method"] = structpb.NewStringValue("POST")
out.Attributes.Fields["http_request_body"] = structpb.NewStringValue("body")
return out
@ -901,12 +959,226 @@ func TestUpdate(t *testing.T) {
return out
},
},
{
name: "user-password-attributes-change-username-attribute",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePasswordAttribute("orig-pass"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
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: "user-password-attributes-change-password-attribute",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
vault.WithOverridePasswordAttribute("orig-pass"),
)),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
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: "user-password-attributes-change-username-and-password-attributes",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
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]interface{}{
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("user_password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
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]interface{}{
usernameAttribute: "new-user",
passwordAttribute: "new-pass",
}
var err error
out.CredentialMappingOverrides, err = structpb.NewStruct(v)
require.NoError(t, err)
return out
},
},
{
name: "user-password-attributes-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
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("user_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: "user-password-attributes-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
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]interface{}{
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("user_password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
Item: &pb.CredentialLibrary{
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
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
},
},
}
for _, tc := range successCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
st, cleanup := freshLibrary()
st, cleanup := freshLibrary(tc.opts...)
defer cleanup()
if tc.req.Item.GetVersion() == 0 {
@ -969,6 +1241,11 @@ func TestUpdate(t *testing.T) {
path: "authorized actions",
item: &pb.CredentialLibrary{AuthorizedActions: append(testAuthorizedActions, "another")},
},
{
name: "read only credential type",
path: "credential_type",
item: &pb.CredentialLibrary{CredentialType: string(credential.UserPasswordType)},
},
}
for _, tc := range errCases {
t.Run(tc.name, func(t *testing.T) {

Loading…
Cancel
Save