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 105786cc27
commit c643dc1d53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: helper/forwarding/types.proto

View File

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

View File

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

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: helper/identity/types.proto
@ -405,6 +405,8 @@ type Alias struct {
// NamespaceID is the identifier of the namespace to which this alias
// belongs.
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() {
@ -516,6 +518,13 @@ func (x *Alias) GetNamespaceID() string {
return ""
}
func (x *Alias) GetCustomMetadata() map[string]string {
if x != nil {
return x.CustomMetadata
}
return nil
}
// Deprecated. Retained for backwards compatibility.
type EntityStorageEntry struct {
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,
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,
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,
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,
@ -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,
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,
0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x1a, 0x3b, 0x0a,
0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x4b, 0x0a,
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,
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,
@ -966,7 +984,7 @@ func file_helper_identity_types_proto_rawDescGZIP() []byte {
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{}{
(*Group)(nil), // 0: identity.Group
(*Entity)(nil), // 1: identity.Entity
@ -977,40 +995,42 @@ var file_helper_identity_types_proto_goTypes = []interface{}{
nil, // 6: identity.Entity.MetadataEntry
nil, // 7: identity.Entity.MFASecretsEntry
nil, // 8: identity.Alias.MetadataEntry
nil, // 9: identity.EntityStorageEntry.MetadataEntry
nil, // 10: identity.EntityStorageEntry.MFASecretsEntry
nil, // 11: identity.PersonaIndexEntry.MetadataEntry
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
(*mfa.Secret)(nil), // 13: mfa.Secret
nil, // 9: identity.Alias.CustomMetadataEntry
nil, // 10: identity.EntityStorageEntry.MetadataEntry
nil, // 11: identity.EntityStorageEntry.MFASecretsEntry
nil, // 12: identity.PersonaIndexEntry.MetadataEntry
(*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp
(*mfa.Secret)(nil), // 14: mfa.Secret
}
var file_helper_identity_types_proto_depIDxs = []int32{
5, // 0: identity.Group.metadata:type_name -> identity.Group.MetadataEntry
12, // 1: identity.Group.creation_time:type_name -> google.protobuf.Timestamp
12, // 2: identity.Group.last_update_time:type_name -> google.protobuf.Timestamp
13, // 1: identity.Group.creation_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, // 4: identity.Entity.aliases:type_name -> identity.Alias
6, // 5: identity.Entity.metadata:type_name -> identity.Entity.MetadataEntry
12, // 6: identity.Entity.creation_time:type_name -> google.protobuf.Timestamp
12, // 7: identity.Entity.last_update_time:type_name -> google.protobuf.Timestamp
13, // 6: identity.Entity.creation_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
8, // 9: identity.Alias.metadata:type_name -> identity.Alias.MetadataEntry
12, // 10: identity.Alias.creation_time:type_name -> google.protobuf.Timestamp
12, // 11: identity.Alias.last_update_time:type_name -> google.protobuf.Timestamp
4, // 12: identity.EntityStorageEntry.personas:type_name -> identity.PersonaIndexEntry
9, // 13: identity.EntityStorageEntry.metadata:type_name -> identity.EntityStorageEntry.MetadataEntry
12, // 14: identity.EntityStorageEntry.creation_time:type_name -> google.protobuf.Timestamp
12, // 15: identity.EntityStorageEntry.last_update_time:type_name -> google.protobuf.Timestamp
10, // 16: identity.EntityStorageEntry.mfa_secrets:type_name -> identity.EntityStorageEntry.MFASecretsEntry
11, // 17: identity.PersonaIndexEntry.metadata:type_name -> identity.PersonaIndexEntry.MetadataEntry
12, // 18: identity.PersonaIndexEntry.creation_time:type_name -> google.protobuf.Timestamp
12, // 19: identity.PersonaIndexEntry.last_update_time:type_name -> google.protobuf.Timestamp
13, // 20: identity.Entity.MFASecretsEntry.value:type_name -> mfa.Secret
13, // 21: identity.EntityStorageEntry.MFASecretsEntry.value:type_name -> mfa.Secret
22, // [22:22] is the sub-list for method output_type
22, // [22:22] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
13, // 10: identity.Alias.creation_time:type_name -> google.protobuf.Timestamp
13, // 11: identity.Alias.last_update_time:type_name -> google.protobuf.Timestamp
9, // 12: identity.Alias.customMetadata:type_name -> identity.Alias.CustomMetadataEntry
4, // 13: identity.EntityStorageEntry.personas:type_name -> identity.PersonaIndexEntry
10, // 14: identity.EntityStorageEntry.metadata:type_name -> identity.EntityStorageEntry.MetadataEntry
13, // 15: identity.EntityStorageEntry.creation_time:type_name -> google.protobuf.Timestamp
13, // 16: identity.EntityStorageEntry.last_update_time:type_name -> google.protobuf.Timestamp
11, // 17: identity.EntityStorageEntry.mfa_secrets:type_name -> identity.EntityStorageEntry.MFASecretsEntry
12, // 18: identity.PersonaIndexEntry.metadata:type_name -> identity.PersonaIndexEntry.MetadataEntry
13, // 19: identity.PersonaIndexEntry.creation_time:type_name -> google.protobuf.Timestamp
13, // 20: identity.PersonaIndexEntry.last_update_time:type_name -> google.protobuf.Timestamp
14, // 21: identity.Entity.MFASecretsEntry.value:type_name -> mfa.Secret
14, // 22: identity.EntityStorageEntry.MFASecretsEntry.value:type_name -> mfa.Secret
23, // [23:23] is the sub-list for method output_type
23, // [23:23] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension 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() }
@ -1086,7 +1106,7 @@ func file_helper_identity_types_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_helper_identity_types_proto_rawDesc,
NumEnums: 0,
NumMessages: 12,
NumMessages: 13,
NumExtensions: 0,
NumServices: 0,
},

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.27.1
// protoc v3.17.3
// 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."):
split := strings.SplitN(trimmed, ".", 2)
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
@ -222,7 +231,7 @@ func performTemplating(input string, p *PopulateStringInput) (string, error) {
}
// 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)
}

View File

@ -17,23 +17,24 @@ var testNow = time.Now().Add(100 * time.Hour)
func TestPopulate_Basic(t *testing.T) {
tests := []struct {
mode int
name string
input string
output string
err error
entityName string
metadata map[string]string
aliasAccessor string
aliasID string
aliasName string
nilEntity bool
validityCheckOnly bool
aliasMetadata map[string]string
groupName string
groupMetadata map[string]string
groupMemberships []string
now time.Time
mode int
name string
input string
output string
err error
entityName string
metadata map[string]string
aliasAccessor string
aliasID string
aliasName string
nilEntity bool
validityCheckOnly bool
aliasMetadata map[string]string
aliasCustomMetadata map[string]string
groupName string
groupMetadata map[string]string
groupMemberships []string
now time.Time
}{
// time.* tests. Keep tests with time.Now() at the front to avoid false
// 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"},
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 {
@ -343,10 +391,11 @@ func TestPopulate_Basic(t *testing.T) {
if test.aliasAccessor != "" {
entity.Aliases = []*logical.Alias{
{
MountAccessor: test.aliasAccessor,
ID: test.aliasID,
Name: test.aliasName,
Metadata: test.aliasMetadata,
MountAccessor: test.aliasAccessor,
ID: test.aliasID,
Name: test.aliasName,
Metadata: test.aliasMetadata,
CustomMetadata: test.aliasCustomMetadata,
},
}
}
@ -436,6 +485,10 @@ func TestPopulate_FullObject(t *testing.T) {
"service": "ec2",
"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}},
"group names": {{identity.entity.groups.names}},
"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 := `
@ -474,7 +531,11 @@ func TestPopulate_FullObject(t *testing.T) {
"one not found alias metadata key": "",
"group names": ["g1","g2"],
"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{

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: sdk/logical/identity.proto
@ -138,6 +138,8 @@ type Alias struct {
// NamespaceID is the identifier of the namespace to which this alias
// belongs.
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() {
@ -214,6 +216,13 @@ func (x *Alias) GetNamespaceID() string {
return ""
}
func (x *Alias) GetCustomMetadata() map[string]string {
if x != nil {
return x.CustomMetadata
}
return nil
}
type Group struct {
state protoimpl.MessageState
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,
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, 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,
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,
@ -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,
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,
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,
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,
@ -359,25 +377,27 @@ func file_sdk_logical_identity_proto_rawDescGZIP() []byte {
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{}{
(*Entity)(nil), // 0: logical.Entity
(*Alias)(nil), // 1: logical.Alias
(*Group)(nil), // 2: logical.Group
nil, // 3: logical.Entity.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{
1, // 0: logical.Entity.aliases:type_name -> logical.Alias
3, // 1: logical.Entity.metadata:type_name -> logical.Entity.MetadataEntry
4, // 2: logical.Alias.metadata:type_name -> logical.Alias.MetadataEntry
5, // 3: logical.Group.metadata:type_name -> logical.Group.MetadataEntry
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
5, // 3: logical.Alias.custom_metadata:type_name -> logical.Alias.CustomMetadataEntry
6, // 4: logical.Group.metadata:type_name -> logical.Group.MetadataEntry
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension 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() }
@ -429,7 +449,7 @@ func file_sdk_logical_identity_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_sdk_logical_identity_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},

View File

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

View File

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

View File

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

View File

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

View File

@ -6,13 +6,21 @@ import (
"strings"
"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/namespace"
"github.com/hashicorp/vault/helper/storagepacker"
"github.com/hashicorp/vault/sdk/framework"
"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.
// Following are the paths supported:
// entity-alias - To register/modify an alias
@ -44,6 +52,10 @@ This field is deprecated, use canonical_id.`,
Type: framework.TypeString,
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{
logical.UpdateOperation: i.handleAliasCreateUpdate(),
@ -77,6 +89,10 @@ This field is deprecated, use canonical_id.`,
Type: framework.TypeString,
Description: "(Unused)",
},
"custom_metadata": {
Type: framework.TypeKVPairs,
Description: "User provided key-value pairs",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: i.handleAliasCreateUpdate(),
@ -118,6 +134,16 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
// Get ID, if any
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
canonicalID := d.Get("canonical_id").(string)
if canonicalID == "" {
@ -125,6 +151,15 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
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()
defer i.lock.Unlock()
@ -143,31 +178,31 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
if alias.NamespaceID != ns.ID {
return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied
}
switch {
case mountAccessor == "" && name == "":
case mountAccessor == "" && name == "" && len(customMetadata) == 0:
// Just a canonical ID update, maybe
if canonicalID == "" {
// Nothing to do, so be idempotent
return nil, nil
}
name = alias.Name
mountAccessor = alias.MountAccessor
customMetadata = alias.CustomMetadata
case mountAccessor == "":
// No change to mount accessor
mountAccessor = alias.MountAccessor
case name == "":
// No change to mount name
name = alias.Name
case len(customMetadata) == 0:
// No change to custom metadata
customMetadata = alias.CustomMetadata
default:
// Both provided
// mountAccessor, name and customMetadata provided
}
return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias)
return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias, customMetadata)
}
}
@ -196,24 +231,25 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc {
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
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)
if err != nil {
return nil, err
}
alias := &identity.Alias{
MountAccessor: mountAccessor,
Name: name,
MountAccessor: mountAccessor,
Name: name,
CustomMetadata: customMetadata,
}
entity := &identity.Entity{}
// 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
}
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 &&
mountAccessor == alias.MountAccessor &&
(canonicalID == alias.CanonicalID || canonicalID == "") {
(canonicalID == alias.CanonicalID || canonicalID == "") && (strutil.EqualStringMaps(customMetadata, alias.CustomMetadata)) {
// Nothing to do; return nil to be idempotent
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
// there isn't a matching alias already, and make sure it's in the same
// 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
mountEntry := i.router.MatchingMountByAccessor(mountAccessor)
if mountEntry == nil {
@ -296,6 +332,7 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
if err != nil {
return nil, err
}
// Bail unless it's just a case change
if existingAlias != nil && !strings.EqualFold(existingAlias.Name, name) {
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
alias.Name = name
alias.MountAccessor = mountAccessor
alias.CustomMetadata = customMetadata
}
// Get our current entity, which may be the same as the new one if the
// canonical ID hasn't changed
currentEntity, err := i.MemDBEntityByAliasID(alias.ID, true)
@ -373,6 +410,55 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ
}, 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
// alias ID
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["mount_accessor"] = alias.MountAccessor
respData["metadata"] = alias.Metadata
respData["custom_metadata"] = alias.CustomMetadata
respData["name"] = alias.Name
respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs
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)
}
aliasID := resp.Data["id"].(string)
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
updateData := map[string]interface{}{
"name": "updatedaliasname",
"mount_accessor": githubAccessor,
"name": "updatedaliasname",
"mount_accessor": githubAccessor,
"custom_metadata": customMetadata,
}
aliasReq.Data = updateData
@ -397,6 +400,9 @@ func TestIdentityStore_AliasUpdate(t *testing.T) {
if resp.Data["name"] != "updatedaliasname" {
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) {
@ -425,9 +431,12 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
t.Fatalf("expected an error due to invalid alias id")
}
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
registerData := map[string]interface{}{
"name": "testaliasname",
"mount_accessor": githubAccessor,
"name": "testaliasname",
"mount_accessor": githubAccessor,
"custom_metadata": customMetadata,
}
registerReq := &logical.Request{
@ -468,6 +477,9 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
if resp.Data["name"] != "updatedaliasname" {
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")
@ -498,10 +510,13 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) {
ctx := namespace.RootContext(nil)
is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t)
customMetadata := make(map[string]string)
customMetadata["foo"] = "abc"
registerData := map[string]interface{}{
"name": "testaliasname",
"mount_accessor": githubAccessor,
"metadata": []string{"organization=hashicorp", "team=vault"},
"name": "testaliasname",
"mount_accessor": githubAccessor,
"metadata": []string{"organization=hashicorp", "team=vault"},
"custom_metadata": customMetadata,
}
registerReq := &logical.Request{
@ -537,7 +552,7 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) {
if resp.Data["id"].(string) == "" ||
resp.Data["canonical_id"].(string) == "" ||
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)
}

View File

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

View File

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