Add Custom metadata field to alias (#12502)

* adding changes

* removing q.Q

* removing empty lines

* testing

* checking tests

* fixing tests

* adding changes

* added requested changes

* added requested changes

* added policy templating changes and fixed tests

* adding proto changes

* making changes

* adding unit tests

* using suggested function
This commit is contained in:
akshya96
2021-09-17 11:03:47 -07:00
committed by GitHub
parent e2e4b50e87
commit d324066040
21 changed files with 335 additions and 112 deletions

3
changelog/12502.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:feature
core: adds custom_metadata field for aliases
```

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: helper/forwarding/types.proto // source: helper/forwarding/types.proto

View File

@@ -75,12 +75,13 @@ func ToSDKAlias(a *Alias) *logical.Alias {
} }
return &logical.Alias{ return &logical.Alias{
Name: a.Name, Name: a.Name,
ID: a.ID, ID: a.ID,
MountAccessor: a.MountAccessor, MountAccessor: a.MountAccessor,
MountType: a.MountType, MountType: a.MountType,
Metadata: metadata, Metadata: metadata,
NamespaceID: a.NamespaceID, NamespaceID: a.NamespaceID,
CustomMetadata: a.CustomMetadata,
} }
} }

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: helper/identity/mfa/types.proto // source: helper/identity/mfa/types.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: helper/identity/types.proto // source: helper/identity/types.proto
@@ -405,6 +405,8 @@ type Alias struct {
// NamespaceID is the identifier of the namespace to which this alias // NamespaceID is the identifier of the namespace to which this alias
// belongs. // belongs.
NamespaceID string `sentinel:"" protobuf:"bytes,11,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` NamespaceID string `sentinel:"" protobuf:"bytes,11,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"`
// Custom Metadata represents the custom data tied to this alias
CustomMetadata map[string]string `sentinel:"" protobuf:"bytes,12,rep,name=customMetadata,proto3" json:"customMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
} }
func (x *Alias) Reset() { func (x *Alias) Reset() {
@@ -516,6 +518,13 @@ func (x *Alias) GetNamespaceID() string {
return "" return ""
} }
func (x *Alias) GetCustomMetadata() map[string]string {
if x != nil {
return x.CustomMetadata
}
return nil
}
// Deprecated. Retained for backwards compatibility. // Deprecated. Retained for backwards compatibility.
type EntityStorageEntry struct { type EntityStorageEntry struct {
state protoimpl.MessageState state protoimpl.MessageState
@@ -842,7 +851,7 @@ var file_helper_identity_types_proto_rawDesc = []byte{
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x21, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x21, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x0b, 0x2e, 0x6d, 0x66, 0x61, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x0b, 0x2e, 0x6d, 0x66, 0x61, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x90, 0x04, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa0, 0x05, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61,
0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63,
@@ -871,8 +880,17 @@ var file_helper_identity_types_proto_rawDesc = []byte{
0x28, 0x09, 0x52, 0x16, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x61, 0x28, 0x09, 0x52, 0x16, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x61,
0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x1a, 0x3b, 0x0a, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x4b, 0x0a,
0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74,
0x6f, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x43, 0x75, 0x73, 0x74, 0x6f,
0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x05, 0x0a, 0x12, 0x45, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x05, 0x0a, 0x12, 0x45,
@@ -966,7 +984,7 @@ func file_helper_identity_types_proto_rawDescGZIP() []byte {
return file_helper_identity_types_proto_rawDescData return file_helper_identity_types_proto_rawDescData
} }
var file_helper_identity_types_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_helper_identity_types_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_helper_identity_types_proto_goTypes = []interface{}{ var file_helper_identity_types_proto_goTypes = []interface{}{
(*Group)(nil), // 0: identity.Group (*Group)(nil), // 0: identity.Group
(*Entity)(nil), // 1: identity.Entity (*Entity)(nil), // 1: identity.Entity
@@ -977,40 +995,42 @@ var file_helper_identity_types_proto_goTypes = []interface{}{
nil, // 6: identity.Entity.MetadataEntry nil, // 6: identity.Entity.MetadataEntry
nil, // 7: identity.Entity.MFASecretsEntry nil, // 7: identity.Entity.MFASecretsEntry
nil, // 8: identity.Alias.MetadataEntry nil, // 8: identity.Alias.MetadataEntry
nil, // 9: identity.EntityStorageEntry.MetadataEntry nil, // 9: identity.Alias.CustomMetadataEntry
nil, // 10: identity.EntityStorageEntry.MFASecretsEntry nil, // 10: identity.EntityStorageEntry.MetadataEntry
nil, // 11: identity.PersonaIndexEntry.MetadataEntry nil, // 11: identity.EntityStorageEntry.MFASecretsEntry
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp nil, // 12: identity.PersonaIndexEntry.MetadataEntry
(*mfa.Secret)(nil), // 13: mfa.Secret (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp
(*mfa.Secret)(nil), // 14: mfa.Secret
} }
var file_helper_identity_types_proto_depIDxs = []int32{ var file_helper_identity_types_proto_depIDxs = []int32{
5, // 0: identity.Group.metadata:type_name -> identity.Group.MetadataEntry 5, // 0: identity.Group.metadata:type_name -> identity.Group.MetadataEntry
12, // 1: identity.Group.creation_time:type_name -> google.protobuf.Timestamp 13, // 1: identity.Group.creation_time:type_name -> google.protobuf.Timestamp
12, // 2: identity.Group.last_update_time:type_name -> google.protobuf.Timestamp 13, // 2: identity.Group.last_update_time:type_name -> google.protobuf.Timestamp
2, // 3: identity.Group.alias:type_name -> identity.Alias 2, // 3: identity.Group.alias:type_name -> identity.Alias
2, // 4: identity.Entity.aliases:type_name -> identity.Alias 2, // 4: identity.Entity.aliases:type_name -> identity.Alias
6, // 5: identity.Entity.metadata:type_name -> identity.Entity.MetadataEntry 6, // 5: identity.Entity.metadata:type_name -> identity.Entity.MetadataEntry
12, // 6: identity.Entity.creation_time:type_name -> google.protobuf.Timestamp 13, // 6: identity.Entity.creation_time:type_name -> google.protobuf.Timestamp
12, // 7: identity.Entity.last_update_time:type_name -> google.protobuf.Timestamp 13, // 7: identity.Entity.last_update_time:type_name -> google.protobuf.Timestamp
7, // 8: identity.Entity.mfa_secrets:type_name -> identity.Entity.MFASecretsEntry 7, // 8: identity.Entity.mfa_secrets:type_name -> identity.Entity.MFASecretsEntry
8, // 9: identity.Alias.metadata:type_name -> identity.Alias.MetadataEntry 8, // 9: identity.Alias.metadata:type_name -> identity.Alias.MetadataEntry
12, // 10: identity.Alias.creation_time:type_name -> google.protobuf.Timestamp 13, // 10: identity.Alias.creation_time:type_name -> google.protobuf.Timestamp
12, // 11: identity.Alias.last_update_time:type_name -> google.protobuf.Timestamp 13, // 11: identity.Alias.last_update_time:type_name -> google.protobuf.Timestamp
4, // 12: identity.EntityStorageEntry.personas:type_name -> identity.PersonaIndexEntry 9, // 12: identity.Alias.customMetadata:type_name -> identity.Alias.CustomMetadataEntry
9, // 13: identity.EntityStorageEntry.metadata:type_name -> identity.EntityStorageEntry.MetadataEntry 4, // 13: identity.EntityStorageEntry.personas:type_name -> identity.PersonaIndexEntry
12, // 14: identity.EntityStorageEntry.creation_time:type_name -> google.protobuf.Timestamp 10, // 14: identity.EntityStorageEntry.metadata:type_name -> identity.EntityStorageEntry.MetadataEntry
12, // 15: identity.EntityStorageEntry.last_update_time:type_name -> google.protobuf.Timestamp 13, // 15: identity.EntityStorageEntry.creation_time:type_name -> google.protobuf.Timestamp
10, // 16: identity.EntityStorageEntry.mfa_secrets:type_name -> identity.EntityStorageEntry.MFASecretsEntry 13, // 16: identity.EntityStorageEntry.last_update_time:type_name -> google.protobuf.Timestamp
11, // 17: identity.PersonaIndexEntry.metadata:type_name -> identity.PersonaIndexEntry.MetadataEntry 11, // 17: identity.EntityStorageEntry.mfa_secrets:type_name -> identity.EntityStorageEntry.MFASecretsEntry
12, // 18: identity.PersonaIndexEntry.creation_time:type_name -> google.protobuf.Timestamp 12, // 18: identity.PersonaIndexEntry.metadata:type_name -> identity.PersonaIndexEntry.MetadataEntry
12, // 19: identity.PersonaIndexEntry.last_update_time:type_name -> google.protobuf.Timestamp 13, // 19: identity.PersonaIndexEntry.creation_time:type_name -> google.protobuf.Timestamp
13, // 20: identity.Entity.MFASecretsEntry.value:type_name -> mfa.Secret 13, // 20: identity.PersonaIndexEntry.last_update_time:type_name -> google.protobuf.Timestamp
13, // 21: identity.EntityStorageEntry.MFASecretsEntry.value:type_name -> mfa.Secret 14, // 21: identity.Entity.MFASecretsEntry.value:type_name -> mfa.Secret
22, // [22:22] is the sub-list for method output_type 14, // 22: identity.EntityStorageEntry.MFASecretsEntry.value:type_name -> mfa.Secret
22, // [22:22] is the sub-list for method input_type 23, // [23:23] is the sub-list for method output_type
22, // [22:22] is the sub-list for extension type_name 23, // [23:23] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension extendee 23, // [23:23] is the sub-list for extension type_name
0, // [0:22] is the sub-list for field type_name 23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
} }
func init() { file_helper_identity_types_proto_init() } func init() { file_helper_identity_types_proto_init() }
@@ -1086,7 +1106,7 @@ func file_helper_identity_types_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_helper_identity_types_proto_rawDesc, RawDescriptor: file_helper_identity_types_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 12, NumMessages: 13,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -172,6 +172,9 @@ message Alias {
// NamespaceID is the identifier of the namespace to which this alias // NamespaceID is the identifier of the namespace to which this alias
// belongs. // belongs.
string namespace_id = 11; string namespace_id = 11;
// Custom Metadata represents the custom data tied to this alias
map<string, string> customMetadata = 12;
} }
// Deprecated. Retained for backwards compatibility. // Deprecated. Retained for backwards compatibility.

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: helper/storagepacker/types.proto // source: helper/storagepacker/types.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: physical/raft/types.proto // source: physical/raft/types.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: sdk/database/dbplugin/database.proto // source: sdk/database/dbplugin/database.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: sdk/database/dbplugin/v5/proto/database.proto // source: sdk/database/dbplugin/v5/proto/database.proto

View File

@@ -178,6 +178,15 @@ func performTemplating(input string, p *PopulateStringInput) (string, error) {
case strings.HasPrefix(trimmed, "metadata."): case strings.HasPrefix(trimmed, "metadata."):
split := strings.SplitN(trimmed, ".", 2) split := strings.SplitN(trimmed, ".", 2)
return p.templateHandler(alias.Metadata, split[1]) return p.templateHandler(alias.Metadata, split[1])
case trimmed == "custom_metadata":
return p.templateHandler(alias.CustomMetadata)
case strings.HasPrefix(trimmed, "custom_metadata."):
split := strings.SplitN(trimmed, ".", 2)
return p.templateHandler(alias.CustomMetadata, split[1])
} }
return "", ErrTemplateValueNotFound return "", ErrTemplateValueNotFound
@@ -222,7 +231,7 @@ func performTemplating(input string, p *PopulateStringInput) (string, error) {
} }
// An empty alias is sufficient for generating defaults // An empty alias is sufficient for generating defaults
alias = &logical.Alias{Metadata: make(map[string]string)} alias = &logical.Alias{Metadata: make(map[string]string), CustomMetadata: make(map[string]string)}
} }
return performAliasTemplating(split[1], alias) return performAliasTemplating(split[1], alias)
} }

View File

@@ -17,23 +17,24 @@ var testNow = time.Now().Add(100 * time.Hour)
func TestPopulate_Basic(t *testing.T) { func TestPopulate_Basic(t *testing.T) {
tests := []struct { tests := []struct {
mode int mode int
name string name string
input string input string
output string output string
err error err error
entityName string entityName string
metadata map[string]string metadata map[string]string
aliasAccessor string aliasAccessor string
aliasID string aliasID string
aliasName string aliasName string
nilEntity bool nilEntity bool
validityCheckOnly bool validityCheckOnly bool
aliasMetadata map[string]string aliasMetadata map[string]string
groupName string aliasCustomMetadata map[string]string
groupMetadata map[string]string groupName string
groupMemberships []string groupMetadata map[string]string
now time.Time groupMemberships []string
now time.Time
}{ }{
// time.* tests. Keep tests with time.Now() at the front to avoid false // time.* tests. Keep tests with time.Now() at the front to avoid false
// positives due to the second changing during the test // positives due to the second changing during the test
@@ -329,6 +330,53 @@ func TestPopulate_Basic(t *testing.T) {
aliasMetadata: map[string]string{"foo": "bar", "color": "green"}, aliasMetadata: map[string]string{"foo": "bar", "color": "green"},
output: `{}`, output: `{}`,
}, },
{
mode: JSONTemplating,
name: "one alias custom metadata key",
input: "{{identity.entity.aliases.aws_123.custom_metadata.foo}}",
aliasAccessor: "aws_123",
aliasCustomMetadata: map[string]string{"foo": "abc", "bar": "123"},
output: `"abc"`,
},
{
mode: JSONTemplating,
name: "one alias custom metadata key not found",
input: "{{identity.entity.aliases.aws_123.custom_metadata.size}}",
aliasAccessor: "aws_123",
aliasCustomMetadata: map[string]string{"foo": "abc", "bar": "123"},
output: `""`,
},
{
mode: JSONTemplating,
name: "one alias custom metadata, accessor not found",
input: "{{identity.entity.aliases.aws_123.custom_metadata.size}}",
aliasAccessor: "not_gonna_match",
aliasCustomMetadata: map[string]string{"foo": "abc", "bar": "123"},
output: `""`,
},
{
mode: JSONTemplating,
name: "all alias custom metadata",
input: "{{identity.entity.aliases.aws_123.custom_metadata}}",
aliasAccessor: "aws_123",
aliasCustomMetadata: map[string]string{"foo": "abc", "bar": "123"},
output: `{"bar":"123","foo":"abc"}`,
},
{
mode: JSONTemplating,
name: "null alias custom metadata",
input: "{{identity.entity.aliases.aws_123.custom_metadata}}",
aliasAccessor: "aws_123",
output: `{}`,
},
{
mode: JSONTemplating,
name: "all alias custom metadata, accessor not found",
input: "{{identity.entity.aliases.aws_123.custom_metadata}}",
aliasAccessor: "not_gonna_match",
aliasCustomMetadata: map[string]string{"foo": "abc", "bar": "123"},
output: `{}`,
},
} }
for _, test := range tests { for _, test := range tests {
@@ -343,10 +391,11 @@ func TestPopulate_Basic(t *testing.T) {
if test.aliasAccessor != "" { if test.aliasAccessor != "" {
entity.Aliases = []*logical.Alias{ entity.Aliases = []*logical.Alias{
{ {
MountAccessor: test.aliasAccessor, MountAccessor: test.aliasAccessor,
ID: test.aliasID, ID: test.aliasID,
Name: test.aliasName, Name: test.aliasName,
Metadata: test.aliasMetadata, Metadata: test.aliasMetadata,
CustomMetadata: test.aliasCustomMetadata,
}, },
} }
} }
@@ -436,6 +485,10 @@ func TestPopulate_FullObject(t *testing.T) {
"service": "ec2", "service": "ec2",
"region": "west", "region": "west",
}, },
CustomMetadata: map[string]string{
"foo": "abc",
"bar": "123",
},
}, },
}, },
} }
@@ -458,7 +511,11 @@ func TestPopulate_FullObject(t *testing.T) {
"one not found alias metadata key": {{identity.entity.aliases.blahblah.metadata.service}}, "one not found alias metadata key": {{identity.entity.aliases.blahblah.metadata.service}},
"group names": {{identity.entity.groups.names}}, "group names": {{identity.entity.groups.names}},
"group ids": {{identity.entity.groups.ids}}, "group ids": {{identity.entity.groups.ids}},
"repeated and": {"nested element": {{identity.entity.name}}} "repeated and": {"nested element": {{identity.entity.name}}},
"alias custom metadata": {{identity.entity.aliases.aws_123.custom_metadata}},
"alias not found custom metadata": {{identity.entity.aliases.blahblah.custom_metadata}},
"one alias custom metadata key": {{identity.entity.aliases.aws_123.custom_metadata.foo}},
"one not found alias custom metadata key": {{identity.entity.aliases.blahblah.custom_metadata.foo}},
}` }`
expected := ` expected := `
@@ -474,7 +531,11 @@ func TestPopulate_FullObject(t *testing.T) {
"one not found alias metadata key": "", "one not found alias metadata key": "",
"group names": ["g1","g2"], "group names": ["g1","g2"],
"group ids": ["a08b0c02","239bef91"], "group ids": ["a08b0c02","239bef91"],
"repeated and": {"nested element": "Entity Name"} "repeated and": {"nested element": "Entity Name"},
"alias custom metadata": {"bar":"123","foo":"abc"},
"alias not found custom metadata": {},
"one alias custom metadata key": "abc",
"one not found alias custom metadata key": "",
}` }`
input := PopulateStringInput{ input := PopulateStringInput{

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: sdk/logical/identity.proto // source: sdk/logical/identity.proto
@@ -138,6 +138,8 @@ type Alias struct {
// NamespaceID is the identifier of the namespace to which this alias // NamespaceID is the identifier of the namespace to which this alias
// belongs. // belongs.
NamespaceID string `sentinel:"" protobuf:"bytes,6,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` NamespaceID string `sentinel:"" protobuf:"bytes,6,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"`
// Custom Metadata represents the custom data tied to this alias
CustomMetadata map[string]string `sentinel:"" protobuf:"bytes,7,rep,name=custom_metadata,json=customMetadata,proto3" json:"custom_metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
} }
func (x *Alias) Reset() { func (x *Alias) Reset() {
@@ -214,6 +216,13 @@ func (x *Alias) GetNamespaceID() string {
return "" return ""
} }
func (x *Alias) GetCustomMetadata() map[string]string {
if x != nil {
return x.CustomMetadata
}
return nil
}
type Group struct { type Group struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -312,7 +321,7 @@ var file_sdk_logical_identity_proto_rawDesc = []byte{
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8b, 0x02, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x03, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12,
0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25,
0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72,
@@ -325,7 +334,16 @@ var file_sdk_logical_identity_proto_rawDesc = []byte{
0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x49, 0x44, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x02, 0x49, 0x44, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73,
0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x22, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x2e,
0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64,
0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x1a, 0x41, 0x0a, 0x13, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
@@ -359,25 +377,27 @@ func file_sdk_logical_identity_proto_rawDescGZIP() []byte {
return file_sdk_logical_identity_proto_rawDescData return file_sdk_logical_identity_proto_rawDescData
} }
var file_sdk_logical_identity_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_sdk_logical_identity_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_sdk_logical_identity_proto_goTypes = []interface{}{ var file_sdk_logical_identity_proto_goTypes = []interface{}{
(*Entity)(nil), // 0: logical.Entity (*Entity)(nil), // 0: logical.Entity
(*Alias)(nil), // 1: logical.Alias (*Alias)(nil), // 1: logical.Alias
(*Group)(nil), // 2: logical.Group (*Group)(nil), // 2: logical.Group
nil, // 3: logical.Entity.MetadataEntry nil, // 3: logical.Entity.MetadataEntry
nil, // 4: logical.Alias.MetadataEntry nil, // 4: logical.Alias.MetadataEntry
nil, // 5: logical.Group.MetadataEntry nil, // 5: logical.Alias.CustomMetadataEntry
nil, // 6: logical.Group.MetadataEntry
} }
var file_sdk_logical_identity_proto_depIDxs = []int32{ var file_sdk_logical_identity_proto_depIDxs = []int32{
1, // 0: logical.Entity.aliases:type_name -> logical.Alias 1, // 0: logical.Entity.aliases:type_name -> logical.Alias
3, // 1: logical.Entity.metadata:type_name -> logical.Entity.MetadataEntry 3, // 1: logical.Entity.metadata:type_name -> logical.Entity.MetadataEntry
4, // 2: logical.Alias.metadata:type_name -> logical.Alias.MetadataEntry 4, // 2: logical.Alias.metadata:type_name -> logical.Alias.MetadataEntry
5, // 3: logical.Group.metadata:type_name -> logical.Group.MetadataEntry 5, // 3: logical.Alias.custom_metadata:type_name -> logical.Alias.CustomMetadataEntry
4, // [4:4] is the sub-list for method output_type 6, // 4: logical.Group.metadata:type_name -> logical.Group.MetadataEntry
4, // [4:4] is the sub-list for method input_type 5, // [5:5] is the sub-list for method output_type
4, // [4:4] is the sub-list for extension type_name 5, // [5:5] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension type_name
0, // [0:4] is the sub-list for field type_name 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
} }
func init() { file_sdk_logical_identity_proto_init() } func init() { file_sdk_logical_identity_proto_init() }
@@ -429,7 +449,7 @@ func file_sdk_logical_identity_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_sdk_logical_identity_proto_rawDesc, RawDescriptor: file_sdk_logical_identity_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 6, NumMessages: 7,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -50,6 +50,9 @@ message Alias {
// NamespaceID is the identifier of the namespace to which this alias // NamespaceID is the identifier of the namespace to which this alias
// belongs. // belongs.
string namespace_id = 6; string namespace_id = 6;
// Custom Metadata represents the custom data tied to this alias
map<string, string> custom_metadata = 7;
} }
message Group { message Group {

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: sdk/logical/plugin.proto // source: sdk/logical/plugin.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: sdk/plugin/pb/backend.proto // source: sdk/plugin/pb/backend.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: vault/activity/activity_log.proto // source: vault/activity/activity_log.proto

View File

@@ -6,13 +6,21 @@ import (
"strings" "strings"
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/helper/identity" "github.com/hashicorp/vault/helper/identity"
"github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/helper/storagepacker"
"github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/logical"
"github.com/mitchellh/mapstructure"
) )
const maxCustomMetadataKeys = 64
const maxCustomMetadataKeyLength = 128
const maxCustomMetadataValueLength = 512
const customMetadataValidationErrorPrefix = "custom_metadata validation failed"
// aliasPaths returns the API endpoints to operate on aliases. // aliasPaths returns the API endpoints to operate on aliases.
// Following are the paths supported: // Following are the paths supported:
// entity-alias - To register/modify an alias // entity-alias - To register/modify an alias
@@ -44,6 +52,10 @@ This field is deprecated, use canonical_id.`,
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the alias; unused for a modify", Description: "Name of the alias; unused for a modify",
}, },
"custom_metadata": {
Type: framework.TypeKVPairs,
Description: "User provided key-value pairs",
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: i.handleAliasCreateUpdate(), logical.UpdateOperation: i.handleAliasCreateUpdate(),
@@ -77,6 +89,10 @@ This field is deprecated, use canonical_id.`,
Type: framework.TypeString, Type: framework.TypeString,
Description: "(Unused)", Description: "(Unused)",
}, },
"custom_metadata": {
Type: framework.TypeKVPairs,
Description: "User provided key-value pairs",
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: i.handleAliasCreateUpdate(), logical.UpdateOperation: i.handleAliasCreateUpdate(),
@@ -118,6 +134,16 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
// Get ID, if any // Get ID, if any
id := d.Get("id").(string) id := d.Get("id").(string)
// Get custom metadata, if any
customMetadata := make(map[string]string)
data, customMetadataExists := d.GetOk("custom_metadata")
if customMetadataExists {
err = mapstructure.Decode(data, &customMetadata)
if err != nil {
return nil, err
}
}
// Get entity id // Get entity id
canonicalID := d.Get("canonical_id").(string) canonicalID := d.Get("canonical_id").(string)
if canonicalID == "" { if canonicalID == "" {
@@ -125,6 +151,15 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
canonicalID = d.Get("entity_id").(string) canonicalID = d.Get("entity_id").(string)
} }
//validate customMetadata if provided
if len(customMetadata) != 0 {
err := validateCustomMetadata(customMetadata)
if err != nil {
return nil, err
}
}
i.lock.Lock() i.lock.Lock()
defer i.lock.Unlock() defer i.lock.Unlock()
@@ -143,31 +178,31 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
if alias.NamespaceID != ns.ID { if alias.NamespaceID != ns.ID {
return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied
} }
switch { switch {
case mountAccessor == "" && name == "": case mountAccessor == "" && name == "" && len(customMetadata) == 0:
// Just a canonical ID update, maybe // Just a canonical ID update, maybe
if canonicalID == "" { if canonicalID == "" {
// Nothing to do, so be idempotent // Nothing to do, so be idempotent
return nil, nil return nil, nil
} }
name = alias.Name name = alias.Name
mountAccessor = alias.MountAccessor mountAccessor = alias.MountAccessor
customMetadata = alias.CustomMetadata
case mountAccessor == "": case mountAccessor == "":
// No change to mount accessor // No change to mount accessor
mountAccessor = alias.MountAccessor mountAccessor = alias.MountAccessor
case name == "": case name == "":
// No change to mount name // No change to mount name
name = alias.Name name = alias.Name
case len(customMetadata) == 0:
// No change to custom metadata
customMetadata = alias.CustomMetadata
default: default:
// Both provided // mountAccessor, name and customMetadata provided
} }
return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias, customMetadata)
return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias)
} }
} }
@@ -196,24 +231,25 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied
} }
return i.handleAliasUpdate(ctx, req, alias.CanonicalID, name, mountAccessor, alias) return i.handleAliasUpdate(ctx, req, alias.CanonicalID, name, mountAccessor, alias, customMetadata)
} }
// At this point we know it's a new creation request // At this point we know it's a new creation request
return i.handleAliasCreate(ctx, req, canonicalID, name, mountAccessor) return i.handleAliasCreate(ctx, req, canonicalID, name, mountAccessor, customMetadata)
} }
} }
func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string) (*logical.Response, error) { func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string, customMetadata map[string]string) (*logical.Response, error) {
ns, err := namespace.FromContext(ctx) ns, err := namespace.FromContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
alias := &identity.Alias{ alias := &identity.Alias{
MountAccessor: mountAccessor, MountAccessor: mountAccessor,
Name: name, Name: name,
CustomMetadata: customMetadata,
} }
entity := &identity.Entity{} entity := &identity.Entity{}
// If a canonical ID is provided pull up the entity and make sure we're in // If a canonical ID is provided pull up the entity and make sure we're in
@@ -266,10 +302,10 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Requ
}, nil }, nil
} }
func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string, alias *identity.Alias) (*logical.Response, error) { func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string, alias *identity.Alias, customMetadata map[string]string) (*logical.Response, error) {
if name == alias.Name && if name == alias.Name &&
mountAccessor == alias.MountAccessor && mountAccessor == alias.MountAccessor &&
(canonicalID == alias.CanonicalID || canonicalID == "") { (canonicalID == alias.CanonicalID || canonicalID == "") && (strutil.EqualStringMaps(customMetadata, alias.CustomMetadata)) {
// Nothing to do; return nil to be idempotent // Nothing to do; return nil to be idempotent
return nil, nil return nil, nil
} }
@@ -279,7 +315,7 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
// If we're changing one or the other or both of these, make sure that // If we're changing one or the other or both of these, make sure that
// there isn't a matching alias already, and make sure it's in the same // there isn't a matching alias already, and make sure it's in the same
// namespace. // namespace.
if name != alias.Name || mountAccessor != alias.MountAccessor { if name != alias.Name || mountAccessor != alias.MountAccessor || !strutil.EqualStringMaps(customMetadata, alias.CustomMetadata) {
// Check here to see if such an alias already exists, if so bail // Check here to see if such an alias already exists, if so bail
mountEntry := i.router.MatchingMountByAccessor(mountAccessor) mountEntry := i.router.MatchingMountByAccessor(mountAccessor)
if mountEntry == nil { if mountEntry == nil {
@@ -296,6 +332,7 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Bail unless it's just a case change // Bail unless it's just a case change
if existingAlias != nil && !strings.EqualFold(existingAlias.Name, name) { if existingAlias != nil && !strings.EqualFold(existingAlias.Name, name) {
return logical.ErrorResponse("alias with combination of mount accessor and name already exists"), nil return logical.ErrorResponse("alias with combination of mount accessor and name already exists"), nil
@@ -304,8 +341,8 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
// Update the values in the alias // Update the values in the alias
alias.Name = name alias.Name = name
alias.MountAccessor = mountAccessor alias.MountAccessor = mountAccessor
alias.CustomMetadata = customMetadata
} }
// Get our current entity, which may be the same as the new one if the // Get our current entity, which may be the same as the new one if the
// canonical ID hasn't changed // canonical ID hasn't changed
currentEntity, err := i.MemDBEntityByAliasID(alias.ID, true) currentEntity, err := i.MemDBEntityByAliasID(alias.ID, true)
@@ -373,6 +410,55 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
}, nil }, nil
} }
func validateCustomMetadata(customMetadata map[string]string) error {
var errs *multierror.Error
if keyCount := len(customMetadata); keyCount > maxCustomMetadataKeys {
errs = multierror.Append(errs, fmt.Errorf("%s: payload must contain at most %d keys, provided %d",
customMetadataValidationErrorPrefix,
maxCustomMetadataKeys,
keyCount))
return errs.ErrorOrNil()
}
// Perform validation on each key and value and return ALL errors
for key, value := range customMetadata {
if keyLen := len(key); 0 == keyLen || keyLen > maxCustomMetadataKeyLength {
errs = multierror.Append(errs, fmt.Errorf("%s: length of key %q is %d but must be 0 < len(key) <= %d",
customMetadataValidationErrorPrefix,
key,
keyLen,
maxCustomMetadataKeyLength))
}
if valueLen := len(value); 0 == valueLen || valueLen > maxCustomMetadataValueLength {
errs = multierror.Append(errs, fmt.Errorf("%s: length of value for key %q is %d but must be 0 < len(value) <= %d",
customMetadataValidationErrorPrefix,
key,
valueLen,
maxCustomMetadataValueLength))
}
if !strutil.Printable(key) {
// Include unquoted format (%s) to also include the string without the unprintable
// characters visible to allow for easier debug and key identification
errs = multierror.Append(errs, fmt.Errorf("%s: key %q (%s) contains unprintable characters",
customMetadataValidationErrorPrefix,
key,
key))
}
if !strutil.Printable(value) {
errs = multierror.Append(errs, fmt.Errorf("%s: value for key %q contains unprintable characters",
customMetadataValidationErrorPrefix,
key))
}
}
return errs.ErrorOrNil()
}
// pathAliasIDRead returns the properties of an alias for a given // pathAliasIDRead returns the properties of an alias for a given
// alias ID // alias ID
func (i *IdentityStore) pathAliasIDRead() framework.OperationFunc { func (i *IdentityStore) pathAliasIDRead() framework.OperationFunc {
@@ -409,6 +495,7 @@ func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identi
respData["canonical_id"] = alias.CanonicalID respData["canonical_id"] = alias.CanonicalID
respData["mount_accessor"] = alias.MountAccessor respData["mount_accessor"] = alias.MountAccessor
respData["metadata"] = alias.Metadata respData["metadata"] = alias.Metadata
respData["custom_metadata"] = alias.CustomMetadata
respData["name"] = alias.Name respData["name"] = alias.Name
respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs
respData["namespace_id"] = alias.NamespaceID respData["namespace_id"] = alias.NamespaceID

View File

@@ -375,10 +375,13 @@ func TestIdentityStore_AliasUpdate(t *testing.T) {
t.Fatalf("err:%v resp:%#v", err, resp) t.Fatalf("err:%v resp:%#v", err, resp)
} }
aliasID := resp.Data["id"].(string) aliasID := resp.Data["id"].(string)
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
updateData := map[string]interface{}{ updateData := map[string]interface{}{
"name": "updatedaliasname", "name": "updatedaliasname",
"mount_accessor": githubAccessor, "mount_accessor": githubAccessor,
"custom_metadata": customMetadata,
} }
aliasReq.Data = updateData aliasReq.Data = updateData
@@ -397,6 +400,9 @@ func TestIdentityStore_AliasUpdate(t *testing.T) {
if resp.Data["name"] != "updatedaliasname" { if resp.Data["name"] != "updatedaliasname" {
t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data) t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data)
} }
if !reflect.DeepEqual(resp.Data["custom_metadata"], customMetadata) {
t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data)
}
} }
func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
@@ -425,9 +431,12 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
t.Fatalf("expected an error due to invalid alias id") t.Fatalf("expected an error due to invalid alias id")
} }
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
registerData := map[string]interface{}{ registerData := map[string]interface{}{
"name": "testaliasname", "name": "testaliasname",
"mount_accessor": githubAccessor, "mount_accessor": githubAccessor,
"custom_metadata": customMetadata,
} }
registerReq := &logical.Request{ registerReq := &logical.Request{
@@ -468,6 +477,9 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
if resp.Data["name"] != "updatedaliasname" { if resp.Data["name"] != "updatedaliasname" {
t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data) t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data)
} }
if !reflect.DeepEqual(resp.Data["custom_metadata"], customMetadata) {
t.Fatalf("failed to update alias information; \n response data: %#v\n", resp.Data)
}
delete(registerReq.Data, "name") delete(registerReq.Data, "name")
@@ -498,10 +510,13 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) {
ctx := namespace.RootContext(nil) ctx := namespace.RootContext(nil)
is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t)
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
registerData := map[string]interface{}{ registerData := map[string]interface{}{
"name": "testaliasname", "name": "testaliasname",
"mount_accessor": githubAccessor, "mount_accessor": githubAccessor,
"metadata": []string{"organization=hashicorp", "team=vault"}, "metadata": []string{"organization=hashicorp", "team=vault"},
"custom_metadata": customMetadata,
} }
registerReq := &logical.Request{ registerReq := &logical.Request{
@@ -537,7 +552,7 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) {
if resp.Data["id"].(string) == "" || if resp.Data["id"].(string) == "" ||
resp.Data["canonical_id"].(string) == "" || resp.Data["canonical_id"].(string) == "" ||
resp.Data["name"].(string) != registerData["name"] || resp.Data["name"].(string) != registerData["name"] ||
resp.Data["mount_type"].(string) != "github" { resp.Data["mount_type"].(string) != "github" || !reflect.DeepEqual(resp.Data["custom_metadata"], customMetadata) {
t.Fatalf("bad: alias read response; \nexpected: %#v \nactual: %#v\n", registerData, resp.Data) t.Fatalf("bad: alias read response; \nexpected: %#v \nactual: %#v\n", registerData, resp.Data)
} }

View File

@@ -2080,9 +2080,10 @@ func (i *IdentityStore) handleAliasListCommon(ctx context.Context, groupAlias bo
alias := raw.(*identity.Alias) alias := raw.(*identity.Alias)
aliasIDs = append(aliasIDs, alias.ID) aliasIDs = append(aliasIDs, alias.ID)
aliasInfoEntry := map[string]interface{}{ aliasInfoEntry := map[string]interface{}{
"name": alias.Name, "name": alias.Name,
"canonical_id": alias.CanonicalID, "canonical_id": alias.CanonicalID,
"mount_accessor": alias.MountAccessor, "mount_accessor": alias.MountAccessor,
"custom_metadata": alias.CustomMetadata,
} }
mi, ok := mountAccessorMap[alias.MountAccessor] mi, ok := mountAccessorMap[alias.MountAccessor]

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.3 // protoc v3.17.3
// source: vault/request_forwarding_service.proto // source: vault/request_forwarding_service.proto