From f3c85bf7472f686dfb658e09e9f0237c8ab7818b Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 08:39:07 -0300 Subject: [PATCH 01/19] api: Update IS config proto --- api/api.md | 8 + api/api.swagger.json | 11 + api/identityserver.proto | 5 + pkg/ttnpb/identityserver.pb.go | 511 +++++++++++++--------- pkg/ttnpb/identityserver.pb.paths.fm.go | 12 + pkg/ttnpb/identityserver.pb.setters.fm.go | 45 ++ pkg/ttnpb/identityserver.pb.validate.go | 106 +++++ sdk/js/generated/api.json | 36 ++ 8 files changed, 520 insertions(+), 214 deletions(-) diff --git a/api/api.md b/api/api.md index 088a682b35..aab0577cf7 100644 --- a/api/api.md +++ b/api/api.md @@ -357,6 +357,7 @@ - [Message `GetIsConfigurationResponse`](#ttn.lorawan.v3.GetIsConfigurationResponse) - [Message `IsConfiguration`](#ttn.lorawan.v3.IsConfiguration) - [Message `IsConfiguration.AdminRights`](#ttn.lorawan.v3.IsConfiguration.AdminRights) + - [Message `IsConfiguration.CollaboratorRights`](#ttn.lorawan.v3.IsConfiguration.CollaboratorRights) - [Message `IsConfiguration.EndDevicePicture`](#ttn.lorawan.v3.IsConfiguration.EndDevicePicture) - [Message `IsConfiguration.ProfilePicture`](#ttn.lorawan.v3.IsConfiguration.ProfilePicture) - [Message `IsConfiguration.UserLogin`](#ttn.lorawan.v3.IsConfiguration.UserLogin) @@ -5290,6 +5291,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `user_rights` | [`IsConfiguration.UserRights`](#ttn.lorawan.v3.IsConfiguration.UserRights) | | | | `user_login` | [`IsConfiguration.UserLogin`](#ttn.lorawan.v3.IsConfiguration.UserLogin) | | | | `admin_rights` | [`IsConfiguration.AdminRights`](#ttn.lorawan.v3.IsConfiguration.AdminRights) | | | +| `collaborator_rights` | [`IsConfiguration.CollaboratorRights`](#ttn.lorawan.v3.IsConfiguration.CollaboratorRights) | | | ### Message `IsConfiguration.AdminRights` @@ -5297,6 +5299,12 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ---- | ----- | ----------- | | `all` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | +### Message `IsConfiguration.CollaboratorRights` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `set_others_as_contacts` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | + ### Message `IsConfiguration.EndDevicePicture` | Field | Type | Label | Description | diff --git a/api/api.swagger.json b/api/api.swagger.json index 584c4bb88d..4f10dc8995 100644 --- a/api/api.swagger.json +++ b/api/api.swagger.json @@ -17601,6 +17601,14 @@ } } }, + "IsConfigurationCollaboratorRights": { + "type": "object", + "properties": { + "set_others_as_contacts": { + "type": "boolean" + } + } + }, "IsConfigurationEndDevicePicture": { "type": "object", "properties": { @@ -22305,6 +22313,9 @@ }, "admin_rights": { "$ref": "#/definitions/IsConfigurationAdminRights" + }, + "collaborator_rights": { + "$ref": "#/definitions/IsConfigurationCollaboratorRights" } } }, diff --git a/api/identityserver.proto b/api/identityserver.proto index 6fad88a0f1..fc76d8d99c 100644 --- a/api/identityserver.proto +++ b/api/identityserver.proto @@ -125,6 +125,11 @@ message IsConfiguration { reserved 10; reserved "application_limits"; reserved 11; reserved "organization_limits"; reserved 12; reserved "user_limits"; + + message CollaboratorRights { + google.protobuf.BoolValue set_others_as_contacts = 1; + } + CollaboratorRights collaborator_rights = 13; } message GetIsConfigurationResponse { diff --git a/pkg/ttnpb/identityserver.pb.go b/pkg/ttnpb/identityserver.pb.go index 468efba1ac..148e83373f 100644 --- a/pkg/ttnpb/identityserver.pb.go +++ b/pkg/ttnpb/identityserver.pb.go @@ -210,12 +210,13 @@ type IsConfiguration struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - UserRegistration *IsConfiguration_UserRegistration `protobuf:"bytes,3,opt,name=user_registration,json=userRegistration,proto3" json:"user_registration,omitempty"` - ProfilePicture *IsConfiguration_ProfilePicture `protobuf:"bytes,4,opt,name=profile_picture,json=profilePicture,proto3" json:"profile_picture,omitempty"` - EndDevicePicture *IsConfiguration_EndDevicePicture `protobuf:"bytes,5,opt,name=end_device_picture,json=endDevicePicture,proto3" json:"end_device_picture,omitempty"` - UserRights *IsConfiguration_UserRights `protobuf:"bytes,6,opt,name=user_rights,json=userRights,proto3" json:"user_rights,omitempty"` - UserLogin *IsConfiguration_UserLogin `protobuf:"bytes,7,opt,name=user_login,json=userLogin,proto3" json:"user_login,omitempty"` - AdminRights *IsConfiguration_AdminRights `protobuf:"bytes,8,opt,name=admin_rights,json=adminRights,proto3" json:"admin_rights,omitempty"` + UserRegistration *IsConfiguration_UserRegistration `protobuf:"bytes,3,opt,name=user_registration,json=userRegistration,proto3" json:"user_registration,omitempty"` + ProfilePicture *IsConfiguration_ProfilePicture `protobuf:"bytes,4,opt,name=profile_picture,json=profilePicture,proto3" json:"profile_picture,omitempty"` + EndDevicePicture *IsConfiguration_EndDevicePicture `protobuf:"bytes,5,opt,name=end_device_picture,json=endDevicePicture,proto3" json:"end_device_picture,omitempty"` + UserRights *IsConfiguration_UserRights `protobuf:"bytes,6,opt,name=user_rights,json=userRights,proto3" json:"user_rights,omitempty"` + UserLogin *IsConfiguration_UserLogin `protobuf:"bytes,7,opt,name=user_login,json=userLogin,proto3" json:"user_login,omitempty"` + AdminRights *IsConfiguration_AdminRights `protobuf:"bytes,8,opt,name=admin_rights,json=adminRights,proto3" json:"admin_rights,omitempty"` + CollaboratorRights *IsConfiguration_CollaboratorRights `protobuf:"bytes,13,opt,name=collaborator_rights,json=collaboratorRights,proto3" json:"collaborator_rights,omitempty"` } func (x *IsConfiguration) Reset() { @@ -292,6 +293,13 @@ func (x *IsConfiguration) GetAdminRights() *IsConfiguration_AdminRights { return nil } +func (x *IsConfiguration) GetCollaboratorRights() *IsConfiguration_CollaboratorRights { + if x != nil { + return x.CollaboratorRights + } + return nil +} + type GetIsConfigurationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -795,6 +803,53 @@ func (x *IsConfiguration_AdminRights) GetAll() *wrapperspb.BoolValue { return nil } +type IsConfiguration_CollaboratorRights struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SetOthersAsContacts *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=set_others_as_contacts,json=setOthersAsContacts,proto3" json:"set_others_as_contacts,omitempty"` +} + +func (x *IsConfiguration_CollaboratorRights) Reset() { + *x = IsConfiguration_CollaboratorRights{} + if protoimpl.UnsafeEnabled { + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IsConfiguration_CollaboratorRights) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsConfiguration_CollaboratorRights) ProtoMessage() {} + +func (x *IsConfiguration_CollaboratorRights) ProtoReflect() protoreflect.Message { + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[12] + 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 IsConfiguration_CollaboratorRights.ProtoReflect.Descriptor instead. +func (*IsConfiguration_CollaboratorRights) Descriptor() ([]byte, []int) { + return file_lorawan_stack_api_identityserver_proto_rawDescGZIP(), []int{2, 6} +} + +func (x *IsConfiguration_CollaboratorRights) GetSetOthersAsContacts() *wrapperspb.BoolValue { + if x != nil { + return x.SetOthersAsContacts + } + return nil +} + type IsConfiguration_UserRegistration_Invitation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -807,7 +862,7 @@ type IsConfiguration_UserRegistration_Invitation struct { func (x *IsConfiguration_UserRegistration_Invitation) Reset() { *x = IsConfiguration_UserRegistration_Invitation{} if protoimpl.UnsafeEnabled { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[12] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -820,7 +875,7 @@ func (x *IsConfiguration_UserRegistration_Invitation) String() string { func (*IsConfiguration_UserRegistration_Invitation) ProtoMessage() {} func (x *IsConfiguration_UserRegistration_Invitation) ProtoReflect() protoreflect.Message { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[12] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -861,7 +916,7 @@ type IsConfiguration_UserRegistration_ContactInfoValidation struct { func (x *IsConfiguration_UserRegistration_ContactInfoValidation) Reset() { *x = IsConfiguration_UserRegistration_ContactInfoValidation{} if protoimpl.UnsafeEnabled { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[13] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -874,7 +929,7 @@ func (x *IsConfiguration_UserRegistration_ContactInfoValidation) String() string func (*IsConfiguration_UserRegistration_ContactInfoValidation) ProtoMessage() {} func (x *IsConfiguration_UserRegistration_ContactInfoValidation) ProtoReflect() protoreflect.Message { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[13] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -908,7 +963,7 @@ type IsConfiguration_UserRegistration_AdminApproval struct { func (x *IsConfiguration_UserRegistration_AdminApproval) Reset() { *x = IsConfiguration_UserRegistration_AdminApproval{} if protoimpl.UnsafeEnabled { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[14] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -921,7 +976,7 @@ func (x *IsConfiguration_UserRegistration_AdminApproval) String() string { func (*IsConfiguration_UserRegistration_AdminApproval) ProtoMessage() {} func (x *IsConfiguration_UserRegistration_AdminApproval) ProtoReflect() protoreflect.Message { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[14] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -959,7 +1014,7 @@ type IsConfiguration_UserRegistration_PasswordRequirements struct { func (x *IsConfiguration_UserRegistration_PasswordRequirements) Reset() { *x = IsConfiguration_UserRegistration_PasswordRequirements{} if protoimpl.UnsafeEnabled { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[15] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -972,7 +1027,7 @@ func (x *IsConfiguration_UserRegistration_PasswordRequirements) String() string func (*IsConfiguration_UserRegistration_PasswordRequirements) ProtoMessage() {} func (x *IsConfiguration_UserRegistration_PasswordRequirements) ProtoReflect() protoreflect.Message { - mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[15] + mi := &file_lorawan_stack_api_identityserver_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1097,7 +1152,7 @@ var file_lorawan_stack_api_identityserver_proto_rawDesc = []byte{ 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x42, 0x0f, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x88, 0x13, 0x0a, 0x0f, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x75, 0x65, 0x73, 0x74, 0x22, 0xd4, 0x14, 0x0a, 0x0f, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5d, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, @@ -1130,152 +1185,165 @@ var file_lorawan_stack_api_identityserver_proto_rawDesc = []byte{ 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x0b, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x1a, 0xd6, 0x08, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, - 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x69, - 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x7e, 0x0a, 0x17, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x63, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0e, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, - 0x6c, 0x52, 0x0d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, - 0x12, 0x7a, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x45, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x6e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x63, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x12, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, + 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x1a, 0xd6, 0x08, 0x0a, + 0x10, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x7e, + 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x46, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x7c, 0x0a, 0x0a, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x09, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x54, 0x74, 0x6c, 0x1a, 0x4f, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, - 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x1a, 0x47, 0x0a, 0x0d, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, - 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x1a, 0xcf, - 0x02, 0x0a, 0x14, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x12, 0x3b, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x12, 0x41, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x75, 0x70, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x64, 0x69, 0x67, 0x69, - 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x44, 0x69, 0x67, 0x69, 0x74, - 0x73, 0x12, 0x3d, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, - 0x1a, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, + 0x0a, 0x0e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, + 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x0d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x7a, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x14, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x7c, 0x0a, 0x0a, 0x49, + 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3d, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x72, - 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x47, 0x72, 0x61, - 0x76, 0x61, 0x74, 0x61, 0x72, 0x1a, 0x55, 0x0a, 0x10, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0xb0, 0x02, 0x0a, - 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x13, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x03, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x12, 0x36, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x74, 0x6c, 0x1a, 0x4f, 0x0a, 0x15, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x12, 0x4d, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x67, 0x61, 0x6e, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, - 0x63, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x56, 0x0a, 0x19, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x17, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x1a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x69, 0x67, - 0x68, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x1a, 0x47, 0x0a, 0x0d, 0x41, 0x64, + 0x6d, 0x69, 0x6e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x08, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x1a, 0xcf, 0x02, 0x0a, 0x14, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x0a, + 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, + 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x3b, 0x0a, 0x0a, 0x6d, 0x61, 0x78, + 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6d, 0x61, 0x78, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x41, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x75, 0x70, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x6d, 0x69, 0x6e, + 0x55, 0x70, 0x70, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, + 0x5f, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6d, 0x69, 0x6e, + 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x70, + 0x65, 0x63, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x61, 0x6c, 0x1a, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x61, 0x6c, - 0x6c, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, - 0x0b, 0x10, 0x0c, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x12, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x13, 0x6f, - 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x73, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x22, - 0x63, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, - 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x68, 0x0a, 0x0c, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x12, 0x58, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x32, 0x8b, - 0x01, 0x0a, 0x02, 0x49, 0x73, 0x12, 0x84, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, - 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x69, 0x73, 0x2f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x31, 0x5a, 0x2f, - 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3d, 0x0a, 0x0c, 0x75, + 0x73, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x75, + 0x73, 0x65, 0x47, 0x72, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x1a, 0x55, 0x0a, 0x10, 0x45, 0x6e, + 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x41, + 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x1a, 0xb0, 0x02, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, + 0x12, 0x4b, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, + 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x43, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x4d, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x12, 0x56, 0x0a, 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x1a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x6d, + 0x69, 0x6e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x1a, 0x65, 0x0a, 0x12, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, + 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x16, + 0x73, 0x65, 0x74, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x73, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x65, 0x74, 0x4f, 0x74, 0x68, + 0x65, 0x72, 0x73, 0x41, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x4a, 0x04, 0x08, + 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x4a, + 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x12, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x13, 0x6f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x0b, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x22, 0x63, 0x0a, 0x1a, 0x47, + 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x32, 0x68, 0x0a, 0x0c, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x12, 0x58, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, + 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x32, 0x8b, 0x01, 0x0a, 0x02, 0x49, + 0x73, 0x12, 0x84, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x69, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x6f, 0x2e, 0x74, + 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, + 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1290,7 +1358,7 @@ func file_lorawan_stack_api_identityserver_proto_rawDescGZIP() []byte { return file_lorawan_stack_api_identityserver_proto_rawDescData } -var file_lorawan_stack_api_identityserver_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_lorawan_stack_api_identityserver_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_lorawan_stack_api_identityserver_proto_goTypes = []interface{}{ (*AuthInfoResponse)(nil), // 0: ttn.lorawan.v3.AuthInfoResponse (*GetIsConfigurationRequest)(nil), // 1: ttn.lorawan.v3.GetIsConfigurationRequest @@ -1304,70 +1372,73 @@ var file_lorawan_stack_api_identityserver_proto_goTypes = []interface{}{ (*IsConfiguration_UserRights)(nil), // 9: ttn.lorawan.v3.IsConfiguration.UserRights (*IsConfiguration_UserLogin)(nil), // 10: ttn.lorawan.v3.IsConfiguration.UserLogin (*IsConfiguration_AdminRights)(nil), // 11: ttn.lorawan.v3.IsConfiguration.AdminRights - (*IsConfiguration_UserRegistration_Invitation)(nil), // 12: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation - (*IsConfiguration_UserRegistration_ContactInfoValidation)(nil), // 13: ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation - (*IsConfiguration_UserRegistration_AdminApproval)(nil), // 14: ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval - (*IsConfiguration_UserRegistration_PasswordRequirements)(nil), // 15: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements - (*OAuthAccessToken)(nil), // 16: ttn.lorawan.v3.OAuthAccessToken - (*UserSession)(nil), // 17: ttn.lorawan.v3.UserSession - (*Rights)(nil), // 18: ttn.lorawan.v3.Rights - (*APIKey)(nil), // 19: ttn.lorawan.v3.APIKey - (*EntityIdentifiers)(nil), // 20: ttn.lorawan.v3.EntityIdentifiers - (*GatewayIdentifiers)(nil), // 21: ttn.lorawan.v3.GatewayIdentifiers - (Right)(0), // 22: ttn.lorawan.v3.Right - (*wrapperspb.BoolValue)(nil), // 23: google.protobuf.BoolValue - (*durationpb.Duration)(nil), // 24: google.protobuf.Duration - (*wrapperspb.UInt32Value)(nil), // 25: google.protobuf.UInt32Value - (*emptypb.Empty)(nil), // 26: google.protobuf.Empty + (*IsConfiguration_CollaboratorRights)(nil), // 12: ttn.lorawan.v3.IsConfiguration.CollaboratorRights + (*IsConfiguration_UserRegistration_Invitation)(nil), // 13: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation + (*IsConfiguration_UserRegistration_ContactInfoValidation)(nil), // 14: ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation + (*IsConfiguration_UserRegistration_AdminApproval)(nil), // 15: ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval + (*IsConfiguration_UserRegistration_PasswordRequirements)(nil), // 16: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements + (*OAuthAccessToken)(nil), // 17: ttn.lorawan.v3.OAuthAccessToken + (*UserSession)(nil), // 18: ttn.lorawan.v3.UserSession + (*Rights)(nil), // 19: ttn.lorawan.v3.Rights + (*APIKey)(nil), // 20: ttn.lorawan.v3.APIKey + (*EntityIdentifiers)(nil), // 21: ttn.lorawan.v3.EntityIdentifiers + (*GatewayIdentifiers)(nil), // 22: ttn.lorawan.v3.GatewayIdentifiers + (Right)(0), // 23: ttn.lorawan.v3.Right + (*wrapperspb.BoolValue)(nil), // 24: google.protobuf.BoolValue + (*durationpb.Duration)(nil), // 25: google.protobuf.Duration + (*wrapperspb.UInt32Value)(nil), // 26: google.protobuf.UInt32Value + (*emptypb.Empty)(nil), // 27: google.protobuf.Empty } var file_lorawan_stack_api_identityserver_proto_depIdxs = []int32{ 4, // 0: ttn.lorawan.v3.AuthInfoResponse.api_key:type_name -> ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess - 16, // 1: ttn.lorawan.v3.AuthInfoResponse.oauth_access_token:type_name -> ttn.lorawan.v3.OAuthAccessToken - 17, // 2: ttn.lorawan.v3.AuthInfoResponse.user_session:type_name -> ttn.lorawan.v3.UserSession + 17, // 1: ttn.lorawan.v3.AuthInfoResponse.oauth_access_token:type_name -> ttn.lorawan.v3.OAuthAccessToken + 18, // 2: ttn.lorawan.v3.AuthInfoResponse.user_session:type_name -> ttn.lorawan.v3.UserSession 5, // 3: ttn.lorawan.v3.AuthInfoResponse.gateway_token:type_name -> ttn.lorawan.v3.AuthInfoResponse.GatewayToken - 18, // 4: ttn.lorawan.v3.AuthInfoResponse.universal_rights:type_name -> ttn.lorawan.v3.Rights + 19, // 4: ttn.lorawan.v3.AuthInfoResponse.universal_rights:type_name -> ttn.lorawan.v3.Rights 6, // 5: ttn.lorawan.v3.IsConfiguration.user_registration:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration 7, // 6: ttn.lorawan.v3.IsConfiguration.profile_picture:type_name -> ttn.lorawan.v3.IsConfiguration.ProfilePicture 8, // 7: ttn.lorawan.v3.IsConfiguration.end_device_picture:type_name -> ttn.lorawan.v3.IsConfiguration.EndDevicePicture 9, // 8: ttn.lorawan.v3.IsConfiguration.user_rights:type_name -> ttn.lorawan.v3.IsConfiguration.UserRights 10, // 9: ttn.lorawan.v3.IsConfiguration.user_login:type_name -> ttn.lorawan.v3.IsConfiguration.UserLogin 11, // 10: ttn.lorawan.v3.IsConfiguration.admin_rights:type_name -> ttn.lorawan.v3.IsConfiguration.AdminRights - 2, // 11: ttn.lorawan.v3.GetIsConfigurationResponse.configuration:type_name -> ttn.lorawan.v3.IsConfiguration - 19, // 12: ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess.api_key:type_name -> ttn.lorawan.v3.APIKey - 20, // 13: ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 21, // 14: ttn.lorawan.v3.AuthInfoResponse.GatewayToken.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers - 22, // 15: ttn.lorawan.v3.AuthInfoResponse.GatewayToken.rights:type_name -> ttn.lorawan.v3.Right - 12, // 16: ttn.lorawan.v3.IsConfiguration.UserRegistration.invitation:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation - 13, // 17: ttn.lorawan.v3.IsConfiguration.UserRegistration.contact_info_validation:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation - 14, // 18: ttn.lorawan.v3.IsConfiguration.UserRegistration.admin_approval:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval - 15, // 19: ttn.lorawan.v3.IsConfiguration.UserRegistration.password_requirements:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements - 23, // 20: ttn.lorawan.v3.IsConfiguration.ProfilePicture.disable_upload:type_name -> google.protobuf.BoolValue - 23, // 21: ttn.lorawan.v3.IsConfiguration.ProfilePicture.use_gravatar:type_name -> google.protobuf.BoolValue - 23, // 22: ttn.lorawan.v3.IsConfiguration.EndDevicePicture.disable_upload:type_name -> google.protobuf.BoolValue - 23, // 23: ttn.lorawan.v3.IsConfiguration.UserRights.create_applications:type_name -> google.protobuf.BoolValue - 23, // 24: ttn.lorawan.v3.IsConfiguration.UserRights.create_clients:type_name -> google.protobuf.BoolValue - 23, // 25: ttn.lorawan.v3.IsConfiguration.UserRights.create_gateways:type_name -> google.protobuf.BoolValue - 23, // 26: ttn.lorawan.v3.IsConfiguration.UserRights.create_organizations:type_name -> google.protobuf.BoolValue - 23, // 27: ttn.lorawan.v3.IsConfiguration.UserLogin.disable_credentials_login:type_name -> google.protobuf.BoolValue - 23, // 28: ttn.lorawan.v3.IsConfiguration.AdminRights.all:type_name -> google.protobuf.BoolValue - 23, // 29: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation.required:type_name -> google.protobuf.BoolValue - 24, // 30: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation.token_ttl:type_name -> google.protobuf.Duration - 23, // 31: ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation.required:type_name -> google.protobuf.BoolValue - 23, // 32: ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval.required:type_name -> google.protobuf.BoolValue - 25, // 33: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_length:type_name -> google.protobuf.UInt32Value - 25, // 34: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.max_length:type_name -> google.protobuf.UInt32Value - 25, // 35: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_uppercase:type_name -> google.protobuf.UInt32Value - 25, // 36: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_digits:type_name -> google.protobuf.UInt32Value - 25, // 37: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_special:type_name -> google.protobuf.UInt32Value - 26, // 38: ttn.lorawan.v3.EntityAccess.AuthInfo:input_type -> google.protobuf.Empty - 1, // 39: ttn.lorawan.v3.Is.GetConfiguration:input_type -> ttn.lorawan.v3.GetIsConfigurationRequest - 0, // 40: ttn.lorawan.v3.EntityAccess.AuthInfo:output_type -> ttn.lorawan.v3.AuthInfoResponse - 3, // 41: ttn.lorawan.v3.Is.GetConfiguration:output_type -> ttn.lorawan.v3.GetIsConfigurationResponse - 40, // [40:42] is the sub-list for method output_type - 38, // [38:40] is the sub-list for method input_type - 38, // [38:38] is the sub-list for extension type_name - 38, // [38:38] is the sub-list for extension extendee - 0, // [0:38] is the sub-list for field type_name + 12, // 11: ttn.lorawan.v3.IsConfiguration.collaborator_rights:type_name -> ttn.lorawan.v3.IsConfiguration.CollaboratorRights + 2, // 12: ttn.lorawan.v3.GetIsConfigurationResponse.configuration:type_name -> ttn.lorawan.v3.IsConfiguration + 20, // 13: ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess.api_key:type_name -> ttn.lorawan.v3.APIKey + 21, // 14: ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 22, // 15: ttn.lorawan.v3.AuthInfoResponse.GatewayToken.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 23, // 16: ttn.lorawan.v3.AuthInfoResponse.GatewayToken.rights:type_name -> ttn.lorawan.v3.Right + 13, // 17: ttn.lorawan.v3.IsConfiguration.UserRegistration.invitation:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation + 14, // 18: ttn.lorawan.v3.IsConfiguration.UserRegistration.contact_info_validation:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation + 15, // 19: ttn.lorawan.v3.IsConfiguration.UserRegistration.admin_approval:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval + 16, // 20: ttn.lorawan.v3.IsConfiguration.UserRegistration.password_requirements:type_name -> ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements + 24, // 21: ttn.lorawan.v3.IsConfiguration.ProfilePicture.disable_upload:type_name -> google.protobuf.BoolValue + 24, // 22: ttn.lorawan.v3.IsConfiguration.ProfilePicture.use_gravatar:type_name -> google.protobuf.BoolValue + 24, // 23: ttn.lorawan.v3.IsConfiguration.EndDevicePicture.disable_upload:type_name -> google.protobuf.BoolValue + 24, // 24: ttn.lorawan.v3.IsConfiguration.UserRights.create_applications:type_name -> google.protobuf.BoolValue + 24, // 25: ttn.lorawan.v3.IsConfiguration.UserRights.create_clients:type_name -> google.protobuf.BoolValue + 24, // 26: ttn.lorawan.v3.IsConfiguration.UserRights.create_gateways:type_name -> google.protobuf.BoolValue + 24, // 27: ttn.lorawan.v3.IsConfiguration.UserRights.create_organizations:type_name -> google.protobuf.BoolValue + 24, // 28: ttn.lorawan.v3.IsConfiguration.UserLogin.disable_credentials_login:type_name -> google.protobuf.BoolValue + 24, // 29: ttn.lorawan.v3.IsConfiguration.AdminRights.all:type_name -> google.protobuf.BoolValue + 24, // 30: ttn.lorawan.v3.IsConfiguration.CollaboratorRights.set_others_as_contacts:type_name -> google.protobuf.BoolValue + 24, // 31: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation.required:type_name -> google.protobuf.BoolValue + 25, // 32: ttn.lorawan.v3.IsConfiguration.UserRegistration.Invitation.token_ttl:type_name -> google.protobuf.Duration + 24, // 33: ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation.required:type_name -> google.protobuf.BoolValue + 24, // 34: ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval.required:type_name -> google.protobuf.BoolValue + 26, // 35: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_length:type_name -> google.protobuf.UInt32Value + 26, // 36: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.max_length:type_name -> google.protobuf.UInt32Value + 26, // 37: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_uppercase:type_name -> google.protobuf.UInt32Value + 26, // 38: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_digits:type_name -> google.protobuf.UInt32Value + 26, // 39: ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements.min_special:type_name -> google.protobuf.UInt32Value + 27, // 40: ttn.lorawan.v3.EntityAccess.AuthInfo:input_type -> google.protobuf.Empty + 1, // 41: ttn.lorawan.v3.Is.GetConfiguration:input_type -> ttn.lorawan.v3.GetIsConfigurationRequest + 0, // 42: ttn.lorawan.v3.EntityAccess.AuthInfo:output_type -> ttn.lorawan.v3.AuthInfoResponse + 3, // 43: ttn.lorawan.v3.Is.GetConfiguration:output_type -> ttn.lorawan.v3.GetIsConfigurationResponse + 42, // [42:44] is the sub-list for method output_type + 40, // [40:42] is the sub-list for method input_type + 40, // [40:40] is the sub-list for extension type_name + 40, // [40:40] is the sub-list for extension extendee + 0, // [0:40] is the sub-list for field type_name } func init() { file_lorawan_stack_api_identityserver_proto_init() } @@ -1525,7 +1596,7 @@ func file_lorawan_stack_api_identityserver_proto_init() { } } file_lorawan_stack_api_identityserver_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsConfiguration_UserRegistration_Invitation); i { + switch v := v.(*IsConfiguration_CollaboratorRights); i { case 0: return &v.state case 1: @@ -1537,7 +1608,7 @@ func file_lorawan_stack_api_identityserver_proto_init() { } } file_lorawan_stack_api_identityserver_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsConfiguration_UserRegistration_ContactInfoValidation); i { + switch v := v.(*IsConfiguration_UserRegistration_Invitation); i { case 0: return &v.state case 1: @@ -1549,7 +1620,7 @@ func file_lorawan_stack_api_identityserver_proto_init() { } } file_lorawan_stack_api_identityserver_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsConfiguration_UserRegistration_AdminApproval); i { + switch v := v.(*IsConfiguration_UserRegistration_ContactInfoValidation); i { case 0: return &v.state case 1: @@ -1561,6 +1632,18 @@ func file_lorawan_stack_api_identityserver_proto_init() { } } file_lorawan_stack_api_identityserver_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IsConfiguration_UserRegistration_AdminApproval); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_lorawan_stack_api_identityserver_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*IsConfiguration_UserRegistration_PasswordRequirements); i { case 0: return &v.state @@ -1585,7 +1668,7 @@ func file_lorawan_stack_api_identityserver_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_lorawan_stack_api_identityserver_proto_rawDesc, NumEnums: 0, - NumMessages: 16, + NumMessages: 17, NumExtensions: 0, NumServices: 2, }, diff --git a/pkg/ttnpb/identityserver.pb.paths.fm.go b/pkg/ttnpb/identityserver.pb.paths.fm.go index 94f06072f7..1cb7f3bbb7 100644 --- a/pkg/ttnpb/identityserver.pb.paths.fm.go +++ b/pkg/ttnpb/identityserver.pb.paths.fm.go @@ -76,6 +76,8 @@ var GetIsConfigurationRequestFieldPathsTopLevel []string var IsConfigurationFieldPathsNested = []string{ "admin_rights", "admin_rights.all", + "collaborator_rights", + "collaborator_rights.set_others_as_contacts", "end_device_picture", "end_device_picture.disable_upload", "profile_picture", @@ -107,6 +109,7 @@ var IsConfigurationFieldPathsNested = []string{ var IsConfigurationFieldPathsTopLevel = []string{ "admin_rights", + "collaborator_rights", "end_device_picture", "profile_picture", "user_login", @@ -117,6 +120,8 @@ var GetIsConfigurationResponseFieldPathsNested = []string{ "configuration", "configuration.admin_rights", "configuration.admin_rights.all", + "configuration.collaborator_rights", + "configuration.collaborator_rights.set_others_as_contacts", "configuration.end_device_picture", "configuration.end_device_picture.disable_upload", "configuration.profile_picture", @@ -263,6 +268,13 @@ var IsConfiguration_AdminRightsFieldPathsNested = []string{ var IsConfiguration_AdminRightsFieldPathsTopLevel = []string{ "all", } +var IsConfiguration_CollaboratorRightsFieldPathsNested = []string{ + "set_others_as_contacts", +} + +var IsConfiguration_CollaboratorRightsFieldPathsTopLevel = []string{ + "set_others_as_contacts", +} var IsConfiguration_UserRegistration_InvitationFieldPathsNested = []string{ "required", "token_ttl", diff --git a/pkg/ttnpb/identityserver.pb.setters.fm.go b/pkg/ttnpb/identityserver.pb.setters.fm.go index a4cccf5554..04a175ebf3 100644 --- a/pkg/ttnpb/identityserver.pb.setters.fm.go +++ b/pkg/ttnpb/identityserver.pb.setters.fm.go @@ -375,6 +375,31 @@ func (dst *IsConfiguration) SetFields(src *IsConfiguration, paths ...string) err dst.AdminRights = nil } } + case "collaborator_rights": + if len(subs) > 0 { + var newDst, newSrc *IsConfiguration_CollaboratorRights + if (src == nil || src.CollaboratorRights == nil) && dst.CollaboratorRights == nil { + continue + } + if src != nil { + newSrc = src.CollaboratorRights + } + if dst.CollaboratorRights != nil { + newDst = dst.CollaboratorRights + } else { + newDst = &IsConfiguration_CollaboratorRights{} + dst.CollaboratorRights = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.CollaboratorRights = src.CollaboratorRights + } else { + dst.CollaboratorRights = nil + } + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -782,6 +807,26 @@ func (dst *IsConfiguration_AdminRights) SetFields(src *IsConfiguration_AdminRigh return nil } +func (dst *IsConfiguration_CollaboratorRights) SetFields(src *IsConfiguration_CollaboratorRights, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "set_others_as_contacts": + if len(subs) > 0 { + return fmt.Errorf("'set_others_as_contacts' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.SetOthersAsContacts = src.SetOthersAsContacts + } else { + dst.SetOthersAsContacts = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + func (dst *IsConfiguration_UserRegistration_Invitation) SetFields(src *IsConfiguration_UserRegistration_Invitation, paths ...string) error { for name, subs := range _processPaths(paths) { switch name { diff --git a/pkg/ttnpb/identityserver.pb.validate.go b/pkg/ttnpb/identityserver.pb.validate.go index 0dbd8313fc..0b0fb26806 100644 --- a/pkg/ttnpb/identityserver.pb.validate.go +++ b/pkg/ttnpb/identityserver.pb.validate.go @@ -354,6 +354,18 @@ func (m *IsConfiguration) ValidateFields(paths ...string) error { } } + case "collaborator_rights": + + if v, ok := interface{}(m.GetCollaboratorRights()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return IsConfigurationValidationError{ + field: "collaborator_rights", + reason: "embedded message failed validation", + cause: err, + } + } + } + default: return IsConfigurationValidationError{ field: name, @@ -1385,6 +1397,100 @@ var _ interface { ErrorName() string } = IsConfiguration_AdminRightsValidationError{} +// ValidateFields checks the field values on IsConfiguration_CollaboratorRights +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *IsConfiguration_CollaboratorRights) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = IsConfiguration_CollaboratorRightsFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "set_others_as_contacts": + + if v, ok := interface{}(m.GetSetOthersAsContacts()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return IsConfiguration_CollaboratorRightsValidationError{ + field: "set_others_as_contacts", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return IsConfiguration_CollaboratorRightsValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// IsConfiguration_CollaboratorRightsValidationError is the validation error +// returned by IsConfiguration_CollaboratorRights.ValidateFields if the +// designated constraints aren't met. +type IsConfiguration_CollaboratorRightsValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e IsConfiguration_CollaboratorRightsValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e IsConfiguration_CollaboratorRightsValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e IsConfiguration_CollaboratorRightsValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e IsConfiguration_CollaboratorRightsValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e IsConfiguration_CollaboratorRightsValidationError) ErrorName() string { + return "IsConfiguration_CollaboratorRightsValidationError" +} + +// Error satisfies the builtin error interface +func (e IsConfiguration_CollaboratorRightsValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sIsConfiguration_CollaboratorRights.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = IsConfiguration_CollaboratorRightsValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = IsConfiguration_CollaboratorRightsValidationError{} + // ValidateFields checks the field values on // IsConfiguration_UserRegistration_Invitation with the rules defined in the // proto definition for this message. If any rules are violated, an error is returned. diff --git a/sdk/js/generated/api.json b/sdk/js/generated/api.json index cbf4d9c838..3adf5b9118 100644 --- a/sdk/js/generated/api.json +++ b/sdk/js/generated/api.json @@ -24049,6 +24049,18 @@ "isoneof": false, "oneofdecl": "", "defaultValue": "" + }, + { + "name": "collaborator_rights", + "description": "", + "label": "", + "type": "CollaboratorRights", + "longType": "IsConfiguration.CollaboratorRights", + "fullType": "ttn.lorawan.v3.IsConfiguration.CollaboratorRights", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" } ] }, @@ -24076,6 +24088,30 @@ } ] }, + { + "name": "CollaboratorRights", + "longName": "IsConfiguration.CollaboratorRights", + "fullName": "ttn.lorawan.v3.IsConfiguration.CollaboratorRights", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "set_others_as_contacts", + "description": "", + "label": "", + "type": "BoolValue", + "longType": "google.protobuf.BoolValue", + "fullType": "google.protobuf.BoolValue", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, { "name": "EndDevicePicture", "longName": "IsConfiguration.EndDevicePicture", From 62129eea872ab389fa165b08753055e026186a46 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 15:51:44 -0300 Subject: [PATCH 02/19] is: Update server configuration --- pkg/identityserver/config.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/config.go b/pkg/identityserver/config.go index 9aee2cf3b4..07caa1e194 100644 --- a/pkg/identityserver/config.go +++ b/pkg/identityserver/config.go @@ -83,13 +83,16 @@ type Config struct { AdminRights struct { All bool `name:"all" description:"Grant all rights to admins, including _KEYS and _ALL"` } `name:"admin-rights"` + CollaboratorRights struct { + SetOthersAsContacts bool `name:"set-others-as-contacts" description:"Allow users to set other users as entity contacts"` // nolint:lll + } `name:"collaborator-rights"` LoginTokens struct { Enabled bool `name:"enabled" description:"enable users requesting login tokens"` TokenTTL time.Duration `name:"token-ttl" description:"TTL of login tokens"` } `name:"login-tokens"` Email struct { email.Config `name:",squash"` - Dir string `name:"dir" description:"Directory to write emails to if the dir provider is used (development only)"` //nolint:lll + Dir string `name:"dir" description:"Directory to write emails to if the dir provider is used (development only)"` // nolint:lll SendGrid sendgrid.Config `name:"sendgrid"` SMTP smtp.Config `name:"smtp"` Templates emailTemplatesConfig `name:"templates"` @@ -207,6 +210,9 @@ func (c Config) toProto() *ttnpb.IsConfiguration { AdminRights: &ttnpb.IsConfiguration_AdminRights{ All: &wrapperspb.BoolValue{Value: c.AdminRights.All}, }, + CollaboratorRights: &ttnpb.IsConfiguration_CollaboratorRights{ + SetOthersAsContacts: &wrapperspb.BoolValue{Value: c.CollaboratorRights.SetOthersAsContacts}, + }, } } From 204e420f66451811322ef50142e75007c0167f0e Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Fri, 7 Jul 2023 13:16:48 -0300 Subject: [PATCH 03/19] dev: Add new value to Identity Server default config --- cmd/internal/shared/identityserver/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/internal/shared/identityserver/config.go b/cmd/internal/shared/identityserver/config.go index f9943be566..e79ed4477b 100644 --- a/cmd/internal/shared/identityserver/config.go +++ b/cmd/internal/shared/identityserver/config.go @@ -74,6 +74,7 @@ func init() { DefaultIdentityServerConfig.UserRights.CreateClients = true DefaultIdentityServerConfig.UserRights.CreateGateways = true DefaultIdentityServerConfig.UserRights.CreateOrganizations = true + DefaultIdentityServerConfig.CollaboratorRights.SetOthersAsContacts = true DefaultIdentityServerConfig.LoginTokens.TokenTTL = time.Hour DefaultIdentityServerConfig.Delete.Restore = 24 * time.Hour } From 088f8d0e5e7c929e9d8a32503eab2c02f6f98d8b Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 20:58:12 -0300 Subject: [PATCH 04/19] is: Add contact info restriction error --- config/messages.json | 9 +++++++++ pkg/identityserver/store/errors.go | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/config/messages.json b/config/messages.json index 8a995eb4c7..13e97549b4 100644 --- a/config/messages.json +++ b/config/messages.json @@ -5372,6 +5372,15 @@ "file": "errors.go" } }, + "error:pkg/identityserver/store:contact_info_restricted": { + "translations": { + "en": "contact information can only reference the caller" + }, + "description": { + "package": "pkg/identityserver/store", + "file": "errors.go" + } + }, "error:pkg/identityserver/store:end_device_not_found": { "translations": { "en": "end device with id `{device_id}` not found in application with id `{application_id}`" diff --git a/pkg/identityserver/store/errors.go b/pkg/identityserver/store/errors.go index f35864fa53..12cc97076b 100644 --- a/pkg/identityserver/store/errors.go +++ b/pkg/identityserver/store/errors.go @@ -124,4 +124,8 @@ var ( "application_dev_eui_limit_reached", "application issued DevEUI limit ({dev_eui_limit}) reached", ) + + ErrContactInfoRestricted = errors.DefinePermissionDenied( + "contact_info_restricted", "contact information can only reference the caller", + ) ) From d171d6889834d62130d344a5425bdf6eeeb9e05b Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 20:58:31 -0300 Subject: [PATCH 05/19] is: Add contact info validation method --- pkg/identityserver/contact_info_registry.go | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pkg/identityserver/contact_info_registry.go b/pkg/identityserver/contact_info_registry.go index 210990e644..c0e211e21a 100644 --- a/pkg/identityserver/contact_info_registry.go +++ b/pkg/identityserver/contact_info_registry.go @@ -42,6 +42,32 @@ var ( ) ) +// validateContactInfoRestrictions fetches the auth info from the context and validates if the caller ID matches the +// provided `ids` in the parameters. The usage of this function should be restricted to testing the administrative and +// technical contacts in methods belonging to each entity registry. +func (is *IdentityServer) validateContactInfoRestrictions( + ctx context.Context, ids ...*ttnpb.OrganizationOrUserIdentifiers, +) error { + authInfo, err := is.authInfo(ctx) + if err != nil { + return err + } + callerID := authInfo.GetOrganizationOrUserIdentifiers() + if is.configFromContext(ctx).CollaboratorRights.SetOthersAsContacts || authInfo.IsAdmin { + return nil + } + + for _, id := range ids { + if id == nil { + continue + } + if callerID.EntityType() != id.EntityType() || callerID.IDString() != id.IDString() { + return store.ErrContactInfoRestricted.New() + } + } + return nil +} + func validateCollaboratorEqualsContact(collaborator, contact *ttnpb.OrganizationOrUserIdentifiers) error { if contact == nil { return nil From 2be4f94fef5152509de4643f7bb1b4f3d9fa7f07 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 21:45:40 -0300 Subject: [PATCH 06/19] is: Add contact restriction to organization registry --- pkg/identityserver/organization_registry.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/organization_registry.go b/pkg/identityserver/organization_registry.go index e7169122c1..f89b6c40a7 100644 --- a/pkg/identityserver/organization_registry.go +++ b/pkg/identityserver/organization_registry.go @@ -245,7 +245,17 @@ func (is *IdentityServer) updateOrganization(ctx context.Context, req *ttnpb.Upd return nil, err } } - req.FieldMask.Paths = ttnpb.FlattenPaths(req.FieldMask.Paths, []string{"administrative_contact", "technical_contact"}) + + if err := is.validateContactInfoRestrictions( + ctx, req.Organization.GetAdministrativeContact(), req.Organization.GetTechnicalContact(), + ); err != nil { + return nil, err + } + + req.FieldMask.Paths = ttnpb.FlattenPaths( + req.FieldMask.Paths, + []string{"administrative_contact", "technical_contact"}, + ) err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { if err := validateContactIsCollaborator(ctx, st, req.Organization.AdministrativeContact, req.Organization.GetEntityIdentifiers()); err != nil { return err From 20bc1cc16b3a2f68254c4527f90ae8cc91cc4a8f Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 21:45:51 -0300 Subject: [PATCH 07/19] is: Add contact restriction to gateway registry --- pkg/identityserver/gateway_registry.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index dbb8470468..de7cc2c83e 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -558,6 +558,12 @@ func (is *IdentityServer) updateGateway(ctx context.Context, req *ttnpb.UpdateGa } } + if err := is.validateContactInfoRestrictions( + ctx, req.Gateway.GetAdministrativeContact(), req.Gateway.GetTechnicalContact(), + ); err != nil { + return nil, err + } + err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { if err := validateContactIsCollaborator(ctx, st, req.Gateway.AdministrativeContact, req.Gateway.GetEntityIdentifiers()); err != nil { return err From d92da3f08db58a8abe21a76a426f22a85caa13c9 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 21:46:06 -0300 Subject: [PATCH 08/19] is: Add contact restriction to client registry --- pkg/identityserver/client_registry.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/client_registry.go b/pkg/identityserver/client_registry.go index 49708fbe23..b15c5fbb9b 100644 --- a/pkg/identityserver/client_registry.go +++ b/pkg/identityserver/client_registry.go @@ -317,6 +317,12 @@ func (is *IdentityServer) updateClient( []string{"administrative_contact", "technical_contact"}, ) + if err := is.validateContactInfoRestrictions( + ctx, req.Client.GetAdministrativeContact(), req.Client.GetTechnicalContact(), + ); err != nil { + return nil, err + } + if err = is.RequireAdminForFieldUpdate(ctx, req.GetFieldMask().GetPaths(), []string{ "state", "state_description", "skip_authorization", "endorsed", "grants", }); err != nil { @@ -331,10 +337,14 @@ func (is *IdentityServer) updateClient( } err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { - if err := validateContactIsCollaborator(ctx, st, req.Client.AdministrativeContact, req.Client.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Client.AdministrativeContact, req.Client.GetEntityIdentifiers(), + ); err != nil { return err } - if err := validateContactIsCollaborator(ctx, st, req.Client.TechnicalContact, req.Client.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Client.TechnicalContact, req.Client.GetEntityIdentifiers(), + ); err != nil { return err } cli, err = st.UpdateClient(ctx, req.Client, req.FieldMask.GetPaths()) From 9908ceb328fdec0ad7d4d55665ce38db8573b959 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 21:12:41 -0300 Subject: [PATCH 09/19] is: Add contact restriction to application registry --- pkg/identityserver/application_registry.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/application_registry.go b/pkg/identityserver/application_registry.go index 0c125faf38..238967d7b2 100644 --- a/pkg/identityserver/application_registry.go +++ b/pkg/identityserver/application_registry.go @@ -273,7 +273,17 @@ func (is *IdentityServer) updateApplication(ctx context.Context, req *ttnpb.Upda return nil, err } } - req.FieldMask.Paths = ttnpb.FlattenPaths(req.FieldMask.Paths, []string{"administrative_contact", "technical_contact"}) + + if err := is.validateContactInfoRestrictions( + ctx, req.Application.GetAdministrativeContact(), req.Application.GetTechnicalContact(), + ); err != nil { + return nil, err + } + + req.FieldMask.Paths = ttnpb.FlattenPaths( + req.FieldMask.Paths, + []string{"administrative_contact", "technical_contact"}, + ) err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { if err := validateContactIsCollaborator(ctx, st, req.Application.AdministrativeContact, req.Application.GetEntityIdentifiers()); err != nil { return err From c9e6c8ddeefff9242ff25859b31464e7ff77008c Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 22:43:10 -0300 Subject: [PATCH 10/19] is: Add restriction test to application registry --- .../application_registry_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/pkg/identityserver/application_registry_test.go b/pkg/identityserver/application_registry_test.go index e3d70901db..4f47b0cb81 100644 --- a/pkg/identityserver/application_registry_test.go +++ b/pkg/identityserver/application_registry_test.go @@ -187,6 +187,58 @@ func TestApplicationsCRUD(t *testing.T) { a.So(updated.Name, should.Equal, "Updated Name") } + t.Run("Contact Info Restrictions", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + + oldSetOtherAsContacts := is.config.CollaboratorRights.SetOthersAsContacts + t.Cleanup(func() { is.config.CollaboratorRights.SetOthersAsContacts = oldSetOtherAsContacts }) + is.config.CollaboratorRights.SetOthersAsContacts = false + + // Set usr-2 as collaborator to application. + aac := ttnpb.NewApplicationAccessClient(cc) + _, err := aac.SetCollaborator(ctx, &ttnpb.SetApplicationCollaboratorRequest{ + ApplicationIds: created.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, + }, + }, creds) + a.So(err, should.BeNil) + + // Attempt to set another collaborator as administrative contact. + _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: created.GetIds(), + AdministrativeContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, creds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Admin can bypass contact info restrictions. + _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: created.GetIds(), + AdministrativeContact: usr1.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, adminCreds) + a.So(err, should.BeNil) + + is.config.CollaboratorRights.SetOthersAsContacts = true + + // Now usr-1 can set usr-2 as technical contact. + _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: created.GetIds(), + Name: "Updated Name", + TechnicalContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("technical_contact"), + }, creds) + a.So(err, should.BeNil) + }) + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { list, err := reg.List(ctx, &ttnpb.ListApplicationsRequest{ FieldMask: ttnpb.FieldMask("name"), From ee89041914dac2b7572800f0674d66519eb2e7a0 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 22:43:27 -0300 Subject: [PATCH 11/19] is: Add restriction test to client registry --- pkg/identityserver/client_registry_test.go | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pkg/identityserver/client_registry_test.go b/pkg/identityserver/client_registry_test.go index d2cc75a4da..133c38793d 100644 --- a/pkg/identityserver/client_registry_test.go +++ b/pkg/identityserver/client_registry_test.go @@ -177,6 +177,57 @@ func TestClientsCRUD(t *testing.T) { a.So(updated.Name, should.Equal, "Updated Name") } + t.Run("Contact Info Restrictions", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + + oldSetOtherAsContacts := is.config.CollaboratorRights.SetOthersAsContacts + t.Cleanup(func() { is.config.CollaboratorRights.SetOthersAsContacts = oldSetOtherAsContacts }) + is.config.CollaboratorRights.SetOthersAsContacts = false + + // Set usr-2 as collaborator to client. + cac := ttnpb.NewClientAccessClient(cc) + _, err := cac.SetCollaborator(ctx, &ttnpb.SetClientCollaboratorRequest{ + ClientIds: created.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, + }, + }, creds) + a.So(err, should.BeNil) + + // Attempt to set another collaborator as administrative contact. + _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ + Client: &ttnpb.Client{ + Ids: created.GetIds(), + AdministrativeContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, creds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Admin can bypass contact info restrictions. + _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ + Client: &ttnpb.Client{ + Ids: created.GetIds(), + AdministrativeContact: usr1.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, adminCreds) + a.So(err, should.BeNil) + + is.config.CollaboratorRights.SetOthersAsContacts = true + + // Now usr-1 can set usr-2 as technical contact. + _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ + Client: &ttnpb.Client{ + Ids: created.GetIds(), + TechnicalContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("technical_contact"), + }, creds) + a.So(err, should.BeNil) + }) + updated, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ Client: &ttnpb.Client{ Ids: created.GetIds(), From f10658bb98fc8be08eaa66c118398962184d3818 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 22:43:41 -0300 Subject: [PATCH 12/19] is: Add restriction test to gateway registry --- pkg/identityserver/gateway_registry_test.go | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pkg/identityserver/gateway_registry_test.go b/pkg/identityserver/gateway_registry_test.go index bbb6488380..689f9eb7b8 100644 --- a/pkg/identityserver/gateway_registry_test.go +++ b/pkg/identityserver/gateway_registry_test.go @@ -207,6 +207,57 @@ func TestGatewaysCRUD(t *testing.T) { a.So(updated.Name, should.Equal, "Updated Name") } + t.Run("Contact Info Restrictions", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + + oldSetOtherAsContacts := is.config.CollaboratorRights.SetOthersAsContacts + t.Cleanup(func() { is.config.CollaboratorRights.SetOthersAsContacts = oldSetOtherAsContacts }) + is.config.CollaboratorRights.SetOthersAsContacts = false + + // Set usr-2 as collaborator to client. + gac := ttnpb.NewGatewayAccessClient(cc) + _, err := gac.SetCollaborator(ctx, &ttnpb.SetGatewayCollaboratorRequest{ + GatewayIds: created.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, + }, + }, creds) + a.So(err, should.BeNil) + + // Attempt to set another collaborator as administrative contact. + _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: created.GetIds(), + AdministrativeContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, creds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Admin can bypass contact info restrictions. + _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: created.GetIds(), + AdministrativeContact: usr1.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, adminCreds) + a.So(err, should.BeNil) + + is.config.CollaboratorRights.SetOthersAsContacts = true + + // Now usr-1 can set usr-2 as technical contact. + _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: created.GetIds(), + TechnicalContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("technical_contact"), + }, creds) + a.So(err, should.BeNil) + }) + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { list, err := reg.List(ctx, &ttnpb.ListGatewaysRequest{ FieldMask: ttnpb.FieldMask("name"), From 276fc1878a8dc8789a2581cbf83acfe103faf68f Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 22:43:58 -0300 Subject: [PATCH 13/19] is: Add restriction test to organization registry --- .../organization_registry_test.go | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pkg/identityserver/organization_registry_test.go b/pkg/identityserver/organization_registry_test.go index 4627683210..4b68bf5038 100644 --- a/pkg/identityserver/organization_registry_test.go +++ b/pkg/identityserver/organization_registry_test.go @@ -213,6 +213,57 @@ func TestOrganizationsCRUD(t *testing.T) { a.So(updated.Name, should.Equal, "Updated Name") } + t.Run("Contact Info Restrictions", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + + oldSetOtherAsContacts := is.config.CollaboratorRights.SetOthersAsContacts + t.Cleanup(func() { is.config.CollaboratorRights.SetOthersAsContacts = oldSetOtherAsContacts }) + is.config.CollaboratorRights.SetOthersAsContacts = false + + // Set usr-2 as collaborator to client. + oac := ttnpb.NewOrganizationAccessClient(cc) + _, err := oac.SetCollaborator(ctx, &ttnpb.SetOrganizationCollaboratorRequest{ + OrganizationIds: created.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, + }, + }, creds) + a.So(err, should.BeNil) + + // Attempt to set another collaborator as administrative contact. + _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: created.GetIds(), + AdministrativeContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, creds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Admin can bypass contact info restrictions. + _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: created.GetIds(), + AdministrativeContact: usr1.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("administrative_contact"), + }, adminCreds) + a.So(err, should.BeNil) + + is.config.CollaboratorRights.SetOthersAsContacts = true + + // Now usr-1 can set usr-2 as technical contact. + _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: created.GetIds(), + TechnicalContact: usr2.GetOrganizationOrUserIdentifiers(), + }, + FieldMask: ttnpb.FieldMask("technical_contact"), + }, creds) + a.So(err, should.BeNil) + }) + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { list, err := reg.List(ctx, &ttnpb.ListOrganizationsRequest{ FieldMask: ttnpb.FieldMask("name"), From 252279f25f373f4d0efc98a553794bc7ec7e681e Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Fri, 7 Jul 2023 10:53:09 -0300 Subject: [PATCH 14/19] dev: Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4204bb887..9dd46614d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ For details about compatibility between different releases, see the **Commitment - RPCs and CLI command to delete a batch of end devices within an application. - Check `ttn-lw-cli end-devices batch-delete` for more details. - Add `UserInput` component to the Console to handle user id input fields by implementing an autosuggest. +- The Identity Server configuration has a new optional restriction regarding adminstrative and technical contacts of entities. This limits the action of an user or organization to set these contacts only to themselves, it is disabled by default but it is possible to enable it by setting `is.collaborator-rights.set-others-as-contacts` as false. ### Changed From 08ab657ca344c73585eb7647f1b311122491b7c4 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 16:09:05 -0300 Subject: [PATCH 15/19] is: Format organization registry --- pkg/identityserver/organization_registry.go | 112 ++++++++++++++---- .../organization_registry_test.go | 7 +- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/pkg/identityserver/organization_registry.go b/pkg/identityserver/organization_registry.go index f89b6c40a7..d5893105be 100644 --- a/pkg/identityserver/organization_registry.go +++ b/pkg/identityserver/organization_registry.go @@ -62,12 +62,22 @@ var ( ) var ( - errNestedOrganizations = errors.DefineInvalidArgument("nested_organizations", "organizations can not be nested") - errAdminsCreateOrganizations = errors.DefinePermissionDenied("admins_create_organizations", "organizations may only be created by admins") - errAdminsPurgeOrganizations = errors.DefinePermissionDenied("admins_purge_organizations", "organizations may only be purged by admins") + errNestedOrganizations = errors.DefineInvalidArgument( + "nested_organizations", "organizations can not be nested", + ) + errAdminsCreateOrganizations = errors.DefinePermissionDenied( + "admins_create_organizations", + "organizations may only be created by admins", + ) + errAdminsPurgeOrganizations = errors.DefinePermissionDenied( + "admins_purge_organizations", + "organizations may only be purged by admins", + ) ) -func (is *IdentityServer) createOrganization(ctx context.Context, req *ttnpb.CreateOrganizationRequest) (org *ttnpb.Organization, err error) { +func (is *IdentityServer) createOrganization( + ctx context.Context, req *ttnpb.CreateOrganizationRequest, +) (org *ttnpb.Organization, err error) { if err = blocklist.Check(ctx, req.Organization.GetIds().GetOrganizationId()); err != nil { return nil, err } @@ -84,7 +94,9 @@ func (is *IdentityServer) createOrganization(ctx context.Context, req *ttnpb.Cre if req.Organization.AdministrativeContact == nil { req.Organization.AdministrativeContact = req.Collaborator - } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Organization.AdministrativeContact); err != nil { + } else if err := validateCollaboratorEqualsContact( + req.Collaborator, req.Organization.AdministrativeContact, + ); err != nil { return nil, err } if req.Organization.TechnicalContact == nil { @@ -125,7 +137,10 @@ func (is *IdentityServer) createOrganization(ctx context.Context, req *ttnpb.Cre return org, nil } -func (is *IdentityServer) getOrganization(ctx context.Context, req *ttnpb.GetOrganizationRequest) (org *ttnpb.Organization, err error) { +func (is *IdentityServer) getOrganization( + ctx context.Context, + req *ttnpb.GetOrganizationRequest, +) (org *ttnpb.Organization, err error) { if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } @@ -155,7 +170,10 @@ func (is *IdentityServer) getOrganization(ctx context.Context, req *ttnpb.GetOrg return org, nil } -func (is *IdentityServer) listOrganizations(ctx context.Context, req *ttnpb.ListOrganizationsRequest) (orgs *ttnpb.Organizations, err error) { +func (is *IdentityServer) listOrganizations( + ctx context.Context, + req *ttnpb.ListOrganizationsRequest, +) (orgs *ttnpb.Organizations, err error) { req.FieldMask = cleanFieldMaskPaths(ttnpb.OrganizationFieldPathsNested, req.FieldMask, getPaths, nil) authInfo, err := is.authInfo(ctx) @@ -202,7 +220,11 @@ func (is *IdentityServer) listOrganizations(ctx context.Context, req *ttnpb.List if len(ids) == 0 { return nil } - callerMemberships, err = st.FindAccountMembershipChains(ctx, callerAccountID, "organization", idStrings(ids...)...) + callerMemberships, err = st.FindAccountMembershipChains( + ctx, + callerAccountID, + "organization", + idStrings(ids...)...) if err != nil { return err } @@ -232,8 +254,13 @@ func (is *IdentityServer) listOrganizations(ctx context.Context, req *ttnpb.List return orgs, nil } -func (is *IdentityServer) updateOrganization(ctx context.Context, req *ttnpb.UpdateOrganizationRequest) (org *ttnpb.Organization, err error) { - if err = rights.RequireOrganization(ctx, req.Organization.GetIds(), ttnpb.Right_RIGHT_ORGANIZATION_SETTINGS_BASIC); err != nil { +func (is *IdentityServer) updateOrganization( + ctx context.Context, + req *ttnpb.UpdateOrganizationRequest, +) (org *ttnpb.Organization, err error) { + if err = rights.RequireOrganization( + ctx, req.Organization.GetIds(), ttnpb.Right_RIGHT_ORGANIZATION_SETTINGS_BASIC, + ); err != nil { return nil, err } req.FieldMask = cleanFieldMaskPaths(ttnpb.OrganizationFieldPathsNested, req.FieldMask, nil, getPaths) @@ -257,10 +284,14 @@ func (is *IdentityServer) updateOrganization(ctx context.Context, req *ttnpb.Upd []string{"administrative_contact", "technical_contact"}, ) err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { - if err := validateContactIsCollaborator(ctx, st, req.Organization.AdministrativeContact, req.Organization.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Organization.AdministrativeContact, req.Organization.GetEntityIdentifiers(), + ); err != nil { return err } - if err := validateContactIsCollaborator(ctx, st, req.Organization.TechnicalContact, req.Organization.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Organization.TechnicalContact, req.Organization.GetEntityIdentifiers(), + ); err != nil { return err } org, err = st.UpdateOrganization(ctx, req.Organization, req.FieldMask.GetPaths()) @@ -279,11 +310,16 @@ func (is *IdentityServer) updateOrganization(ctx context.Context, req *ttnpb.Upd if err != nil { return nil, err } - events.Publish(evtUpdateOrganization.NewWithIdentifiersAndData(ctx, req.Organization.GetIds(), req.FieldMask.GetPaths())) + events.Publish( + evtUpdateOrganization.NewWithIdentifiersAndData(ctx, req.Organization.GetIds(), req.FieldMask.GetPaths()), + ) return org, nil } -func (is *IdentityServer) deleteOrganization(ctx context.Context, ids *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { +func (is *IdentityServer) deleteOrganization( + ctx context.Context, + ids *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { if err := rights.RequireOrganization(ctx, ids, ttnpb.Right_RIGHT_ORGANIZATION_DELETE); err != nil { return nil, err } @@ -297,8 +333,13 @@ func (is *IdentityServer) deleteOrganization(ctx context.Context, ids *ttnpb.Org return ttnpb.Empty, nil } -func (is *IdentityServer) restoreOrganization(ctx context.Context, ids *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { - if err := rights.RequireOrganization(store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_ORGANIZATION_DELETE); err != nil { +func (is *IdentityServer) restoreOrganization( + ctx context.Context, + ids *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { + if err := rights.RequireOrganization( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_ORGANIZATION_DELETE, + ); err != nil { return nil, err } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { @@ -322,7 +363,10 @@ func (is *IdentityServer) restoreOrganization(ctx context.Context, ids *ttnpb.Or return ttnpb.Empty, nil } -func (is *IdentityServer) purgeOrganization(ctx context.Context, ids *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { +func (is *IdentityServer) purgeOrganization( + ctx context.Context, + ids *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { if !is.IsAdmin(ctx) { return nil, errAdminsPurgeOrganizations.New() } @@ -355,30 +399,50 @@ type organizationRegistry struct { *IdentityServer } -func (or *organizationRegistry) Create(ctx context.Context, req *ttnpb.CreateOrganizationRequest) (*ttnpb.Organization, error) { +func (or *organizationRegistry) Create( + ctx context.Context, + req *ttnpb.CreateOrganizationRequest, +) (*ttnpb.Organization, error) { return or.createOrganization(ctx, req) } -func (or *organizationRegistry) Get(ctx context.Context, req *ttnpb.GetOrganizationRequest) (*ttnpb.Organization, error) { +func (or *organizationRegistry) Get( + ctx context.Context, + req *ttnpb.GetOrganizationRequest, +) (*ttnpb.Organization, error) { return or.getOrganization(ctx, req) } -func (or *organizationRegistry) List(ctx context.Context, req *ttnpb.ListOrganizationsRequest) (*ttnpb.Organizations, error) { +func (or *organizationRegistry) List( + ctx context.Context, + req *ttnpb.ListOrganizationsRequest, +) (*ttnpb.Organizations, error) { return or.listOrganizations(ctx, req) } -func (or *organizationRegistry) Update(ctx context.Context, req *ttnpb.UpdateOrganizationRequest) (*ttnpb.Organization, error) { +func (or *organizationRegistry) Update( + ctx context.Context, + req *ttnpb.UpdateOrganizationRequest, +) (*ttnpb.Organization, error) { return or.updateOrganization(ctx, req) } -func (or *organizationRegistry) Delete(ctx context.Context, req *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { +func (or *organizationRegistry) Delete( + ctx context.Context, + req *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { return or.deleteOrganization(ctx, req) } -func (or *organizationRegistry) Restore(ctx context.Context, req *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { +func (or *organizationRegistry) Restore( + ctx context.Context, + req *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { return or.restoreOrganization(ctx, req) } -func (or *organizationRegistry) Purge(ctx context.Context, req *ttnpb.OrganizationIdentifiers) (*emptypb.Empty, error) { +func (or *organizationRegistry) Purge( + ctx context.Context, req *ttnpb.OrganizationIdentifiers, +) (*emptypb.Empty, error) { return or.purgeOrganization(ctx, req) } diff --git a/pkg/identityserver/organization_registry_test.go b/pkg/identityserver/organization_registry_test.go index 4b68bf5038..7a9cb1f67c 100644 --- a/pkg/identityserver/organization_registry_test.go +++ b/pkg/identityserver/organization_registry_test.go @@ -264,12 +264,15 @@ func TestOrganizationsCRUD(t *testing.T) { a.So(err, should.BeNil) }) - for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{ + nil, usr1.GetOrganizationOrUserIdentifiers(), + } { list, err := reg.List(ctx, &ttnpb.ListOrganizationsRequest{ FieldMask: ttnpb.FieldMask("name"), Collaborator: collaborator, }, creds) - if a.So(err, should.BeNil) && a.So(list, should.NotBeNil) && a.So(list.Organizations, should.HaveLength, 6) { + if a.So(err, should.BeNil) && a.So(list, should.NotBeNil) && + a.So(list.Organizations, should.HaveLength, 6) { var found bool for _, item := range list.Organizations { if item.GetIds().GetOrganizationId() == created.GetIds().GetOrganizationId() { From e4b698880957c5d26ad7ceb852be9d7fa01e7661 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Mon, 3 Jul 2023 21:52:55 -0300 Subject: [PATCH 16/19] is: Format gateway registry --- pkg/identityserver/gateway_registry.go | 105 ++++++++++++++++---- pkg/identityserver/gateway_registry_test.go | 4 +- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index de7cc2c83e..a44889c174 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -65,13 +65,29 @@ var ( ) var ( - errAdminsCreateGateways = errors.DefinePermissionDenied("admins_create_gateways", "gateways may only be created by admins, or in organizations") - errGatewayEUITaken = errors.DefineAlreadyExists("gateway_eui_taken", "a gateway with EUI `{gateway_eui}` is already registered (by you or someone else) as `{gateway_id}`", "administrative_contact") - errAdminsPurgeGateways = errors.DefinePermissionDenied("admins_purge_gateways", "gateways may only be purged by admins") - errClaimAuthenticationCode = errors.DefineInvalidArgument("claim_authentication_code", "invalid claim authentication code") + errAdminsCreateGateways = errors.DefinePermissionDenied( + "admins_create_gateways", + "gateways may only be created by admins, or in organizations", + ) + errGatewayEUITaken = errors.DefineAlreadyExists( + "gateway_eui_taken", + "a gateway with EUI `{gateway_eui}` is already registered (by you or someone else) as `{gateway_id}`", + "administrative_contact", + ) + errAdminsPurgeGateways = errors.DefinePermissionDenied( + "admins_purge_gateways", + "gateways may only be purged by admins", + ) + errClaimAuthenticationCode = errors.DefineInvalidArgument( + "claim_authentication_code", + "invalid claim authentication code", + ) ) -func (is *IdentityServer) createGateway(ctx context.Context, req *ttnpb.CreateGatewayRequest) (gtw *ttnpb.Gateway, err error) { +func (is *IdentityServer) createGateway( // nolint:gocyclo + ctx context.Context, + req *ttnpb.CreateGatewayRequest, +) (gtw *ttnpb.Gateway, err error) { reqGtw := req.GetGateway() if err = blocklist.Check(ctx, reqGtw.GetIds().GetGatewayId()); err != nil { return nil, err @@ -91,7 +107,9 @@ func (is *IdentityServer) createGateway(ctx context.Context, req *ttnpb.CreateGa if req.Gateway.AdministrativeContact == nil { req.Gateway.AdministrativeContact = req.Collaborator - } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Gateway.AdministrativeContact); err != nil { + } else if err := validateCollaboratorEqualsContact( + req.Collaborator, req.Gateway.AdministrativeContact, + ); err != nil { return nil, err } if req.Gateway.TechnicalContact == nil { @@ -199,7 +217,10 @@ func (is *IdentityServer) createGateway(ctx context.Context, req *ttnpb.CreateGa return gtw, nil } -func (is *IdentityServer) getGateway(ctx context.Context, req *ttnpb.GetGatewayRequest) (gtw *ttnpb.Gateway, err error) { +func (is *IdentityServer) getGateway( // nolint:gocyclo + ctx context.Context, + req *ttnpb.GetGatewayRequest, +) (gtw *ttnpb.Gateway, err error) { if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } @@ -210,7 +231,12 @@ func (is *IdentityServer) getGateway(ctx context.Context, req *ttnpb.GetGatewayR req.FieldMask.Paths = append(req.FieldMask.GetPaths(), "frequency_plan_ids") } } - req.FieldMask = cleanFieldMaskPaths(ttnpb.GatewayFieldPathsNested, req.FieldMask, getPaths, []string{"frequency_plan_id"}) + req.FieldMask = cleanFieldMaskPaths( + ttnpb.GatewayFieldPathsNested, + req.FieldMask, + getPaths, + []string{"frequency_plan_id"}, + ) if err = rights.RequireGateway(ctx, req.GetGatewayIds(), ttnpb.Right_RIGHT_GATEWAY_INFO); err != nil { if !ttnpb.HasOnlyAllowedFields(req.FieldMask.GetPaths(), ttnpb.PublicGatewayFields...) { @@ -259,7 +285,8 @@ func (is *IdentityServer) getGateway(ctx context.Context, req *ttnpb.GetGatewayR if gtw.ClaimAuthenticationCode != nil && gtw.ClaimAuthenticationCode.Secret != nil { value := gtw.ClaimAuthenticationCode.Secret.Value if gtw.ClaimAuthenticationCode.Secret.KeyId != "" { - value, err = is.KeyService().Decrypt(ctx, gtw.ClaimAuthenticationCode.Secret.Value, gtw.ClaimAuthenticationCode.Secret.KeyId) + value, err = is.KeyService(). + Decrypt(ctx, gtw.ClaimAuthenticationCode.Secret.Value, gtw.ClaimAuthenticationCode.Secret.KeyId) if err != nil { return nil, err } @@ -292,7 +319,10 @@ func (is *IdentityServer) getGateway(ctx context.Context, req *ttnpb.GetGatewayR return gtw, nil } -func (is *IdentityServer) getGatewayIdentifiersForEUI(ctx context.Context, req *ttnpb.GetGatewayIdentifiersForEUIRequest) (ids *ttnpb.GatewayIdentifiers, err error) { +func (is *IdentityServer) getGatewayIdentifiersForEUI( + ctx context.Context, + req *ttnpb.GetGatewayIdentifiersForEUIRequest, +) (ids *ttnpb.GatewayIdentifiers, err error) { if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } @@ -312,14 +342,22 @@ func (is *IdentityServer) getGatewayIdentifiersForEUI(ctx context.Context, req * return ids, nil } -func (is *IdentityServer) listGateways(ctx context.Context, req *ttnpb.ListGatewaysRequest) (gtws *ttnpb.Gateways, err error) { +func (is *IdentityServer) listGateways( // nolint:gocyclo + ctx context.Context, + req *ttnpb.ListGatewaysRequest, +) (gtws *ttnpb.Gateways, err error) { // Backwards compatibility for frequency_plan_id field. if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "frequency_plan_id") { if !ttnpb.HasAnyField(req.FieldMask.GetPaths(), "frequency_plan_ids") { req.FieldMask.Paths = append(req.FieldMask.GetPaths(), "frequency_plan_ids") } } - req.FieldMask = cleanFieldMaskPaths(ttnpb.GatewayFieldPathsNested, req.FieldMask, getPaths, []string{"frequency_plan_id"}) + req.FieldMask = cleanFieldMaskPaths( + ttnpb.GatewayFieldPathsNested, + req.FieldMask, + getPaths, + []string{"frequency_plan_id"}, + ) authInfo, err := is.authInfo(ctx) if err != nil { @@ -407,7 +445,9 @@ func (is *IdentityServer) listGateways(ctx context.Context, req *ttnpb.ListGatew } else if gtws.Gateways[i].LbsLnsSecret != nil { value := gtws.Gateways[i].LbsLnsSecret.Value if gtws.Gateways[i].LbsLnsSecret.KeyId != "" { - value, err = is.KeyService().Decrypt(ctx, gtws.Gateways[i].LbsLnsSecret.Value, gtws.Gateways[i].LbsLnsSecret.KeyId) + value, err = is.KeyService().Decrypt( + ctx, gtws.Gateways[i].LbsLnsSecret.Value, gtws.Gateways[i].LbsLnsSecret.KeyId, + ) if err != nil { return nil, err } @@ -442,7 +482,7 @@ func (is *IdentityServer) listGateways(ctx context.Context, req *ttnpb.ListGatew if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "claim_authentication_code") { if !entityRights.IncludesAll(ttnpb.Right_RIGHT_GATEWAY_READ_SECRETS) { gtws.Gateways[i].ClaimAuthenticationCode = nil - } else if gtws.Gateways[i].ClaimAuthenticationCode != nil && gtws.Gateways[i].ClaimAuthenticationCode.Secret != nil { + } else if authCode := gtws.Gateways[i].ClaimAuthenticationCode; authCode != nil && authCode.Secret != nil { value := gtws.Gateways[i].ClaimAuthenticationCode.Secret.Value if keyID := gtws.Gateways[i].ClaimAuthenticationCode.Secret.KeyId; keyID != "" { value, err = is.KeyService().Decrypt(ctx, value, keyID) @@ -461,7 +501,10 @@ func (is *IdentityServer) listGateways(ctx context.Context, req *ttnpb.ListGatew return gtws, nil } -func (is *IdentityServer) updateGateway(ctx context.Context, req *ttnpb.UpdateGatewayRequest) (gtw *ttnpb.Gateway, err error) { +func (is *IdentityServer) updateGateway( // nolint:gocyclo + ctx context.Context, + req *ttnpb.UpdateGatewayRequest, +) (gtw *ttnpb.Gateway, err error) { reqGtw := req.GetGateway() if err = rights.RequireGateway(ctx, reqGtw.GetIds(), ttnpb.Right_RIGHT_GATEWAY_SETTINGS_BASIC); err != nil { // Allow setting the location field or the attributes field with the RIGHT_GATEWAY_LINK right. @@ -484,7 +527,12 @@ func (is *IdentityServer) updateGateway(ctx context.Context, req *ttnpb.UpdateGa reqGtw.FrequencyPlanIds = []string{reqGtw.FrequencyPlanId} } - req.FieldMask = cleanFieldMaskPaths(ttnpb.GatewayFieldPathsNested, req.FieldMask, nil, append(getPaths, "frequency_plan_id")) + req.FieldMask = cleanFieldMaskPaths( + ttnpb.GatewayFieldPathsNested, + req.FieldMask, + nil, + append(getPaths, "frequency_plan_id"), + ) if len(req.FieldMask.GetPaths()) == 0 { req.FieldMask = ttnpb.FieldMask(updatePaths...) } @@ -493,7 +541,10 @@ func (is *IdentityServer) updateGateway(ctx context.Context, req *ttnpb.UpdateGa return nil, err } } - req.FieldMask.Paths = ttnpb.FlattenPaths(req.FieldMask.Paths, []string{"administrative_contact", "technical_contact"}) + req.FieldMask.Paths = ttnpb.FlattenPaths( + req.FieldMask.Paths, + []string{"administrative_contact", "technical_contact"}, + ) if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "lbs_lns_secret") { if err := rights.RequireGateway(ctx, reqGtw.GetIds(), ttnpb.Right_RIGHT_GATEWAY_WRITE_SECRETS); err != nil { @@ -565,10 +616,14 @@ func (is *IdentityServer) updateGateway(ctx context.Context, req *ttnpb.UpdateGa } err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { - if err := validateContactIsCollaborator(ctx, st, req.Gateway.AdministrativeContact, req.Gateway.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Gateway.AdministrativeContact, req.Gateway.GetEntityIdentifiers(), + ); err != nil { return err } - if err := validateContactIsCollaborator(ctx, st, req.Gateway.TechnicalContact, req.Gateway.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Gateway.TechnicalContact, req.Gateway.GetEntityIdentifiers(), + ); err != nil { return err } gtw, err = st.UpdateGateway(ctx, reqGtw, req.FieldMask.GetPaths()) @@ -617,7 +672,9 @@ func (is *IdentityServer) deleteGateway(ctx context.Context, ids *ttnpb.GatewayI } func (is *IdentityServer) restoreGateway(ctx context.Context, ids *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error) { - if err := rights.RequireGateway(store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_GATEWAY_DELETE); err != nil { + if err := rights.RequireGateway( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_GATEWAY_DELETE, + ); err != nil { return nil, err } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { @@ -675,7 +732,8 @@ func validateClaimAuthenticationCode(authCode *ttnpb.GatewayClaimAuthenticationC if authCode.Secret == nil { return errClaimAuthenticationCode.New() } - if validFrom, validTo := ttnpb.StdTime(authCode.ValidFrom), ttnpb.StdTime(authCode.ValidTo); validFrom != nil && validTo != nil { + validFrom, validTo := ttnpb.StdTime(authCode.ValidFrom), ttnpb.StdTime(authCode.ValidTo) + if validFrom != nil && validTo != nil { if validTo.Before(*validFrom) { return errClaimAuthenticationCode.New() } @@ -697,7 +755,10 @@ func (gr *gatewayRegistry) Get(ctx context.Context, req *ttnpb.GetGatewayRequest return gr.getGateway(ctx, req) } -func (gr *gatewayRegistry) GetIdentifiersForEUI(ctx context.Context, req *ttnpb.GetGatewayIdentifiersForEUIRequest) (*ttnpb.GatewayIdentifiers, error) { +func (gr *gatewayRegistry) GetIdentifiersForEUI( + ctx context.Context, + req *ttnpb.GetGatewayIdentifiersForEUIRequest, +) (*ttnpb.GatewayIdentifiers, error) { return gr.getGatewayIdentifiersForEUI(ctx, req) } diff --git a/pkg/identityserver/gateway_registry_test.go b/pkg/identityserver/gateway_registry_test.go index 689f9eb7b8..e546ea0fa6 100644 --- a/pkg/identityserver/gateway_registry_test.go +++ b/pkg/identityserver/gateway_registry_test.go @@ -258,7 +258,9 @@ func TestGatewaysCRUD(t *testing.T) { a.So(err, should.BeNil) }) - for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{ + nil, usr1.GetOrganizationOrUserIdentifiers(), + } { list, err := reg.List(ctx, &ttnpb.ListGatewaysRequest{ FieldMask: ttnpb.FieldMask("name"), Collaborator: collaborator, From dab37148a71f2d46238c5d0dc8343e84719232b2 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Tue, 4 Jul 2023 21:13:28 -0300 Subject: [PATCH 17/19] is: Format application registry --- pkg/identityserver/application_registry.go | 121 ++++++++++++++---- .../application_registry_test.go | 4 +- 2 files changed, 98 insertions(+), 27 deletions(-) diff --git a/pkg/identityserver/application_registry.go b/pkg/identityserver/application_registry.go index 238967d7b2..ff047af2e2 100644 --- a/pkg/identityserver/application_registry.go +++ b/pkg/identityserver/application_registry.go @@ -69,9 +69,18 @@ var ( ) var ( - errAdminsCreateApplications = errors.DefinePermissionDenied("admins_create_applications", "applications may only be created by admins, or in organizations") - errAdminsPurgeApplications = errors.DefinePermissionDenied("admins_purge_applications", "applications may only be purged by admins") - errDevEUIIssuingNotEnabled = errors.DefineInvalidArgument("dev_eui_issuing_not_enabled", "DevEUI issuing not configured") + errAdminsCreateApplications = errors.DefinePermissionDenied( + "admins_create_applications", + "applications may only be created by admins, or in organizations", + ) + errAdminsPurgeApplications = errors.DefinePermissionDenied( + "admins_purge_applications", + "applications may only be purged by admins", + ) + errDevEUIIssuingNotEnabled = errors.DefineInvalidArgument( + "dev_eui_issuing_not_enabled", + "DevEUI issuing not configured", + ) ) func (is *IdentityServer) createApplication( //nolint:gocyclo @@ -88,18 +97,24 @@ func (is *IdentityServer) createApplication( //nolint:gocyclo return nil, err } } else if orgIDs := req.Collaborator.GetOrganizationIds(); orgIDs != nil { - if err = rights.RequireOrganization(ctx, orgIDs, ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_CREATE); err != nil { + if err = rights.RequireOrganization( + ctx, orgIDs, ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_CREATE, + ); err != nil { return nil, err } } if req.Application.AdministrativeContact == nil { req.Application.AdministrativeContact = req.Collaborator - } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Application.AdministrativeContact); err != nil { + } else if err := validateCollaboratorEqualsContact( + req.Collaborator, req.Application.AdministrativeContact, + ); err != nil { return nil, err } if req.Application.TechnicalContact == nil { req.Application.TechnicalContact = req.Collaborator - } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Application.TechnicalContact); err != nil { + } else if err := validateCollaboratorEqualsContact( + req.Collaborator, req.Application.TechnicalContact, + ); err != nil { return nil, err } if err := validateContactInfo(req.Application.ContactInfo); err != nil { @@ -134,7 +149,10 @@ func (is *IdentityServer) createApplication( //nolint:gocyclo return app, nil } -func (is *IdentityServer) getApplication(ctx context.Context, req *ttnpb.GetApplicationRequest) (app *ttnpb.Application, err error) { +func (is *IdentityServer) getApplication( + ctx context.Context, + req *ttnpb.GetApplicationRequest, +) (app *ttnpb.Application, err error) { if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } @@ -164,7 +182,10 @@ func (is *IdentityServer) getApplication(ctx context.Context, req *ttnpb.GetAppl return app, nil } -func (is *IdentityServer) listApplications(ctx context.Context, req *ttnpb.ListApplicationsRequest) (apps *ttnpb.Applications, err error) { +func (is *IdentityServer) listApplications( // nolint:gocyclo + ctx context.Context, + req *ttnpb.ListApplicationsRequest, +) (apps *ttnpb.Applications, err error) { req.FieldMask = cleanFieldMaskPaths(ttnpb.ApplicationFieldPathsNested, req.FieldMask, getPaths, nil) authInfo, err := is.authInfo(ctx) @@ -195,7 +216,9 @@ func (is *IdentityServer) listApplications(ctx context.Context, req *ttnpb.ListA return nil, err } } else if orgIDs := req.Collaborator.GetOrganizationIds(); orgIDs != nil { - if err = rights.RequireOrganization(ctx, orgIDs, ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST); err != nil { + if err = rights.RequireOrganization( + ctx, orgIDs, ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, + ); err != nil { return nil, err } } @@ -227,7 +250,11 @@ func (is *IdentityServer) listApplications(ctx context.Context, req *ttnpb.ListA if len(ids) == 0 { return nil } - callerMemberships, err = st.FindAccountMembershipChains(ctx, callerAccountID, "application", idStrings(ids...)...) + callerMemberships, err = st.FindAccountMembershipChains( + ctx, + callerAccountID, + "application", + idStrings(ids...)...) if err != nil { return err } @@ -260,8 +287,13 @@ func (is *IdentityServer) listApplications(ctx context.Context, req *ttnpb.ListA return apps, nil } -func (is *IdentityServer) updateApplication(ctx context.Context, req *ttnpb.UpdateApplicationRequest) (app *ttnpb.Application, err error) { - if err = rights.RequireApplication(ctx, req.Application.GetIds(), ttnpb.Right_RIGHT_APPLICATION_SETTINGS_BASIC); err != nil { +func (is *IdentityServer) updateApplication( + ctx context.Context, + req *ttnpb.UpdateApplicationRequest, +) (app *ttnpb.Application, err error) { + if err = rights.RequireApplication( + ctx, req.Application.GetIds(), ttnpb.Right_RIGHT_APPLICATION_SETTINGS_BASIC, + ); err != nil { return nil, err } req.FieldMask = cleanFieldMaskPaths(ttnpb.ApplicationFieldPathsNested, req.FieldMask, nil, getPaths) @@ -285,10 +317,14 @@ func (is *IdentityServer) updateApplication(ctx context.Context, req *ttnpb.Upda []string{"administrative_contact", "technical_contact"}, ) err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { - if err := validateContactIsCollaborator(ctx, st, req.Application.AdministrativeContact, req.Application.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Application.AdministrativeContact, req.Application.GetEntityIdentifiers(), + ); err != nil { return err } - if err := validateContactIsCollaborator(ctx, st, req.Application.TechnicalContact, req.Application.GetEntityIdentifiers()); err != nil { + if err := validateContactIsCollaborator( + ctx, st, req.Application.TechnicalContact, req.Application.GetEntityIdentifiers(), + ); err != nil { return err } app, err = st.UpdateApplication(ctx, req.Application, req.FieldMask.GetPaths()) @@ -307,13 +343,21 @@ func (is *IdentityServer) updateApplication(ctx context.Context, req *ttnpb.Upda if err != nil { return nil, err } - events.Publish(evtUpdateApplication.NewWithIdentifiersAndData(ctx, req.Application.GetIds(), req.FieldMask.GetPaths())) + events.Publish( + evtUpdateApplication.NewWithIdentifiersAndData(ctx, req.Application.GetIds(), req.FieldMask.GetPaths()), + ) return app, nil } -var errApplicationHasDevices = errors.DefineFailedPrecondition("application_has_devices", "application still has `{count}` devices") +var errApplicationHasDevices = errors.DefineFailedPrecondition( + "application_has_devices", + "application still has `{count}` devices", +) -func (is *IdentityServer) deleteApplication(ctx context.Context, ids *ttnpb.ApplicationIdentifiers) (*emptypb.Empty, error) { +func (is *IdentityServer) deleteApplication( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, +) (*emptypb.Empty, error) { if err := rights.RequireApplication(ctx, ids, ttnpb.Right_RIGHT_APPLICATION_DELETE); err != nil { return nil, err } @@ -334,8 +378,13 @@ func (is *IdentityServer) deleteApplication(ctx context.Context, ids *ttnpb.Appl return ttnpb.Empty, nil } -func (is *IdentityServer) restoreApplication(ctx context.Context, ids *ttnpb.ApplicationIdentifiers) (*emptypb.Empty, error) { - if err := rights.RequireApplication(store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_APPLICATION_DELETE); err != nil { +func (is *IdentityServer) restoreApplication( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, +) (*emptypb.Empty, error) { + if err := rights.RequireApplication( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_APPLICATION_DELETE, + ); err != nil { return nil, err } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { @@ -359,7 +408,10 @@ func (is *IdentityServer) restoreApplication(ctx context.Context, ids *ttnpb.App return ttnpb.Empty, nil } -func (is *IdentityServer) purgeApplication(ctx context.Context, ids *ttnpb.ApplicationIdentifiers) (*emptypb.Empty, error) { +func (is *IdentityServer) purgeApplication( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, +) (*emptypb.Empty, error) { if !is.IsAdmin(ctx) { return nil, errAdminsPurgeApplications.New() } @@ -395,8 +447,13 @@ func (is *IdentityServer) purgeApplication(ctx context.Context, ids *ttnpb.Appli return ttnpb.Empty, nil } -func (is *IdentityServer) issueDevEUI(ctx context.Context, ids *ttnpb.ApplicationIdentifiers) (*ttnpb.IssueDevEUIResponse, error) { - if err := rights.RequireApplication(store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE); err != nil { +func (is *IdentityServer) issueDevEUI( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, +) (*ttnpb.IssueDevEUIResponse, error) { + if err := rights.RequireApplication( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ); err != nil { return nil, err } if !is.config.DevEUIBlock.Enabled { @@ -424,7 +481,10 @@ type applicationRegistry struct { *IdentityServer } -func (ar *applicationRegistry) Create(ctx context.Context, req *ttnpb.CreateApplicationRequest) (*ttnpb.Application, error) { +func (ar *applicationRegistry) Create( + ctx context.Context, + req *ttnpb.CreateApplicationRequest, +) (*ttnpb.Application, error) { return ar.createApplication(ctx, req) } @@ -432,11 +492,17 @@ func (ar *applicationRegistry) Get(ctx context.Context, req *ttnpb.GetApplicatio return ar.getApplication(ctx, req) } -func (ar *applicationRegistry) List(ctx context.Context, req *ttnpb.ListApplicationsRequest) (*ttnpb.Applications, error) { +func (ar *applicationRegistry) List( + ctx context.Context, + req *ttnpb.ListApplicationsRequest, +) (*ttnpb.Applications, error) { return ar.listApplications(ctx, req) } -func (ar *applicationRegistry) Update(ctx context.Context, req *ttnpb.UpdateApplicationRequest) (*ttnpb.Application, error) { +func (ar *applicationRegistry) Update( + ctx context.Context, + req *ttnpb.UpdateApplicationRequest, +) (*ttnpb.Application, error) { return ar.updateApplication(ctx, req) } @@ -452,6 +518,9 @@ func (ar *applicationRegistry) Restore(ctx context.Context, req *ttnpb.Applicati return ar.restoreApplication(ctx, req) } -func (ar *applicationRegistry) IssueDevEUI(ctx context.Context, req *ttnpb.ApplicationIdentifiers) (*ttnpb.IssueDevEUIResponse, error) { +func (ar *applicationRegistry) IssueDevEUI( + ctx context.Context, + req *ttnpb.ApplicationIdentifiers, +) (*ttnpb.IssueDevEUIResponse, error) { return ar.issueDevEUI(ctx, req) } diff --git a/pkg/identityserver/application_registry_test.go b/pkg/identityserver/application_registry_test.go index 4f47b0cb81..0966e92478 100644 --- a/pkg/identityserver/application_registry_test.go +++ b/pkg/identityserver/application_registry_test.go @@ -239,7 +239,9 @@ func TestApplicationsCRUD(t *testing.T) { a.So(err, should.BeNil) }) - for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{ + nil, usr1.GetOrganizationOrUserIdentifiers(), + } { list, err := reg.List(ctx, &ttnpb.ListApplicationsRequest{ FieldMask: ttnpb.FieldMask("name"), Collaborator: collaborator, From 18f7a701e79f9e1a41027c4675cc6284fabffd1e Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Wed, 5 Jul 2023 17:55:28 -0300 Subject: [PATCH 18/19] is: Format client registry --- pkg/identityserver/client_registry_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/client_registry_test.go b/pkg/identityserver/client_registry_test.go index 133c38793d..101b04f0c7 100644 --- a/pkg/identityserver/client_registry_test.go +++ b/pkg/identityserver/client_registry_test.go @@ -261,7 +261,9 @@ func TestClientsCRUD(t *testing.T) { a.So(got.StateDescription, should.Equal, "") } - for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{nil, usr1.GetOrganizationOrUserIdentifiers()} { + for _, collaborator := range []*ttnpb.OrganizationOrUserIdentifiers{ + nil, usr1.GetOrganizationOrUserIdentifiers(), + } { list, err := reg.List(ctx, &ttnpb.ListClientsRequest{ FieldMask: ttnpb.FieldMask("name"), Collaborator: collaborator, From 5245e769e043889c87b4a759250b9d08e0ebbded Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Wed, 5 Jul 2023 07:55:32 -0300 Subject: [PATCH 19/19] is: Format contact info registry --- pkg/identityserver/contact_info_registry.go | 35 ++++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/pkg/identityserver/contact_info_registry.go b/pkg/identityserver/contact_info_registry.go index c0e211e21a..b4cb18657e 100644 --- a/pkg/identityserver/contact_info_registry.go +++ b/pkg/identityserver/contact_info_registry.go @@ -78,7 +78,12 @@ func validateCollaboratorEqualsContact(collaborator, contact *ttnpb.Organization return nil } -func validateContactIsCollaborator(ctx context.Context, st store.Store, contact *ttnpb.OrganizationOrUserIdentifiers, entity *ttnpb.EntityIdentifiers) error { +func validateContactIsCollaborator( + ctx context.Context, + st store.Store, + contact *ttnpb.OrganizationOrUserIdentifiers, + entity *ttnpb.EntityIdentifiers, +) error { if contact == nil { return nil } @@ -92,7 +97,10 @@ func validateContactIsCollaborator(ctx context.Context, st store.Store, contact return nil } -func (is *IdentityServer) requestContactInfoValidation(ctx context.Context, ids *ttnpb.EntityIdentifiers) (*ttnpb.ContactInfoValidation, error) { +func (is *IdentityServer) requestContactInfoValidation( + ctx context.Context, + ids *ttnpb.EntityIdentifiers, +) (*ttnpb.ContactInfoValidation, error) { // NOTE: This does NOT check auth. Internal use only. id, err := auth.GenerateID(ctx) if err != nil { @@ -165,10 +173,15 @@ func (is *IdentityServer) requestContactInfoValidation(ctx context.Context, ids Token: validation.Token, TTL: ttl, } - go is.SendTemplateEmailToUsers(is.FromRequestContext(ctx), "validate", func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { - validateData.TemplateData = data - return validateData, nil - }, &ttnpb.User{PrimaryEmailAddress: address}) + go is.SendTemplateEmailToUsers( // nolint:errcheck + is.FromRequestContext(ctx), + "validate", + func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { + validateData.TemplateData = data + return validateData, nil + }, + &ttnpb.User{PrimaryEmailAddress: address}, + ) pendingContactInfo = append(pendingContactInfo, validation.ContactInfo...) validation.Token = "" // Unset tokens after sending emails } @@ -214,7 +227,10 @@ type contactInfoRegistry struct { var errNoContactInfoForEntity = errors.DefineInvalidArgument("no_contact_info", "no contact info for this entity type") -func (cir *contactInfoRegistry) RequestValidation(ctx context.Context, ids *ttnpb.EntityIdentifiers) (*ttnpb.ContactInfoValidation, error) { +func (cir *contactInfoRegistry) RequestValidation( + ctx context.Context, + ids *ttnpb.EntityIdentifiers, +) (*ttnpb.ContactInfoValidation, error) { var err error switch id := ids.GetIds().(type) { case *ttnpb.EntityIdentifiers_ApplicationIds: @@ -236,6 +252,9 @@ func (cir *contactInfoRegistry) RequestValidation(ctx context.Context, ids *ttnp return cir.requestContactInfoValidation(ctx, ids) } -func (cir *contactInfoRegistry) Validate(ctx context.Context, req *ttnpb.ContactInfoValidation) (*emptypb.Empty, error) { +func (cir *contactInfoRegistry) Validate( + ctx context.Context, + req *ttnpb.ContactInfoValidation, +) (*emptypb.Empty, error) { return cir.validateContactInfo(ctx, req) }