feat(credential): Add mapping override update for API/SDK/CLI
This change adds support for updating a credential mapping override on a credential library.
This commit is contained in:
1 parent
da20a8b434
commit
e75f1d2f73
6 files changed
+405
-34
No files matched your search
@@ -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) {
|
||||
|
||||
+99
-14
@@ -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
|
||||
}
|
||||
+290
-13
@@ -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,21 +674,21 @@ func TestGet(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
id: vl.GetPublicId(),
|
||||
id: unspecifiedLib.GetPublicId(),
|
||||
res: &pbs.GetCredentialLibraryResponse{
|
||||
Item: &pb.CredentialLibrary{
|
||||
Id: vl.GetPublicId(),
|
||||
CredentialStoreId: vl.GetStoreId(),
|
||||
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: vl.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: vl.UpdateTime.GetTimestamp(),
|
||||
CreatedTime: unspecifiedLib.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: unspecifiedLib.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(unspecifiedLib.GetVaultPath()),
|
||||
HttpMethod: wrapperspb.String(unspecifiedLib.GetHttpMethod()),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return attrs
|
||||
@@ -682,6 +696,40 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success-userpassword",
|
||||
id: userPassLib.GetPublicId(),
|
||||
res: &pbs.GetCredentialLibraryResponse{
|
||||
Item: &pb.CredentialLibrary{
|
||||
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: userPassLib.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: userPassLib.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, err := handlers.ProtoToStruct(&pb.VaultCredentialLibraryAttributes{
|
||||
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
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", vault.CredentialLibraryPrefix),
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user