From 476fb08e0df074b62133a28a6b2a7f3c1ed83bbc Mon Sep 17 00:00:00 2001 From: Vishal Nayak Date: Fri, 15 Oct 2021 15:20:00 -0400 Subject: [PATCH] Local aliases OSS patch (#12848) * Local aliases OSS patch * build fix --- helper/identity/identity.go | 10 + helper/identity/types.pb.go | 519 +++++++++++------- helper/identity/types.proto | 18 + sdk/logical/identity.pb.go | 63 ++- sdk/logical/identity.proto | 5 + vault/external_tests/identity/aliases_test.go | 51 -- vault/identity_store.go | 232 +++++++- vault/identity_store_aliases.go | 169 ++++-- vault/identity_store_aliases_test.go | 2 + vault/identity_store_entities.go | 2 + vault/identity_store_oss.go | 4 + vault/identity_store_schema.go | 6 + vault/identity_store_structs.go | 13 + vault/identity_store_test.go | 53 +- vault/identity_store_util.go | 379 ++++++++++++- vault/request_handling.go | 15 +- vault/request_handling_util.go | 6 + 17 files changed, 1144 insertions(+), 403 deletions(-) diff --git a/helper/identity/identity.go b/helper/identity/identity.go index 4f8eb6d42..9a28be715 100644 --- a/helper/identity/identity.go +++ b/helper/identity/identity.go @@ -45,6 +45,16 @@ func (e *Entity) Clone() (*Entity, error) { return &clonedEntity, nil } +func (e *Entity) UpsertAlias(alias *Alias) { + for i, item := range e.Aliases { + if item.ID == alias.ID { + e.Aliases[i] = alias + return + } + } + e.Aliases = append(e.Aliases, alias) +} + func (p *Alias) Clone() (*Alias, error) { if p == nil { return nil, fmt.Errorf("nil alias") diff --git a/helper/identity/types.pb.go b/helper/identity/types.pb.go index 424be9ef8..c4730c775 100644 --- a/helper/identity/types.pb.go +++ b/helper/identity/types.pb.go @@ -204,6 +204,55 @@ func (x *Group) GetNamespaceID() string { return "" } +// LocalAliases holds the aliases belonging to an entity that are local to the +// cluster. +type LocalAliases struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Aliases []*Alias `protobuf:"bytes,1,rep,name=aliases,proto3" json:"aliases,omitempty"` +} + +func (x *LocalAliases) Reset() { + *x = LocalAliases{} + if protoimpl.UnsafeEnabled { + mi := &file_helper_identity_types_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LocalAliases) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LocalAliases) ProtoMessage() {} + +func (x *LocalAliases) ProtoReflect() protoreflect.Message { + mi := &file_helper_identity_types_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LocalAliases.ProtoReflect.Descriptor instead. +func (*LocalAliases) Descriptor() ([]byte, []int) { + return file_helper_identity_types_proto_rawDescGZIP(), []int{1} +} + +func (x *LocalAliases) GetAliases() []*Alias { + if x != nil { + return x.Aliases + } + return nil +} + // Entity represents an entity that gets persisted and indexed. // Entity is fundamentally composed of zero or many aliases. type Entity struct { @@ -274,7 +323,7 @@ type Entity struct { func (x *Entity) Reset() { *x = Entity{} if protoimpl.UnsafeEnabled { - mi := &file_helper_identity_types_proto_msgTypes[1] + mi := &file_helper_identity_types_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -287,7 +336,7 @@ func (x *Entity) String() string { func (*Entity) ProtoMessage() {} func (x *Entity) ProtoReflect() protoreflect.Message { - mi := &file_helper_identity_types_proto_msgTypes[1] + mi := &file_helper_identity_types_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -300,7 +349,7 @@ func (x *Entity) ProtoReflect() protoreflect.Message { // Deprecated: Use Entity.ProtoReflect.Descriptor instead. func (*Entity) Descriptor() ([]byte, []int) { - return file_helper_identity_types_proto_rawDescGZIP(), []int{1} + return file_helper_identity_types_proto_rawDescGZIP(), []int{2} } func (x *Entity) GetAliases() []*Alias { @@ -444,12 +493,22 @@ type Alias struct { // Custom Metadata represents the custom data tied to this alias // @inject_tag: sentinel:"-" CustomMetadata map[string]string `protobuf:"bytes,12,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" sentinel:"-"` + // Local indicates if the alias only belongs to the cluster where it was + // created. If true, the alias will be stored in a location that is ignored by + // the performance replication subsystem. + // @inject_tag: sentinel:"-" + Local bool `protobuf:"varint,13,opt,name=local,proto3" json:"local,omitempty" sentinel:"-"` + // LocalBucketKey is the identifying element of the location where this alias + // is stored in the storage packer. This helps in querying local aliases + // during invalidation of local aliases in performance standbys. + // @inject_tag: sentinel:"-" + LocalBucketKey string `protobuf:"bytes,14,opt,name=local_bucket_key,json=localBucketKey,proto3" json:"local_bucket_key,omitempty" sentinel:"-"` } func (x *Alias) Reset() { *x = Alias{} if protoimpl.UnsafeEnabled { - mi := &file_helper_identity_types_proto_msgTypes[2] + mi := &file_helper_identity_types_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -462,7 +521,7 @@ func (x *Alias) String() string { func (*Alias) ProtoMessage() {} func (x *Alias) ProtoReflect() protoreflect.Message { - mi := &file_helper_identity_types_proto_msgTypes[2] + mi := &file_helper_identity_types_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -475,7 +534,7 @@ func (x *Alias) ProtoReflect() protoreflect.Message { // Deprecated: Use Alias.ProtoReflect.Descriptor instead. func (*Alias) Descriptor() ([]byte, []int) { - return file_helper_identity_types_proto_rawDescGZIP(), []int{2} + return file_helper_identity_types_proto_rawDescGZIP(), []int{3} } func (x *Alias) GetID() string { @@ -562,6 +621,20 @@ func (x *Alias) GetCustomMetadata() map[string]string { return nil } +func (x *Alias) GetLocal() bool { + if x != nil { + return x.Local + } + return false +} + +func (x *Alias) GetLocalBucketKey() string { + if x != nil { + return x.LocalBucketKey + } + return "" +} + // Deprecated. Retained for backwards compatibility. type EntityStorageEntry struct { state protoimpl.MessageState @@ -583,7 +656,7 @@ type EntityStorageEntry struct { func (x *EntityStorageEntry) Reset() { *x = EntityStorageEntry{} if protoimpl.UnsafeEnabled { - mi := &file_helper_identity_types_proto_msgTypes[3] + mi := &file_helper_identity_types_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -596,7 +669,7 @@ func (x *EntityStorageEntry) String() string { func (*EntityStorageEntry) ProtoMessage() {} func (x *EntityStorageEntry) ProtoReflect() protoreflect.Message { - mi := &file_helper_identity_types_proto_msgTypes[3] + mi := &file_helper_identity_types_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -609,7 +682,7 @@ func (x *EntityStorageEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use EntityStorageEntry.ProtoReflect.Descriptor instead. func (*EntityStorageEntry) Descriptor() ([]byte, []int) { - return file_helper_identity_types_proto_rawDescGZIP(), []int{3} + return file_helper_identity_types_proto_rawDescGZIP(), []int{4} } func (x *EntityStorageEntry) GetPersonas() []*PersonaIndexEntry { @@ -703,7 +776,7 @@ type PersonaIndexEntry struct { func (x *PersonaIndexEntry) Reset() { *x = PersonaIndexEntry{} if protoimpl.UnsafeEnabled { - mi := &file_helper_identity_types_proto_msgTypes[4] + mi := &file_helper_identity_types_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -716,7 +789,7 @@ func (x *PersonaIndexEntry) String() string { func (*PersonaIndexEntry) ProtoMessage() {} func (x *PersonaIndexEntry) ProtoReflect() protoreflect.Message { - mi := &file_helper_identity_types_proto_msgTypes[4] + mi := &file_helper_identity_types_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -729,7 +802,7 @@ func (x *PersonaIndexEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use PersonaIndexEntry.ProtoReflect.Descriptor instead. func (*PersonaIndexEntry) Descriptor() ([]byte, []int) { - return file_helper_identity_types_proto_rawDescGZIP(), []int{4} + return file_helper_identity_types_proto_rawDescGZIP(), []int{5} } func (x *PersonaIndexEntry) GetID() string { @@ -847,166 +920,174 @@ var file_helper_identity_types_proto_rawDesc = []byte{ 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, 0x8c, 0x05, 0x0a, 0x06, 0x45, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3f, - 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61, + 0x6c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x22, 0x8c, 0x05, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x29, + 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x72, + 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x0b, 0x6d, 0x66, 0x61, 0x5f, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x4d, + 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, + 0x6d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 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, 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, 0x4a, 0x0a, 0x0f, 0x4d, 0x66, 0x61, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 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, 0xe1, 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, 0x61, 0x6c, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 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, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x39, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x65, + 0x72, 0x67, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 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, 0x12, 0x4c, 0x0a, 0x0f, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x6d, 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, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x28, 0x0a, 0x10, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x4b, 0x65, 0x79, 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, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x37, 0x0a, + 0x08, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, + 0x6e, 0x61, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x70, 0x65, + 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0f, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1d, 0x0a, - 0x0a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x0b, - 0x6d, 0x66, 0x61, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x2e, 0x4d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 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, 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, 0x4a, 0x0a, 0x0f, 0x4d, - 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 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, 0xa1, 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, - 0x61, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x72, + 0x67, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4d, 0x0a, 0x0b, 0x6d, 0x66, 0x61, + 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x66, 0x61, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x66, + 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 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, 0x4a, 0x0a, 0x0f, 0x4d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 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, 0xf9, 0x03, 0x0a, 0x11, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 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, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x39, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x39, 0x0a, 0x19, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, - 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, - 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, 0x12, 0x4c, 0x0a, - 0x0f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 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, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x08, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, - 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2a, - 0x0a, 0x11, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x72, 0x67, 0x65, - 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4d, - 0x0a, 0x0b, 0x6d, 0x66, 0x61, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x2e, 0x4d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0a, 0x6d, 0x66, 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 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, 0x4a, 0x0a, 0x0f, 0x4d, 0x66, - 0x61, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 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, 0xf9, 0x03, 0x0a, 0x11, 0x50, 0x65, 0x72, 0x73, 0x6f, - 0x6e, 0x61, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 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, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x45, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x50, 0x65, 0x72, 0x73, - 0x6f, 0x6e, 0x61, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x33, 0x0a, 0x16, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x13, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x45, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x49, 0x64, 0x73, 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, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, - 0x2f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x6d, + 0x65, 0x72, 0x67, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x6d, 0x65, 0x72, + 0x67, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, + 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, 0x42, 0x2c, 0x5a, + 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x68, 0x65, 0x6c, 0x70, + 0x65, 0x72, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1021,53 +1102,55 @@ 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, 13) +var file_helper_identity_types_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_helper_identity_types_proto_goTypes = []interface{}{ (*Group)(nil), // 0: identity.Group - (*Entity)(nil), // 1: identity.Entity - (*Alias)(nil), // 2: identity.Alias - (*EntityStorageEntry)(nil), // 3: identity.EntityStorageEntry - (*PersonaIndexEntry)(nil), // 4: identity.PersonaIndexEntry - nil, // 5: identity.Group.MetadataEntry - nil, // 6: identity.Entity.MetadataEntry - nil, // 7: identity.Entity.MFASecretsEntry - nil, // 8: identity.Alias.MetadataEntry - 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 + (*LocalAliases)(nil), // 1: identity.LocalAliases + (*Entity)(nil), // 2: identity.Entity + (*Alias)(nil), // 3: identity.Alias + (*EntityStorageEntry)(nil), // 4: identity.EntityStorageEntry + (*PersonaIndexEntry)(nil), // 5: identity.PersonaIndexEntry + nil, // 6: identity.Group.MetadataEntry + nil, // 7: identity.Entity.MetadataEntry + nil, // 8: identity.Entity.MFASecretsEntry + nil, // 9: identity.Alias.MetadataEntry + nil, // 10: identity.Alias.CustomMetadataEntry + nil, // 11: identity.EntityStorageEntry.MetadataEntry + nil, // 12: identity.EntityStorageEntry.MFASecretsEntry + nil, // 13: identity.PersonaIndexEntry.MetadataEntry + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + (*mfa.Secret)(nil), // 15: mfa.Secret } var file_helper_identity_types_proto_depIDxs = []int32{ - 5, // 0: identity.Group.metadata:type_name -> identity.Group.MetadataEntry - 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 - 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 - 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.custom_metadata: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 + 6, // 0: identity.Group.metadata:type_name -> identity.Group.MetadataEntry + 14, // 1: identity.Group.creation_time:type_name -> google.protobuf.Timestamp + 14, // 2: identity.Group.last_update_time:type_name -> google.protobuf.Timestamp + 3, // 3: identity.Group.alias:type_name -> identity.Alias + 3, // 4: identity.LocalAliases.aliases:type_name -> identity.Alias + 3, // 5: identity.Entity.aliases:type_name -> identity.Alias + 7, // 6: identity.Entity.metadata:type_name -> identity.Entity.MetadataEntry + 14, // 7: identity.Entity.creation_time:type_name -> google.protobuf.Timestamp + 14, // 8: identity.Entity.last_update_time:type_name -> google.protobuf.Timestamp + 8, // 9: identity.Entity.mfa_secrets:type_name -> identity.Entity.MFASecretsEntry + 9, // 10: identity.Alias.metadata:type_name -> identity.Alias.MetadataEntry + 14, // 11: identity.Alias.creation_time:type_name -> google.protobuf.Timestamp + 14, // 12: identity.Alias.last_update_time:type_name -> google.protobuf.Timestamp + 10, // 13: identity.Alias.custom_metadata:type_name -> identity.Alias.CustomMetadataEntry + 5, // 14: identity.EntityStorageEntry.personas:type_name -> identity.PersonaIndexEntry + 11, // 15: identity.EntityStorageEntry.metadata:type_name -> identity.EntityStorageEntry.MetadataEntry + 14, // 16: identity.EntityStorageEntry.creation_time:type_name -> google.protobuf.Timestamp + 14, // 17: identity.EntityStorageEntry.last_update_time:type_name -> google.protobuf.Timestamp + 12, // 18: identity.EntityStorageEntry.mfa_secrets:type_name -> identity.EntityStorageEntry.MFASecretsEntry + 13, // 19: identity.PersonaIndexEntry.metadata:type_name -> identity.PersonaIndexEntry.MetadataEntry + 14, // 20: identity.PersonaIndexEntry.creation_time:type_name -> google.protobuf.Timestamp + 14, // 21: identity.PersonaIndexEntry.last_update_time:type_name -> google.protobuf.Timestamp + 15, // 22: identity.Entity.MFASecretsEntry.value:type_name -> mfa.Secret + 15, // 23: identity.EntityStorageEntry.MFASecretsEntry.value:type_name -> mfa.Secret + 24, // [24:24] is the sub-list for method output_type + 24, // [24:24] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_helper_identity_types_proto_init() } @@ -1089,7 +1172,7 @@ func file_helper_identity_types_proto_init() { } } file_helper_identity_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Entity); i { + switch v := v.(*LocalAliases); i { case 0: return &v.state case 1: @@ -1101,7 +1184,7 @@ func file_helper_identity_types_proto_init() { } } file_helper_identity_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Alias); i { + switch v := v.(*Entity); i { case 0: return &v.state case 1: @@ -1113,7 +1196,7 @@ func file_helper_identity_types_proto_init() { } } file_helper_identity_types_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EntityStorageEntry); i { + switch v := v.(*Alias); i { case 0: return &v.state case 1: @@ -1125,6 +1208,18 @@ func file_helper_identity_types_proto_init() { } } file_helper_identity_types_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EntityStorageEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_helper_identity_types_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PersonaIndexEntry); i { case 0: return &v.state @@ -1143,7 +1238,7 @@ func file_helper_identity_types_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_helper_identity_types_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/helper/identity/types.proto b/helper/identity/types.proto index a748198f8..0ea7525eb 100644 --- a/helper/identity/types.proto +++ b/helper/identity/types.proto @@ -74,6 +74,12 @@ message Group { string namespace_id = 13; } +// LocalAliases holds the aliases belonging to an entity that are local to the +// cluster. +message LocalAliases { + repeated Alias aliases = 1; +} + // Entity represents an entity that gets persisted and indexed. // Entity is fundamentally composed of zero or many aliases. message Entity { @@ -212,6 +218,18 @@ message Alias { // Custom Metadata represents the custom data tied to this alias // @inject_tag: sentinel:"-" map custom_metadata = 12; + + // Local indicates if the alias only belongs to the cluster where it was + // created. If true, the alias will be stored in a location that is ignored by + // the performance replication subsystem. + // @inject_tag: sentinel:"-" + bool local = 13; + + // LocalBucketKey is the identifying element of the location where this alias + // is stored in the storage packer. This helps in querying local aliases + // during invalidation of local aliases in performance standbys. + // @inject_tag: sentinel:"-" + string local_bucket_key = 14; } // Deprecated. Retained for backwards compatibility. diff --git a/sdk/logical/identity.pb.go b/sdk/logical/identity.pb.go index b511b587c..b221ccc3b 100644 --- a/sdk/logical/identity.pb.go +++ b/sdk/logical/identity.pb.go @@ -140,6 +140,10 @@ type Alias struct { NamespaceID string `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 `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"` + // Local indicates if the alias only belongs to the cluster where it was + // created. If true, the alias will be stored in a location that are ignored + // by the performance replication subsystem. + Local bool `protobuf:"varint,8,opt,name=local,proto3" json:"local,omitempty"` } func (x *Alias) Reset() { @@ -223,6 +227,13 @@ func (x *Alias) GetCustomMetadata() map[string]string { return nil } +func (x *Alias) GetLocal() bool { + if x != nil { + return x.Local + } + return false +} + type Group struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -321,7 +332,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, 0x9b, 0x03, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb1, 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, @@ -339,30 +350,32 @@ var file_sdk_logical_identity_proto_rawDesc = []byte{ 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, - 0x02, 0x38, 0x01, 0x22, 0xc5, 0x01, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, - 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 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, 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, 0x42, 0x28, 0x5a, 0x26, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, - 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 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, 0xc5, 0x01, 0x0a, 0x05, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x67, 0x69, + 0x63, 0x61, 0x6c, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x04, 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, 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, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, + 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/logical/identity.proto b/sdk/logical/identity.proto index 9e07bd648..11c767823 100644 --- a/sdk/logical/identity.proto +++ b/sdk/logical/identity.proto @@ -53,6 +53,11 @@ message Alias { // Custom Metadata represents the custom data tied to this alias map custom_metadata = 7; + + // Local indicates if the alias only belongs to the cluster where it was + // created. If true, the alias will be stored in a location that are ignored + // by the performance replication subsystem. + bool local = 8; } message Group { diff --git a/vault/external_tests/identity/aliases_test.go b/vault/external_tests/identity/aliases_test.go index 162672e92..059524275 100644 --- a/vault/external_tests/identity/aliases_test.go +++ b/vault/external_tests/identity/aliases_test.go @@ -5,63 +5,12 @@ import ( "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/builtin/credential/github" - credLdap "github.com/hashicorp/vault/builtin/credential/ldap" "github.com/hashicorp/vault/builtin/credential/userpass" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" ) -func TestIdentityStore_EntityAliasLocalMount(t *testing.T) { - coreConfig := &vault.CoreConfig{ - CredentialBackends: map[string]logical.Factory{ - "ldap": credLdap.Factory, - }, - } - cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - }) - cluster.Start() - defer cluster.Cleanup() - - core := cluster.Cores[0].Core - vault.TestWaitActive(t, core) - client := cluster.Cores[0].Client - - // Create a local auth mount - err := client.Sys().EnableAuthWithOptions("ldap", &api.EnableAuthOptions{ - Type: "ldap", - Local: true, - }) - if err != nil { - t.Fatal(err) - } - - // Extract out the mount accessor for LDAP auth - auths, err := client.Sys().ListAuth() - if err != nil { - t.Fatal(err) - } - ldapMountAccessor := auths["ldap/"].Accessor - - // Create an entity - secret, err := client.Logical().Write("identity/entity", nil) - if err != nil { - t.Fatal(err) - } - entityID := secret.Data["id"].(string) - - // Attempt to create an entity alias against a local mount should fail - secret, err = client.Logical().Write("identity/entity-alias", map[string]interface{}{ - "name": "testuser", - "mount_accessor": ldapMountAccessor, - "canonical_id": entityID, - }) - if err == nil { - t.Fatalf("expected error since mount is local") - } -} - func TestIdentityStore_ListAlias(t *testing.T) { coreConfig := &vault.CoreConfig{ CredentialBackends: map[string]logical.Factory{ diff --git a/vault/identity_store.go b/vault/identity_store.go index 18e3a215a..7f9343160 100644 --- a/vault/identity_store.go +++ b/vault/identity_store.go @@ -22,7 +22,8 @@ import ( ) const ( - groupBucketsPrefix = "packer/group/buckets/" + groupBucketsPrefix = "packer/group/buckets/" + localAliasesBucketsPrefix = "packer/local-aliases/buckets/" ) var ( @@ -58,6 +59,7 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo totpPersister: core, groupUpdater: core, tokenStorer: core, + entityCreator: core, } // Create a memdb instance, which by default, operates on lower cased @@ -69,13 +71,21 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo entitiesPackerLogger := iStore.logger.Named("storagepacker").Named("entities") core.AddLogger(entitiesPackerLogger) + localAliasesPackerLogger := iStore.logger.Named("storagepacker").Named("local-aliases") + core.AddLogger(localAliasesPackerLogger) groupsPackerLogger := iStore.logger.Named("storagepacker").Named("groups") core.AddLogger(groupsPackerLogger) + iStore.entityPacker, err = storagepacker.NewStoragePacker(iStore.view, entitiesPackerLogger, "") if err != nil { return nil, fmt.Errorf("failed to create entity packer: %w", err) } + iStore.localAliasPacker, err = storagepacker.NewStoragePacker(iStore.view, localAliasesPackerLogger, localAliasesBucketsPrefix) + if err != nil { + return nil, fmt.Errorf("failed to create local alias packer: %w", err) + } + iStore.groupPacker, err = storagepacker.NewStoragePacker(iStore.view, groupsPackerLogger, groupBucketsPrefix) if err != nil { return nil, fmt.Errorf("failed to create group packer: %w", err) @@ -92,6 +102,9 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo "oidc/provider/+/.well-known/*", "oidc/provider/+/token", }, + LocalStorage: []string{ + localAliasesBucketsPrefix, + }, }, PeriodicFunc: func(ctx context.Context, req *logical.Request) error { iStore.oidcPeriodicFunc(ctx) @@ -237,6 +250,7 @@ func (i *IdentityStore) Invalidate(ctx context.Context, key string) { // storage entry is non-nil, its an indication of an update. In this // case, entities in the updated bucket needs to be reinserted into // MemDB. + entityIDs := make([]string, 0, len(bucket.Items)) if bucket != nil { for _, item := range bucket.Items { entity, err := i.parseEntityFromBucketItem(ctx, item) @@ -245,12 +259,53 @@ func (i *IdentityStore) Invalidate(ctx context.Context, key string) { return } + localAliases, err := i.parseLocalAliases(entity.ID) + if err != nil { + i.logger.Error("failed to load local aliases from storage", "error", err) + return + } + if localAliases != nil { + for _, alias := range localAliases.Aliases { + entity.UpsertAlias(alias) + } + } + // Only update MemDB and don't touch the storage err = i.upsertEntityInTxn(ctx, txn, entity, nil, false) if err != nil { i.logger.Error("failed to update entity in MemDB", "error", err) return } + + // If we are a secondary, the entity created by the secondary + // via the CreateEntity RPC would have been cached. Now that the + // invalidation of the same has hit, there is no need of the + // cache. Clearing the cache. Writing to storage can't be + // performed by perf standbys. So only doing this in the active + // node of the secondary. + if i.localNode.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) && i.localNode.HAState() != consts.PerfStandby { + if err := i.localAliasPacker.DeleteItem(ctx, entity.ID+tmpSuffix); err != nil { + i.logger.Error("failed to clear local alias entity cache", "error", err, "entity_id", entity.ID) + return + } + } + + entityIDs = append(entityIDs, entity.ID) + } + } + + // entitiesFetched are the entities before invalidation. entityIDs + // represent entities that are valid after invalidation. Clear the + // storage entries of local aliases for those entities that are + // indicated deleted by this invalidation. + if i.localNode.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) && i.localNode.HAState() != consts.PerfStandby { + for _, entity := range entitiesFetched { + if !strutil.StrListContains(entityIDs, entity.ID) { + if err := i.localAliasPacker.DeleteItem(ctx, entity.ID); err != nil { + i.logger.Error("failed to clear local alias for entity", "error", err, "entity_id", entity.ID) + return + } + } } } @@ -258,6 +313,7 @@ func (i *IdentityStore) Invalidate(ctx context.Context, key string) { return // Check if the key is a storage entry key for an group bucket + // For those entities that are deleted, clear up the local alias entries case strings.HasPrefix(key, groupBucketsPrefix): // Create a MemDB transaction txn := i.db.Txn(true) @@ -351,9 +407,127 @@ func (i *IdentityStore) Invalidate(ctx context.Context, key string) { i.logger.Error("error invalidating client", "error", err, "key", key) return } + case strings.HasPrefix(key, localAliasesBucketsPrefix): + // + // This invalidation only happens on perf standbys + // + + txn := i.db.Txn(true) + defer txn.Abort() + + // Find all the local aliases belonging to this bucket and remove it + // both from aliases table and entities table. We will add the local + // aliases back by parsing the storage key. This way the deletion + // invalidation gets handled. + aliases, err := i.MemDBLocalAliasesByBucketKeyInTxn(txn, key) + if err != nil { + i.logger.Error("failed to fetch entities using the bucket key", "key", key) + return + } + + for _, alias := range aliases { + entity, err := i.MemDBEntityByIDInTxn(txn, alias.CanonicalID, true) + if err != nil { + i.logger.Error("failed to fetch entity during local alias invalidation", "entity_id", alias.CanonicalID, "error", err) + return + } + + // Delete local aliases from the entity. + err = i.deleteAliasesInEntityInTxn(txn, entity, []*identity.Alias{alias}) + if err != nil { + i.logger.Error("failed to delete aliases in entity", "entity_id", entity.ID, "error", err) + return + } + + // Update the entity with removed alias. + if err := i.MemDBUpsertEntityInTxn(txn, entity); err != nil { + i.logger.Error("failed to delete entity from MemDB", "entity_id", entity.ID, "error", err) + return + } + } + + // Now read the invalidated storage key + bucket, err := i.localAliasPacker.GetBucket(ctx, key) + if err != nil { + i.logger.Error("failed to refresh local aliases", "key", key, "error", err) + return + } + if bucket != nil { + for _, item := range bucket.Items { + if strings.HasSuffix(item.ID, tmpSuffix) { + continue + } + + var localAliases identity.LocalAliases + err = ptypes.UnmarshalAny(item.Message, &localAliases) + if err != nil { + i.logger.Error("failed to parse local aliases during invalidation", "error", err) + return + } + for _, alias := range localAliases.Aliases { + // Add to the aliases table + if err := i.MemDBUpsertAliasInTxn(txn, alias, false); err != nil { + i.logger.Error("failed to insert local alias to memdb during invalidation", "error", err) + return + } + + // Fetch the associated entity and add the alias to that too. + entity, err := i.MemDBEntityByIDInTxn(txn, alias.CanonicalID, false) + if err != nil { + i.logger.Error("failed to fetch entity during local alias invalidation", "error", err) + return + } + if entity == nil { + cachedEntityItem, err := i.localAliasPacker.GetItem(alias.CanonicalID + tmpSuffix) + if err != nil { + i.logger.Error("failed to fetch cached entity", "key", key, "error", err) + return + } + if cachedEntityItem != nil { + entity, err = i.parseCachedEntity(cachedEntityItem) + if err != nil { + i.logger.Error("failed to parse cached entity", "key", key, "error", err) + return + } + } + } + if entity == nil { + i.logger.Error("received local alias invalidation for an invalid entity", "item.ID", item.ID) + return + } + entity.UpsertAlias(alias) + + // Update the entities table + if err := i.MemDBUpsertEntityInTxn(txn, entity); err != nil { + i.logger.Error("failed to upsert entity during local alias invalidation", "error", err) + return + } + } + } + } + txn.Commit() + return } } +func (i *IdentityStore) parseLocalAliases(entityID string) (*identity.LocalAliases, error) { + item, err := i.localAliasPacker.GetItem(entityID) + if err != nil { + return nil, err + } + if item == nil { + return nil, nil + } + + var localAliases identity.LocalAliases + err = ptypes.UnmarshalAny(item.Message, &localAliases) + if err != nil { + return nil, err + } + + return &localAliases, nil +} + func (i *IdentityStore) parseEntityFromBucketItem(ctx context.Context, item *storagepacker.Item) (*identity.Entity, error) { if item == nil { return nil, fmt.Errorf("nil item") @@ -400,7 +574,7 @@ func (i *IdentityStore) parseEntityFromBucketItem(ctx context.Context, item *sto newAlias.CreationTime = oldAlias.CreationTime newAlias.LastUpdateTime = oldAlias.LastUpdateTime newAlias.MergedFromCanonicalIDs = oldAlias.MergedFromEntityIDs - entity.Aliases = append(entity.Aliases, &newAlias) + entity.UpsertAlias(&newAlias) } persistNeeded = true @@ -439,6 +613,24 @@ func (i *IdentityStore) parseEntityFromBucketItem(ctx context.Context, item *sto return &entity, nil } +func (i *IdentityStore) parseCachedEntity(item *storagepacker.Item) (*identity.Entity, error) { + if item == nil { + return nil, fmt.Errorf("nil item") + } + + var entity identity.Entity + err := ptypes.UnmarshalAny(item.Message, &entity) + if err != nil { + return nil, fmt.Errorf("failed to decode cached entity from storage bucket item: %w", err) + } + + if entity.NamespaceID == "" { + entity.NamespaceID = namespace.RootNamespaceID + } + + return &entity, nil +} + func (i *IdentityStore) parseGroupFromBucketItem(item *storagepacker.Item) (*identity.Group, error) { if item == nil { return nil, fmt.Errorf("nil item") @@ -500,6 +692,37 @@ func (i *IdentityStore) entityByAliasFactorsInTxn(txn *memdb.Txn, mountAccessor, return i.MemDBEntityByAliasIDInTxn(txn, alias.ID, clone) } +// CreateEntity creates a new entity. +func (i *IdentityStore) CreateEntity(ctx context.Context) (*identity.Entity, error) { + defer metrics.MeasureSince([]string{"identity", "create_entity"}, time.Now()) + + entity := new(identity.Entity) + err := i.sanitizeEntity(ctx, entity) + if err != nil { + return nil, err + } + if err := i.upsertEntity(ctx, entity, nil, true); err != nil { + return nil, err + } + + // Emit a metric for the new entity + ns, err := i.namespacer.NamespaceByID(ctx, entity.NamespaceID) + var nsLabel metrics.Label + if err != nil { + nsLabel = metrics.Label{"namespace", "unknown"} + } else { + nsLabel = metricsutil.NamespaceLabel(ns) + } + i.metrics.IncrCounterWithLabels( + []string{"identity", "entity", "creation"}, + 1, + []metrics.Label{ + nsLabel, + }) + + return entity, nil +} + // CreateOrFetchEntity creates a new entity. This is used by core to // associate each login attempt by an alias to a unified entity in Vault. func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical.Alias) (*identity.Entity, error) { @@ -522,10 +745,6 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. return nil, fmt.Errorf("invalid mount accessor %q", alias.MountAccessor) } - if mountValidationResp.MountLocal { - return nil, fmt.Errorf("mount_accessor %q is of a local mount", alias.MountAccessor) - } - if mountValidationResp.MountType != alias.MountType { return nil, fmt.Errorf("mount accessor %q is not a mount of type %q", alias.MountAccessor, alias.MountType) } @@ -578,6 +797,7 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. Metadata: alias.Metadata, MountPath: mountValidationResp.MountPath, MountType: mountValidationResp.MountType, + Local: alias.Local, } err = i.sanitizeAlias(ctx, newAlias) diff --git a/vault/identity_store_aliases.go b/vault/identity_store_aliases.go index 4ba3356b8..893eba58b 100644 --- a/vault/identity_store_aliases.go +++ b/vault/identity_store_aliases.go @@ -188,7 +188,6 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc { name = alias.Name mountAccessor = alias.MountAccessor customMetadata = alias.CustomMetadata - case mountAccessor == "": // No change to mount accessor mountAccessor = alias.MountAccessor @@ -198,11 +197,10 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc { case len(customMetadata) == 0: // No change to custom metadata customMetadata = alias.CustomMetadata - default: // mountAccessor, name and customMetadata provided } - return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias, customMetadata) + return i.handleAliasUpdate(ctx, canonicalID, name, mountAccessor, alias, customMetadata) } } @@ -216,9 +214,6 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc { if mountEntry == nil { return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil } - if mountEntry.Local { - return logical.ErrorResponse(fmt.Sprintf("mount accessor %q is of a local mount", mountAccessor)), nil - } if mountEntry.NamespaceID != ns.ID { return logical.ErrorResponse("matching mount is in a different namespace than request"), logical.ErrPermissionDenied } @@ -230,30 +225,20 @@ func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc { if alias.NamespaceID != ns.ID { return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied } - - return i.handleAliasUpdate(ctx, req, alias.CanonicalID, name, mountAccessor, alias, customMetadata) + return i.handleAliasUpdate(ctx, canonicalID, name, mountAccessor, alias, customMetadata) } // At this point we know it's a new creation request - return i.handleAliasCreate(ctx, req, canonicalID, name, mountAccessor, customMetadata) + return i.handleAliasCreate(ctx, canonicalID, name, mountAccessor, mountEntry.Local, customMetadata) } } -func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string, customMetadata map[string]string) (*logical.Response, error) { +func (i *IdentityStore) handleAliasCreate(ctx context.Context, canonicalID, name, mountAccessor string, local bool, 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, - CustomMetadata: customMetadata, - } - - entity := &identity.Entity{} - - // If a canonical ID is provided pull up the entity and make sure we're in - // the right NS + var entity *identity.Entity if canonicalID != "" { entity, err = i.MemDBEntityByID(canonicalID, true) if err != nil { @@ -267,35 +252,62 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Requ } } + if entity == nil && local { + // Check to see if the entity creation should be forwarded. + entity, err = i.entityCreator.CreateEntity(ctx) + if err != nil { + return nil, err + } + } + + persist := false + // If the request was not forwarded, then this is the active node of the + // primary. Create the entity here itself. + if entity == nil { + persist = true + entity = new(identity.Entity) + err = i.sanitizeEntity(ctx, entity) + if err != nil { + return nil, err + } + } + for _, currentAlias := range entity.Aliases { if currentAlias.MountAccessor == mountAccessor { return logical.ErrorResponse("Alias already exists for requested entity and mount accessor"), nil } } - entity.Aliases = append(entity.Aliases, alias) - - // ID creation and other validations; This is more useful for new entities - // and may not perform anything for the existing entities. Placing the - // check here to make the flow common for both new and existing entities. - err = i.sanitizeEntity(ctx, entity) - if err != nil { - return nil, err - } - - // Set the canonical ID in the alias index. This should be done after - // sanitizing entity in case it's a new entity that didn't have an ID. - alias.CanonicalID = entity.ID - - // ID creation and other validations - err = i.sanitizeAlias(ctx, alias) - if err != nil { - return nil, err + var alias *identity.Alias + switch local { + case true: + alias, err = i.processLocalAlias(ctx, &logical.Alias{ + MountAccessor: mountAccessor, + Name: name, + Local: local, + CustomMetadata: customMetadata, + }, entity, false) + if err != nil { + return nil, err + } + default: + alias = &identity.Alias{ + MountAccessor: mountAccessor, + Name: name, + CustomMetadata: customMetadata, + CanonicalID: entity.ID, + } + err = i.sanitizeAlias(ctx, alias) + if err != nil { + return nil, err + } + entity.UpsertAlias(alias) + persist = true } // Index entity and its aliases in MemDB and persist entity along with // aliases in storage. - if err := i.upsertEntity(ctx, entity, nil, true); err != nil { + if err := i.upsertEntity(ctx, entity, nil, persist); err != nil { return nil, err } @@ -308,7 +320,7 @@ 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, customMetadata map[string]string) (*logical.Response, error) { +func (i *IdentityStore) handleAliasUpdate(ctx context.Context, 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 == "") && (strutil.EqualStringMaps(customMetadata, alias.CustomMetadata)) { @@ -350,9 +362,6 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ if mountEntry == nil { return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil } - if mountEntry.Local { - return logical.ErrorResponse(fmt.Sprintf("mount_accessor %q is of a local mount", mountAccessor)), nil - } if mountEntry.NamespaceID != alias.NamespaceID { return logical.ErrorResponse("given mount accessor is not in the same namespace as the existing alias"), logical.ErrPermissionDenied } @@ -373,8 +382,18 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ alias.CustomMetadata = customMetadata } + mountValidationResp := i.router.ValidateMountByAccessor(alias.MountAccessor) + if mountValidationResp == nil { + return nil, fmt.Errorf("invalid mount accessor %q", alias.MountAccessor) + } + newEntity := currentEntity if canonicalID != "" && canonicalID != alias.CanonicalID { + // Don't allow moving local aliases between entities. + if mountValidationResp.MountLocal { + return logical.ErrorResponse("local aliases can't be moved between entities"), nil + } + newEntity, err = i.MemDBEntityByID(canonicalID, true) if err != nil { return nil, err @@ -417,6 +436,25 @@ func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Requ currentEntity = nil } + if mountValidationResp.MountLocal { + alias, err = i.processLocalAlias(ctx, &logical.Alias{ + MountAccessor: mountAccessor, + Name: name, + Local: mountValidationResp.MountLocal, + CustomMetadata: customMetadata, + }, newEntity, true) + if err != nil { + return nil, err + } + + return &logical.Response{ + Data: map[string]interface{}{ + "id": alias.ID, + "canonical_id": newEntity.ID, + }, + }, nil + } + // Index entity and its aliases in MemDB and persist entity along with // aliases in storage. If the alias is being transferred over from // one entity to another, previous entity needs to get refreshed in MemDB @@ -523,6 +561,7 @@ func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identi respData["name"] = alias.Name respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs respData["namespace_id"] = alias.NamespaceID + respData["local"] = alias.Local if mountValidationResp := i.router.ValidateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { respData["mount_path"] = mountValidationResp.MountPath @@ -599,19 +638,39 @@ func (i *IdentityStore) pathAliasIDDelete() framework.OperationFunc { return nil, err } - // Persist the entity object - entityAsAny, err := ptypes.MarshalAny(entity) - if err != nil { - return nil, err - } - item := &storagepacker.Item{ - ID: entity.ID, - Message: entityAsAny, - } + switch alias.Local { + case true: + localAliases, err := i.parseLocalAliases(entity.ID) + if err != nil { + return nil, err + } - err = i.entityPacker.PutItem(ctx, item) - if err != nil { - return nil, err + if localAliases == nil { + return nil, nil + } + + for i, item := range localAliases.Aliases { + if item.ID == alias.ID { + localAliases.Aliases = append(localAliases.Aliases[:i], localAliases.Aliases[i+1:]...) + break + } + } + + marshaledAliases, err := ptypes.MarshalAny(localAliases) + if err != nil { + return nil, err + } + + if err := i.localAliasPacker.PutItem(ctx, &storagepacker.Item{ + ID: entity.ID, + Message: marshaledAliases, + }); err != nil { + return nil, err + } + default: + if err := i.persistEntity(ctx, entity); err != nil { + return nil, err + } } // Committing the transaction *after* successfully updating entity in diff --git a/vault/identity_store_aliases_test.go b/vault/identity_store_aliases_test.go index c44a9c52e..de3db6956 100644 --- a/vault/identity_store_aliases_test.go +++ b/vault/identity_store_aliases_test.go @@ -240,6 +240,7 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) { "testkey1": "testmetadatavalue1", "testkey2": "testmetadatavalue2", }, + LocalBucketKey: is.localAliasPacker.BucketKey(entity.ID), } txn = is.db.Txn(true) @@ -278,6 +279,7 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) { "testkey1": "testmetadatavalue1", "testkey3": "testmetadatavalue3", }, + LocalBucketKey: is.localAliasPacker.BucketKey(entity.ID), } txn = is.db.Txn(true) diff --git a/vault/identity_store_entities.go b/vault/identity_store_entities.go index 542edaa1e..54cb47ce5 100644 --- a/vault/identity_store_entities.go +++ b/vault/identity_store_entities.go @@ -373,6 +373,8 @@ func (i *IdentityStore) handleEntityReadCommon(ctx context.Context, entity *iden aliasMap["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs aliasMap["creation_time"] = ptypes.TimestampString(alias.CreationTime) aliasMap["last_update_time"] = ptypes.TimestampString(alias.LastUpdateTime) + aliasMap["local"] = alias.Local + aliasMap["custom_metadata"] = alias.CustomMetadata if mountValidationResp := i.router.ValidateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { aliasMap["mount_type"] = mountValidationResp.MountType diff --git a/vault/identity_store_oss.go b/vault/identity_store_oss.go index b662b7a26..748186d79 100644 --- a/vault/identity_store_oss.go +++ b/vault/identity_store_oss.go @@ -15,3 +15,7 @@ func (c *Core) PersistTOTPKey(context.Context, string, string, string) error { func (c *Core) SendGroupUpdate(context.Context, *identity.Group) (bool, error) { return false, nil } + +func (c *Core) CreateEntity(ctx context.Context) (*identity.Entity, error) { + return nil, nil +} diff --git a/vault/identity_store_schema.go b/vault/identity_store_schema.go index 76eab6664..2fdb38a87 100644 --- a/vault/identity_store_schema.go +++ b/vault/identity_store_schema.go @@ -70,6 +70,12 @@ func aliasesTableSchema(lowerCaseName bool) *memdb.TableSchema { Field: "NamespaceID", }, }, + "local_bucket_key": { + Name: "local_bucket_key", + Indexer: &memdb.StringFieldIndex{ + Field: "LocalBucketKey", + }, + }, }, } } diff --git a/vault/identity_store_structs.go b/vault/identity_store_structs.go index 0f8e63dcb..24e0f13dd 100644 --- a/vault/identity_store_structs.go +++ b/vault/identity_store_structs.go @@ -76,6 +76,12 @@ type IdentityStore struct { // buckets entityPacker *storagepacker.StoragePacker + // localAliasPacker is used to pack multiple local alias entries into lesser + // storage entries. This is also used to cache entities in the secondary + // clusters, those entities which were created by the primary but hasn't + // reached secondary via invalidations. + localAliasPacker *storagepacker.StoragePacker + // groupPacker is used to pack multiple group storage entries into 256 // buckets groupPacker *storagepacker.StoragePacker @@ -92,6 +98,7 @@ type IdentityStore struct { totpPersister TOTPPersister groupUpdater GroupUpdater tokenStorer TokenStorer + entityCreator EntityCreator } type groupDiff struct { @@ -136,3 +143,9 @@ type TokenStorer interface { } var _ TokenStorer = &Core{} + +type EntityCreator interface { + CreateEntity(ctx context.Context) (*identity.Entity, error) +} + +var _ EntityCreator = &Core{} diff --git a/vault/identity_store_test.go b/vault/identity_store_test.go index 78a409e26..70b5aca60 100644 --- a/vault/identity_store_test.go +++ b/vault/identity_store_test.go @@ -39,11 +39,12 @@ func TestIdentityStore_UnsealingWhenConflictingAliasNames(t *testing.T) { } alias := &identity.Alias{ - ID: "alias1", - CanonicalID: "entity1", - MountType: "github", - MountAccessor: meGH.Accessor, - Name: "githubuser", + ID: "alias1", + CanonicalID: "entity1", + MountType: "github", + MountAccessor: meGH.Accessor, + Name: "githubuser", + LocalBucketKey: c.identityStore.localAliasPacker.BucketKey("entity1"), } entity := &identity.Entity{ ID: "entity1", @@ -53,8 +54,8 @@ func TestIdentityStore_UnsealingWhenConflictingAliasNames(t *testing.T) { alias, }, NamespaceID: namespace.RootNamespaceID, + BucketKey: c.identityStore.entityPacker.BucketKey("entity1"), } - entity.BucketKey = c.identityStore.entityPacker.BucketKey(entity.ID) err = c.identityStore.upsertEntity(namespace.RootContext(nil), entity, nil, true) if err != nil { @@ -62,11 +63,12 @@ func TestIdentityStore_UnsealingWhenConflictingAliasNames(t *testing.T) { } alias2 := &identity.Alias{ - ID: "alias2", - CanonicalID: "entity2", - MountType: "github", - MountAccessor: meGH.Accessor, - Name: "GITHUBUSER", + ID: "alias2", + CanonicalID: "entity2", + MountType: "github", + MountAccessor: meGH.Accessor, + Name: "GITHUBUSER", + LocalBucketKey: c.identityStore.localAliasPacker.BucketKey("entity2"), } entity2 := &identity.Entity{ ID: "entity2", @@ -76,8 +78,8 @@ func TestIdentityStore_UnsealingWhenConflictingAliasNames(t *testing.T) { alias2, }, NamespaceID: namespace.RootNamespaceID, + BucketKey: c.identityStore.entityPacker.BucketKey("entity2"), } - entity2.BucketKey = c.identityStore.entityPacker.BucketKey(entity2.ID) // Persist the second entity directly without the regular flow. This will skip // merging of these enties. @@ -494,11 +496,12 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { } alias := &identity.Alias{ - ID: "alias1", - CanonicalID: "entity1", - MountType: "github", - MountAccessor: meGH.Accessor, - Name: "githubuser", + ID: "alias1", + CanonicalID: "entity1", + MountType: "github", + MountAccessor: meGH.Accessor, + Name: "githubuser", + LocalBucketKey: c.identityStore.localAliasPacker.BucketKey("entity1"), } entity := &identity.Entity{ ID: "entity1", @@ -508,19 +511,20 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { alias, }, NamespaceID: namespace.RootNamespaceID, + BucketKey: c.identityStore.entityPacker.BucketKey("entity1"), } - entity.BucketKey = c.identityStore.entityPacker.BucketKey(entity.ID) err = c.identityStore.upsertEntity(namespace.RootContext(nil), entity, nil, true) if err != nil { t.Fatal(err) } alias2 := &identity.Alias{ - ID: "alias2", - CanonicalID: "entity2", - MountType: "github", - MountAccessor: meGH.Accessor, - Name: "githubuser", + ID: "alias2", + CanonicalID: "entity2", + MountType: "github", + MountAccessor: meGH.Accessor, + Name: "githubuser", + LocalBucketKey: c.identityStore.localAliasPacker.BucketKey("entity2"), } entity2 := &identity.Entity{ ID: "entity2", @@ -530,10 +534,9 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { alias2, }, NamespaceID: namespace.RootNamespaceID, + BucketKey: c.identityStore.entityPacker.BucketKey("entity2"), } - entity2.BucketKey = c.identityStore.entityPacker.BucketKey(entity2.ID) - err = c.identityStore.upsertEntity(namespace.RootContext(nil), entity2, nil, true) if err != nil { t.Fatal(err) diff --git a/vault/identity_store_util.go b/vault/identity_store_util.go index 614eef952..c100f2cd3 100644 --- a/vault/identity_store_util.go +++ b/vault/identity_store_util.go @@ -23,6 +23,7 @@ import ( ) var errDuplicateIdentityName = errors.New("duplicate identity name") +var tmpSuffix = ".tmp" func (c *Core) SetLoadCaseSensitiveIdentityStore(caseSensitive bool) { c.loadCaseSensitiveIdentityStore = caseSensitive @@ -35,15 +36,20 @@ func (c *Core) loadIdentityStoreArtifacts(ctx context.Context) error { } loadFunc := func(context.Context) error { - err := c.identityStore.loadEntities(ctx) - if err != nil { + if err := c.identityStore.loadEntities(ctx); err != nil { return err } - err = c.identityStore.loadGroups(ctx) - if err != nil { + if err := c.identityStore.loadGroups(ctx); err != nil { return err } - return c.identityStore.loadOIDCClients(ctx) + if err := c.identityStore.loadOIDCClients(ctx); err != nil { + return err + } + if err := c.identityStore.loadCachedEntitiesOfLocalAliases(ctx); err != nil { + return err + } + + return nil } if !c.loadCaseSensitiveIdentityStore { @@ -53,7 +59,7 @@ func (c *Core) loadIdentityStoreArtifacts(ctx context.Context) error { case err == nil: // If it succeeds, all is well return nil - case err != nil && !errwrap.Contains(err, errDuplicateIdentityName.Error()): + case !errwrap.Contains(err, errDuplicateIdentityName.Error()): return err } } @@ -213,6 +219,134 @@ func (i *IdentityStore) loadGroups(ctx context.Context) error { return nil } +func (i *IdentityStore) loadCachedEntitiesOfLocalAliases(ctx context.Context) error { + // If we are performance secondary, load from temporary location those + // entities that were created by the secondary via RPCs to the primary, and + // also happen to have not yet been shipped to the secondary through + // performance replication. + if !i.localNode.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) { + return nil + } + + i.logger.Debug("loading cached entities of local aliases") + existing, err := i.localAliasPacker.View().List(ctx, localAliasesBucketsPrefix) + if err != nil { + return fmt.Errorf("failed to scan for cached entities of local alias: %w", err) + } + + i.logger.Debug("cached entities of local alias entries", "num_buckets", len(existing)) + + // Make the channels used for the worker pool + broker := make(chan string) + quit := make(chan bool) + + // Buffer these channels to prevent deadlocks + errs := make(chan error, len(existing)) + result := make(chan *storagepacker.Bucket, len(existing)) + + // Use a wait group + wg := &sync.WaitGroup{} + + // Create 64 workers to distribute work to + for j := 0; j < consts.ExpirationRestoreWorkerCount; j++ { + wg.Add(1) + go func() { + defer wg.Done() + + for { + select { + case key, ok := <-broker: + // broker has been closed, we are done + if !ok { + return + } + + bucket, err := i.localAliasPacker.GetBucket(ctx, localAliasesBucketsPrefix+key) + if err != nil { + errs <- err + continue + } + + // Write results out to the result channel + result <- bucket + + // quit early + case <-quit: + return + } + } + }() + } + + // Distribute the collected keys to the workers in a go routine + wg.Add(1) + go func() { + defer wg.Done() + for j, key := range existing { + if j%500 == 0 { + i.logger.Debug("cached entities of local aliases loading", "progress", j) + } + + select { + case <-quit: + return + + default: + broker <- key + } + } + + // Close the broker, causing worker routines to exit + close(broker) + }() + + // Restore each key by pulling from the result chan + for j := 0; j < len(existing); j++ { + select { + case err := <-errs: + // Close all go routines + close(quit) + + return err + + case bucket := <-result: + // If there is no entry, nothing to restore + if bucket == nil { + continue + } + + for _, item := range bucket.Items { + if !strings.HasSuffix(item.ID, tmpSuffix) { + continue + } + entity, err := i.parseCachedEntity(item) + if err != nil { + return err + } + ns, err := i.namespacer.NamespaceByID(ctx, entity.NamespaceID) + if err != nil { + return err + } + nsCtx := namespace.ContextWithNamespace(ctx, ns) + + err = i.upsertEntity(nsCtx, entity, nil, false) + if err != nil { + return fmt.Errorf("failed to update entity in MemDB: %w", err) + } + } + } + } + + // Let all go routines finish + wg.Wait() + + if i.logger.IsInfo() { + i.logger.Info("cached entities of local aliases restored") + } + + return nil +} + func (i *IdentityStore) loadEntities(ctx context.Context) error { // Accumulate existing entities i.logger.Debug("loading entities") @@ -349,6 +483,17 @@ func (i *IdentityStore) loadEntities(ctx context.Context) error { duplicatedAccessors[accessor] = struct{}{} } } + + localAliases, err := i.parseLocalAliases(entity.ID) + if err != nil { + return fmt.Errorf("failed to load local aliases from storage: %v", err) + } + if localAliases != nil { + for _, alias := range localAliases.Aliases { + entity.UpsertAlias(alias) + } + } + // Only update MemDB and don't hit the storage again err = i.upsertEntity(nsCtx, entity, nil, false) if err != nil { @@ -491,7 +636,7 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e } if strutil.StrListContains(aliasFactors, i.sanitizeName(alias.Name)+alias.MountAccessor) { - i.logger.Warn(errDuplicateIdentityName.Error(), "alias_name", alias.Name, "mount_accessor", alias.MountAccessor, "entity_name", entity.Name, "action", "delete one of the duplicate aliases") + i.logger.Warn(errDuplicateIdentityName.Error(), "alias_name", alias.Name, "mount_accessor", alias.MountAccessor, "local", alias.Local, "entity_name", entity.Name, "action", "delete one of the duplicate aliases") if !i.disableLowerCasedNames { return errDuplicateIdentityName } @@ -515,15 +660,7 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e if persist { // Persist the previous entity object - marshaledPreviousEntity, err := ptypes.MarshalAny(previousEntity) - if err != nil { - return err - } - err = i.entityPacker.PutItem(ctx, &storagepacker.Item{ - ID: previousEntity.ID, - Message: marshaledPreviousEntity, - }) - if err != nil { + if err := i.persistEntity(ctx, previousEntity); err != nil { return err } } @@ -536,20 +673,178 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e } if persist { - entityAsAny, err := ptypes.MarshalAny(entity) - if err != nil { + if err := i.persistEntity(ctx, entity); err != nil { return err } - item := &storagepacker.Item{ - ID: entity.ID, - Message: entityAsAny, - } + } - // Persist the entity object - err = i.entityPacker.PutItem(ctx, item) + return nil +} + +func (i *IdentityStore) processLocalAlias(ctx context.Context, lAlias *logical.Alias, entity *identity.Entity, updateDb bool) (*identity.Alias, error) { + if !lAlias.Local { + return nil, fmt.Errorf("alias is not local") + } + + mountValidationResp := i.router.ValidateMountByAccessor(lAlias.MountAccessor) + if mountValidationResp == nil { + return nil, fmt.Errorf("invalid mount accessor %q", lAlias.MountAccessor) + } + + if !mountValidationResp.MountLocal { + return nil, fmt.Errorf("mount accessor %q is not local", lAlias.MountAccessor) + } + + alias, err := i.MemDBAliasByFactors(lAlias.MountAccessor, lAlias.Name, false, false) + if err != nil { + return nil, err + } + + if alias == nil { + alias = &identity.Alias{} + } + + alias.CanonicalID = entity.ID + alias.Name = lAlias.Name + alias.MountAccessor = lAlias.MountAccessor + alias.Metadata = lAlias.Metadata + alias.MountPath = mountValidationResp.MountPath + alias.MountType = mountValidationResp.MountType + alias.Local = lAlias.Local + alias.CustomMetadata = lAlias.CustomMetadata + + if err := i.sanitizeAlias(ctx, alias); err != nil { + return nil, err + } + + entity.UpsertAlias(alias) + + localAliases, err := i.parseLocalAliases(entity.ID) + if err != nil { + return nil, err + } + if localAliases == nil { + localAliases = &identity.LocalAliases{} + } + + updated := false + for i, item := range localAliases.Aliases { + if item.ID == alias.ID { + localAliases.Aliases[i] = alias + updated = true + break + } + } + + if !updated { + localAliases.Aliases = append(localAliases.Aliases, alias) + } + + marshaledAliases, err := ptypes.MarshalAny(localAliases) + if err != nil { + return nil, err + } + if err := i.localAliasPacker.PutItem(ctx, &storagepacker.Item{ + ID: entity.ID, + Message: marshaledAliases, + }); err != nil { + return nil, err + } + + if updateDb { + txn := i.db.Txn(true) + defer txn.Abort() + if err := i.MemDBUpsertAliasInTxn(txn, alias, false); err != nil { + return nil, err + } + if err := i.upsertEntityInTxn(ctx, txn, entity, nil, false); err != nil { + return nil, err + } + txn.Commit() + } + + return alias, nil +} + +// cacheTemporaryEntity stores in secondary's storage, the entity returned by +// the primary cluster via the CreateEntity RPC. This is so that the secondary +// cluster knows and retains information about the existence of these entities +// before the replication invalidation informs the secondary of the same. This +// also happens to cover the case where the secondary's replication is lagging +// behind the primary by hours and/or days which sometimes may happen. Even if +// the nodes of the secondary are restarted in the interim, the cluster would +// still be aware of the entities. This temporary cache will be cleared when the +// invalidation hits the secondary nodes. +func (i *IdentityStore) cacheTemporaryEntity(ctx context.Context, entity *identity.Entity) error { + if i.localNode.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) && i.localNode.HAState() != consts.PerfStandby { + marshaledEntity, err := ptypes.MarshalAny(entity) if err != nil { return err } + if err := i.localAliasPacker.PutItem(ctx, &storagepacker.Item{ + ID: entity.ID + tmpSuffix, + Message: marshaledEntity, + }); err != nil { + return err + } + } + return nil +} + +func (i *IdentityStore) persistEntity(ctx context.Context, entity *identity.Entity) error { + // If the entity that is passed into this function is resulting from a memdb + // query without cloning, then modifying it will result in a direct DB edit, + // bypassing the transaction. To avoid any surprises arising from this + // effect, work on a replica of the entity struct. + var err error + entity, err = entity.Clone() + if err != nil { + return err + } + + // Separate the local and non-local aliases. + var localAliases []*identity.Alias + var nonLocalAliases []*identity.Alias + for _, alias := range entity.Aliases { + switch alias.Local { + case true: + localAliases = append(localAliases, alias) + default: + nonLocalAliases = append(nonLocalAliases, alias) + } + } + + // Store the entity with non-local aliases. + entity.Aliases = nonLocalAliases + marshaledEntity, err := ptypes.MarshalAny(entity) + if err != nil { + return err + } + if err := i.entityPacker.PutItem(ctx, &storagepacker.Item{ + ID: entity.ID, + Message: marshaledEntity, + }); err != nil { + return err + } + + if len(localAliases) == 0 { + return nil + } + + // Store the local aliases separately. + aliases := &identity.LocalAliases{ + Aliases: localAliases, + } + + marshaledAliases, err := ptypes.MarshalAny(aliases) + if err != nil { + return err + } + if err := i.localAliasPacker.PutItem(ctx, &storagepacker.Item{ + ID: entity.ID, + Message: marshaledAliases, + }); err != nil { + return err } return nil @@ -875,6 +1170,31 @@ func (i *IdentityStore) MemDBEntityByNameInTxn(ctx context.Context, txn *memdb.T return entity, nil } +func (i *IdentityStore) MemDBLocalAliasesByBucketKeyInTxn(txn *memdb.Txn, bucketKey string) ([]*identity.Alias, error) { + if txn == nil { + return nil, fmt.Errorf("nil txn") + } + + if bucketKey == "" { + return nil, fmt.Errorf("empty bucket key") + } + + iter, err := txn.Get(entityAliasesTable, "local_bucket_key", bucketKey) + if err != nil { + return nil, fmt.Errorf("failed to lookup aliases using local bucket entry key hash: %w", err) + } + + var aliases []*identity.Alias + for item := iter.Next(); item != nil; item = iter.Next() { + alias := item.(*identity.Alias) + if alias.Local { + aliases = append(aliases, alias) + } + } + + return aliases, nil +} + func (i *IdentityStore) MemDBEntitiesByBucketKeyInTxn(txn *memdb.Txn, bucketKey string) ([]*identity.Entity, error) { if txn == nil { return nil, fmt.Errorf("nil txn") @@ -890,8 +1210,12 @@ func (i *IdentityStore) MemDBEntitiesByBucketKeyInTxn(txn *memdb.Txn, bucketKey } var entities []*identity.Entity - for entity := entitiesIter.Next(); entity != nil; entity = entitiesIter.Next() { - entities = append(entities, entity.(*identity.Entity)) + for item := entitiesIter.Next(); item != nil; item = entitiesIter.Next() { + entity, err := item.(*identity.Entity).Clone() + if err != nil { + return nil, err + } + entities = append(entities, entity) } return entities, nil @@ -1029,6 +1353,8 @@ func (i *IdentityStore) sanitizeAlias(ctx context.Context, alias *identity.Alias if err != nil { return fmt.Errorf("failed to generate alias ID") } + + alias.LocalBucketKey = i.localAliasPacker.BucketKey(alias.CanonicalID) } if alias.NamespaceID == "" { @@ -2160,6 +2486,7 @@ func (i *IdentityStore) handleAliasListCommon(ctx context.Context, groupAlias bo "canonical_id": alias.CanonicalID, "mount_accessor": alias.MountAccessor, "custom_metadata": alias.CustomMetadata, + "local": alias.Local, } mi, ok := mountAccessorMap[alias.MountAccessor] diff --git a/vault/request_handling.go b/vault/request_handling.go index 16e233e8f..42940c890 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -1326,24 +1326,32 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re if auth.Alias != nil && mEntry != nil && - !mEntry.Local && c.identityStore != nil { // Overwrite the mount type and mount path in the alias // information auth.Alias.MountType = req.MountType auth.Alias.MountAccessor = req.MountAccessor + auth.Alias.Local = mEntry.Local if auth.Alias.Name == "" { return nil, nil, fmt.Errorf("missing name in alias") } var err error - // Fetch the entity for the alias, or create an entity if one // doesn't exist. entity, err = c.identityStore.CreateOrFetchEntity(ctx, auth.Alias) if err != nil { - entity, err = possiblyForwardAliasCreation(ctx, c, err, auth, entity) + switch auth.Alias.Local { + case true: + entity, err = possiblyForwardEntityCreation(ctx, c, err, auth, entity) + if err != nil && strings.Contains(err.Error(), errCreateEntityUnimplemented) { + resp.AddWarning("primary cluster doesn't yet issue entities for local auth mounts; falling back to not issuing entities for local auth mounts") + goto CREATE_TOKEN + } + default: + entity, err = possiblyForwardAliasCreation(ctx, c, err, auth, entity) + } } if err != nil { return nil, nil, err @@ -1364,6 +1372,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re auth.GroupAliases = validAliases } + CREATE_TOKEN: // Determine the source of the login source := c.router.MatchingMount(ctx, req.Path) source = strings.TrimPrefix(source, credentialRoutePrefix) diff --git a/vault/request_handling_util.go b/vault/request_handling_util.go index a0634f505..0487199b2 100644 --- a/vault/request_handling_util.go +++ b/vault/request_handling_util.go @@ -53,3 +53,9 @@ func getAuthRegisterFunc(c *Core) (RegisterAuthFunc, error) { func possiblyForwardAliasCreation(ctx context.Context, c *Core, inErr error, auth *logical.Auth, entity *identity.Entity) (*identity.Entity, error) { return entity, inErr } + +var errCreateEntityUnimplemented = "create entity unimplemented in the server" + +func possiblyForwardEntityCreation(ctx context.Context, c *Core, inErr error, auth *logical.Auth, entity *identity.Entity) (*identity.Entity, error) { + return entity, inErr +}