diff --git a/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go index a8fda0e8b4dd..54fba581d157 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go @@ -135,7 +135,7 @@ func (x LoggedInUser_UserType) Number() protoreflect.EnumNumber { // Deprecated: Use LoggedInUser_UserType.Descriptor instead. func (LoggedInUser_UserType) EnumDescriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{1, 0} + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{3, 0} } // Cluster describes cluster fields. @@ -283,6 +283,103 @@ func (x *Cluster) GetShowResources() ShowResources { return ShowResources_SHOW_RESOURCES_UNSPECIFIED } +// RequestModeKubernetesResources is the Kubernetes resource identifier used +// in access request mode settings. +// Modeled after existing message KubernetesResource. +type RequestModeKubernetesResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Kind specifies the Kubernetes Resource type. + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` +} + +func (x *RequestModeKubernetesResource) Reset() { + *x = RequestModeKubernetesResource{} + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestModeKubernetesResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestModeKubernetesResource) ProtoMessage() {} + +func (x *RequestModeKubernetesResource) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequestModeKubernetesResource.ProtoReflect.Descriptor instead. +func (*RequestModeKubernetesResource) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{1} +} + +func (x *RequestModeKubernetesResource) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +// AccessRequestMode describes request mode settings for applicable resources. +type AccessRequestMode struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // KubernetesResources defines which Kubernetes subresources a user can + // request during request creation. + KubernetesResources []*RequestModeKubernetesResource `protobuf:"bytes,1,rep,name=kubernetes_resources,json=kubernetesResources,proto3" json:"kubernetes_resources,omitempty"` +} + +func (x *AccessRequestMode) Reset() { + *x = AccessRequestMode{} + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AccessRequestMode) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccessRequestMode) ProtoMessage() {} + +func (x *AccessRequestMode) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccessRequestMode.ProtoReflect.Descriptor instead. +func (*AccessRequestMode) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{2} +} + +func (x *AccessRequestMode) GetKubernetesResources() []*RequestModeKubernetesResource { + if x != nil { + return x.KubernetesResources + } + return nil +} + // LoggedInUser describes a logged-in user type LoggedInUser struct { state protoimpl.MessageState @@ -307,11 +404,13 @@ type LoggedInUser struct { // Only present when detailed information is queried from the auth server. RequestableRoles []string `protobuf:"bytes,7,rep,name=requestable_roles,json=requestableRoles,proto3" json:"requestable_roles,omitempty"` UserType LoggedInUser_UserType `protobuf:"varint,8,opt,name=user_type,json=userType,proto3,enum=teleport.lib.teleterm.v1.LoggedInUser_UserType" json:"user_type,omitempty"` + // RequestMode defines what resource kinds a user can request for applicable resources. + RequestMode *AccessRequestMode `protobuf:"bytes,9,opt,name=request_mode,json=requestMode,proto3" json:"request_mode,omitempty"` } func (x *LoggedInUser) Reset() { *x = LoggedInUser{} - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[1] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -323,7 +422,7 @@ func (x *LoggedInUser) String() string { func (*LoggedInUser) ProtoMessage() {} func (x *LoggedInUser) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[1] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -336,7 +435,7 @@ func (x *LoggedInUser) ProtoReflect() protoreflect.Message { // Deprecated: Use LoggedInUser.ProtoReflect.Descriptor instead. func (*LoggedInUser) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{1} + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{3} } func (x *LoggedInUser) GetName() string { @@ -395,6 +494,13 @@ func (x *LoggedInUser) GetUserType() LoggedInUser_UserType { return LoggedInUser_USER_TYPE_UNSPECIFIED } +func (x *LoggedInUser) GetRequestMode() *AccessRequestMode { + if x != nil { + return x.RequestMode + } + return nil +} + // ACL is the access control list of the user type ACL struct { state protoimpl.MessageState @@ -431,7 +537,7 @@ type ACL struct { func (x *ACL) Reset() { *x = ACL{} - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[2] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -443,7 +549,7 @@ func (x *ACL) String() string { func (*ACL) ProtoMessage() {} func (x *ACL) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[2] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -456,7 +562,7 @@ func (x *ACL) ProtoReflect() protoreflect.Message { // Deprecated: Use ACL.ProtoReflect.Descriptor instead. func (*ACL) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{2} + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{4} } func (x *ACL) GetAuthConnectors() *ResourceAccess { @@ -572,7 +678,7 @@ type ResourceAccess struct { func (x *ResourceAccess) Reset() { *x = ResourceAccess{} - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[3] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -584,7 +690,7 @@ func (x *ResourceAccess) String() string { func (*ResourceAccess) ProtoMessage() {} func (x *ResourceAccess) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[3] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -597,7 +703,7 @@ func (x *ResourceAccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceAccess.ProtoReflect.Descriptor instead. func (*ResourceAccess) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{3} + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{5} } func (x *ResourceAccess) GetList() bool { @@ -656,7 +762,7 @@ type Features struct { func (x *Features) Reset() { *x = Features{} - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[4] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -668,7 +774,7 @@ func (x *Features) String() string { func (*Features) ProtoMessage() {} func (x *Features) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[4] + mi := &file_teleport_lib_teleterm_v1_cluster_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -681,7 +787,7 @@ func (x *Features) ProtoReflect() protoreflect.Message { // Deprecated: Use Features.ProtoReflect.Descriptor instead. func (*Features) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{4} + return file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP(), []int{6} } func (x *Features) GetAdvancedAccessWorkflows() bool { @@ -732,124 +838,141 @@ var file_teleport_lib_teleterm_v1_cluster_proto_rawDesc = []byte{ 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x22, 0xac, 0x03, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x64, 0x49, 0x6e, 0x55, 0x73, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x73, 0x68, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x2f, 0x0a, 0x03, 0x61, 0x63, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x43, 0x4c, 0x52, 0x03, 0x61, 0x63, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x65, - 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x12, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, - 0x65, 0x77, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, - 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x64, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x4d, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, - 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x53, 0x45, 0x52, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, - 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x53, 0x4f, 0x10, 0x02, 0x22, - 0xc8, 0x07, 0x0a, 0x03, 0x41, 0x43, 0x4c, 0x12, 0x51, 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x05, 0x72, 0x6f, - 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x05, 0x75, 0x73, - 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x53, 0x0a, 0x10, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0f, - 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x40, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x33, 0x0a, 0x1d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x4b, + 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x7f, 0x0a, 0x11, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x6a, 0x0a, 0x14, 0x6b, 0x75, + 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x4b, + 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x13, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xfc, 0x03, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x67, 0x65, + 0x64, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, + 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x73, + 0x12, 0x2f, 0x0a, 0x03, 0x61, 0x63, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x43, 0x4c, 0x52, 0x03, 0x61, 0x63, + 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, + 0x67, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x64, 0x49, 0x6e, 0x55, + 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4e, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x4d, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, + 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x53, 0x53, 0x4f, 0x10, 0x02, 0x22, 0xc8, 0x07, 0x0a, 0x03, 0x41, 0x43, 0x4c, 0x12, 0x51, 0x0a, + 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x12, 0x3e, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x40, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3c, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x12, 0x3e, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x12, 0x53, 0x0a, 0x10, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x52, 0x0f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x40, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, - 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x3a, 0x0a, 0x03, 0x64, 0x62, 0x73, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x03, 0x64, 0x62, - 0x73, 0x12, 0x4a, 0x0a, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, - 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, - 0x12, 0x55, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, + 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x07, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3c, 0x0a, + 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x51, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, - 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x0e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x69, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x64, 0x69, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x65, 0x64, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x73, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x75, 0x73, 0x65, 0x22, 0x7b, 0x0a, 0x08, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x61, 0x64, 0x76, 0x61, 0x6e, - 0x63, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61, 0x64, 0x76, 0x61, - 0x6e, 0x63, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x62, 0x61, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x55, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65, - 0x64, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x2a, 0x73, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x48, 0x4f, - 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x48, 0x4f, - 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, - 0x45, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x48, 0x4f, - 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, - 0x53, 0x53, 0x49, 0x42, 0x4c, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x42, 0x54, 0x5a, - 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, - 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x3a, 0x0a, 0x03, 0x64, + 0x62, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x52, 0x03, 0x64, 0x62, 0x73, 0x12, 0x4a, 0x0a, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x55, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x65, 0x64, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x10, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x51, 0x0a, + 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x8e, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x61, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x65, + 0x64, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x65, 0x64, 0x69, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x75, 0x73, + 0x65, 0x22, 0x7b, 0x0a, 0x08, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x3a, 0x0a, + 0x19, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x17, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x12, 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, + 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x6c, 0x6c, + 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x2a, 0x73, + 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x1e, 0x0a, 0x1a, 0x53, 0x48, 0x4f, 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x1e, 0x0a, 0x1a, 0x53, 0x48, 0x4f, 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, + 0x22, 0x0a, 0x1e, 0x53, 0x48, 0x4f, 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x49, 0x42, 0x4c, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, + 0x59, 0x10, 0x02, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, + 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -865,40 +988,44 @@ func file_teleport_lib_teleterm_v1_cluster_proto_rawDescGZIP() []byte { } var file_teleport_lib_teleterm_v1_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_teleport_lib_teleterm_v1_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_teleport_lib_teleterm_v1_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_teleport_lib_teleterm_v1_cluster_proto_goTypes = []any{ - (ShowResources)(0), // 0: teleport.lib.teleterm.v1.ShowResources - (LoggedInUser_UserType)(0), // 1: teleport.lib.teleterm.v1.LoggedInUser.UserType - (*Cluster)(nil), // 2: teleport.lib.teleterm.v1.Cluster - (*LoggedInUser)(nil), // 3: teleport.lib.teleterm.v1.LoggedInUser - (*ACL)(nil), // 4: teleport.lib.teleterm.v1.ACL - (*ResourceAccess)(nil), // 5: teleport.lib.teleterm.v1.ResourceAccess - (*Features)(nil), // 6: teleport.lib.teleterm.v1.Features + (ShowResources)(0), // 0: teleport.lib.teleterm.v1.ShowResources + (LoggedInUser_UserType)(0), // 1: teleport.lib.teleterm.v1.LoggedInUser.UserType + (*Cluster)(nil), // 2: teleport.lib.teleterm.v1.Cluster + (*RequestModeKubernetesResource)(nil), // 3: teleport.lib.teleterm.v1.RequestModeKubernetesResource + (*AccessRequestMode)(nil), // 4: teleport.lib.teleterm.v1.AccessRequestMode + (*LoggedInUser)(nil), // 5: teleport.lib.teleterm.v1.LoggedInUser + (*ACL)(nil), // 6: teleport.lib.teleterm.v1.ACL + (*ResourceAccess)(nil), // 7: teleport.lib.teleterm.v1.ResourceAccess + (*Features)(nil), // 8: teleport.lib.teleterm.v1.Features } var file_teleport_lib_teleterm_v1_cluster_proto_depIdxs = []int32{ - 3, // 0: teleport.lib.teleterm.v1.Cluster.logged_in_user:type_name -> teleport.lib.teleterm.v1.LoggedInUser - 6, // 1: teleport.lib.teleterm.v1.Cluster.features:type_name -> teleport.lib.teleterm.v1.Features + 5, // 0: teleport.lib.teleterm.v1.Cluster.logged_in_user:type_name -> teleport.lib.teleterm.v1.LoggedInUser + 8, // 1: teleport.lib.teleterm.v1.Cluster.features:type_name -> teleport.lib.teleterm.v1.Features 0, // 2: teleport.lib.teleterm.v1.Cluster.show_resources:type_name -> teleport.lib.teleterm.v1.ShowResources - 4, // 3: teleport.lib.teleterm.v1.LoggedInUser.acl:type_name -> teleport.lib.teleterm.v1.ACL - 1, // 4: teleport.lib.teleterm.v1.LoggedInUser.user_type:type_name -> teleport.lib.teleterm.v1.LoggedInUser.UserType - 5, // 5: teleport.lib.teleterm.v1.ACL.auth_connectors:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 6: teleport.lib.teleterm.v1.ACL.roles:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 7: teleport.lib.teleterm.v1.ACL.users:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 8: teleport.lib.teleterm.v1.ACL.trusted_clusters:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 9: teleport.lib.teleterm.v1.ACL.events:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 10: teleport.lib.teleterm.v1.ACL.tokens:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 11: teleport.lib.teleterm.v1.ACL.servers:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 12: teleport.lib.teleterm.v1.ACL.apps:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 13: teleport.lib.teleterm.v1.ACL.dbs:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 14: teleport.lib.teleterm.v1.ACL.kubeservers:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 15: teleport.lib.teleterm.v1.ACL.access_requests:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 16: teleport.lib.teleterm.v1.ACL.recorded_sessions:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 5, // 17: teleport.lib.teleterm.v1.ACL.active_sessions:type_name -> teleport.lib.teleterm.v1.ResourceAccess - 18, // [18:18] is the sub-list for method output_type - 18, // [18:18] is the sub-list for method input_type - 18, // [18:18] is the sub-list for extension type_name - 18, // [18:18] is the sub-list for extension extendee - 0, // [0:18] is the sub-list for field type_name + 3, // 3: teleport.lib.teleterm.v1.AccessRequestMode.kubernetes_resources:type_name -> teleport.lib.teleterm.v1.RequestModeKubernetesResource + 6, // 4: teleport.lib.teleterm.v1.LoggedInUser.acl:type_name -> teleport.lib.teleterm.v1.ACL + 1, // 5: teleport.lib.teleterm.v1.LoggedInUser.user_type:type_name -> teleport.lib.teleterm.v1.LoggedInUser.UserType + 4, // 6: teleport.lib.teleterm.v1.LoggedInUser.request_mode:type_name -> teleport.lib.teleterm.v1.AccessRequestMode + 7, // 7: teleport.lib.teleterm.v1.ACL.auth_connectors:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 8: teleport.lib.teleterm.v1.ACL.roles:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 9: teleport.lib.teleterm.v1.ACL.users:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 10: teleport.lib.teleterm.v1.ACL.trusted_clusters:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 11: teleport.lib.teleterm.v1.ACL.events:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 12: teleport.lib.teleterm.v1.ACL.tokens:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 13: teleport.lib.teleterm.v1.ACL.servers:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 14: teleport.lib.teleterm.v1.ACL.apps:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 15: teleport.lib.teleterm.v1.ACL.dbs:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 16: teleport.lib.teleterm.v1.ACL.kubeservers:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 17: teleport.lib.teleterm.v1.ACL.access_requests:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 18: teleport.lib.teleterm.v1.ACL.recorded_sessions:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 7, // 19: teleport.lib.teleterm.v1.ACL.active_sessions:type_name -> teleport.lib.teleterm.v1.ResourceAccess + 20, // [20:20] is the sub-list for method output_type + 20, // [20:20] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name } func init() { file_teleport_lib_teleterm_v1_cluster_proto_init() } @@ -912,7 +1039,7 @@ func file_teleport_lib_teleterm_v1_cluster_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_lib_teleterm_v1_cluster_proto_rawDesc, NumEnums: 2, - NumMessages: 5, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/cluster_pb.ts b/gen/proto/ts/teleport/lib/teleterm/v1/cluster_pb.ts index 9d283257962e..4de3fdb1cb3c 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/cluster_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/cluster_pb.ts @@ -114,6 +114,35 @@ export interface Cluster { */ showResources: ShowResources; } +/** + * RequestModeKubernetesResources is the Kubernetes resource identifier used + * in access request mode settings. + * Modeled after existing message KubernetesResource. + * + * @generated from protobuf message teleport.lib.teleterm.v1.RequestModeKubernetesResource + */ +export interface RequestModeKubernetesResource { + /** + * Kind specifies the Kubernetes Resource type. + * + * @generated from protobuf field: string kind = 1; + */ + kind: string; +} +/** + * AccessRequestMode describes request mode settings for applicable resources. + * + * @generated from protobuf message teleport.lib.teleterm.v1.AccessRequestMode + */ +export interface AccessRequestMode { + /** + * KubernetesResources defines which Kubernetes subresources a user can + * request during request creation. + * + * @generated from protobuf field: repeated teleport.lib.teleterm.v1.RequestModeKubernetesResource kubernetes_resources = 1; + */ + kubernetesResources: RequestModeKubernetesResource[]; +} /** * LoggedInUser describes a logged-in user * @@ -169,6 +198,12 @@ export interface LoggedInUser { * @generated from protobuf field: teleport.lib.teleterm.v1.LoggedInUser.UserType user_type = 8; */ userType: LoggedInUser_UserType; + /** + * RequestMode defines what resource kinds a user can request for applicable resources. + * + * @generated from protobuf field: teleport.lib.teleterm.v1.AccessRequestMode request_mode = 9; + */ + requestMode?: AccessRequestMode; } /** * UserType indicates whether the user was created through an SSO provider or in Teleport itself. @@ -474,6 +509,100 @@ class Cluster$Type extends MessageType { */ export const Cluster = new Cluster$Type(); // @generated message type with reflection information, may provide speed optimized methods +class RequestModeKubernetesResource$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.RequestModeKubernetesResource", [ + { no: 1, name: "kind", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): RequestModeKubernetesResource { + const message = globalThis.Object.create((this.messagePrototype!)); + message.kind = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: RequestModeKubernetesResource): RequestModeKubernetesResource { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string kind */ 1: + message.kind = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: RequestModeKubernetesResource, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string kind = 1; */ + if (message.kind !== "") + writer.tag(1, WireType.LengthDelimited).string(message.kind); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.RequestModeKubernetesResource + */ +export const RequestModeKubernetesResource = new RequestModeKubernetesResource$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class AccessRequestMode$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.AccessRequestMode", [ + { no: 1, name: "kubernetes_resources", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => RequestModeKubernetesResource } + ]); + } + create(value?: PartialMessage): AccessRequestMode { + const message = globalThis.Object.create((this.messagePrototype!)); + message.kubernetesResources = []; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: AccessRequestMode): AccessRequestMode { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* repeated teleport.lib.teleterm.v1.RequestModeKubernetesResource kubernetes_resources */ 1: + message.kubernetesResources.push(RequestModeKubernetesResource.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: AccessRequestMode, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* repeated teleport.lib.teleterm.v1.RequestModeKubernetesResource kubernetes_resources = 1; */ + for (let i = 0; i < message.kubernetesResources.length; i++) + RequestModeKubernetesResource.internalBinaryWrite(message.kubernetesResources[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.AccessRequestMode + */ +export const AccessRequestMode = new AccessRequestMode$Type(); +// @generated message type with reflection information, may provide speed optimized methods class LoggedInUser$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.LoggedInUser", [ @@ -484,7 +613,8 @@ class LoggedInUser$Type extends MessageType { { no: 5, name: "active_requests", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, { no: 6, name: "suggested_reviewers", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, { no: 7, name: "requestable_roles", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, - { no: 8, name: "user_type", kind: "enum", T: () => ["teleport.lib.teleterm.v1.LoggedInUser.UserType", LoggedInUser_UserType, "USER_TYPE_"] } + { no: 8, name: "user_type", kind: "enum", T: () => ["teleport.lib.teleterm.v1.LoggedInUser.UserType", LoggedInUser_UserType, "USER_TYPE_"] }, + { no: 9, name: "request_mode", kind: "message", T: () => AccessRequestMode } ]); } create(value?: PartialMessage): LoggedInUser { @@ -529,6 +659,9 @@ class LoggedInUser$Type extends MessageType { case /* teleport.lib.teleterm.v1.LoggedInUser.UserType user_type */ 8: message.userType = reader.int32(); break; + case /* teleport.lib.teleterm.v1.AccessRequestMode request_mode */ 9: + message.requestMode = AccessRequestMode.internalBinaryRead(reader, reader.uint32(), options, message.requestMode); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -565,6 +698,9 @@ class LoggedInUser$Type extends MessageType { /* teleport.lib.teleterm.v1.LoggedInUser.UserType user_type = 8; */ if (message.userType !== 0) writer.tag(8, WireType.Varint).int32(message.userType); + /* teleport.lib.teleterm.v1.AccessRequestMode request_mode = 9; */ + if (message.requestMode) + AccessRequestMode.internalBinaryWrite(message.requestMode, writer.tag(9, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); diff --git a/lib/teleterm/apiserver/handler/handler_clusters.go b/lib/teleterm/apiserver/handler/handler_clusters.go index 412c9147b91e..61a960c4a1e0 100644 --- a/lib/teleterm/apiserver/handler/handler_clusters.go +++ b/lib/teleterm/apiserver/handler/handler_clusters.go @@ -119,6 +119,7 @@ func newAPIRootClusterWithDetails(cluster *clusters.ClusterWithDetails) (*api.Cl } apiCluster.LoggedInUser.RequestableRoles = cluster.RequestableRoles apiCluster.LoggedInUser.SuggestedReviewers = cluster.SuggestedReviewers + apiCluster.LoggedInUser.RequestMode = cluster.RequestMode apiCluster.AuthClusterId = cluster.AuthClusterID apiCluster.LoggedInUser.Acl = cluster.ACL userType, err := clusters.UserTypeFromString(cluster.UserType) diff --git a/lib/teleterm/clusters/cluster.go b/lib/teleterm/clusters/cluster.go index 0d4c16a4a8d4..db68b03caab2 100644 --- a/lib/teleterm/clusters/cluster.go +++ b/lib/teleterm/clusters/cluster.go @@ -79,6 +79,8 @@ type ClusterWithDetails struct { ProxyVersion string // ShowResources tells if the cluster can show requestable resources on the resources page. ShowResources constants.ShowResources + // RequestMode defines access request mode for specific resources. + RequestMode *api.AccessRequestMode } // Connected indicates if connection to the cluster can be established @@ -194,17 +196,29 @@ func (c *Cluster) GetWithDetails(ctx context.Context, authClient authclient.Clie Cluster: c, SuggestedReviewers: caps.SuggestedReviewers, RequestableRoles: caps.RequestableRoles, - Features: authPingResponse.ServerFeatures, - AuthClusterID: authClusterID, - ACL: acl, - UserType: user.GetUserType(), - ProxyVersion: clusterPingResponse.ServerVersion, - ShowResources: webConfig.UI.ShowResources, + RequestMode: &api.AccessRequestMode{ + KubernetesResources: makeKubernetesRequestMode(caps.RequestMode.KubernetesResources), + }, + Features: authPingResponse.ServerFeatures, + AuthClusterID: authClusterID, + ACL: acl, + UserType: user.GetUserType(), + ProxyVersion: clusterPingResponse.ServerVersion, + ShowResources: webConfig.UI.ShowResources, } return withDetails, nil } +func makeKubernetesRequestMode(resources []types.RequestModeKubernetesResource) []*api.RequestModeKubernetesResource { + apiResources := make([]*api.RequestModeKubernetesResource, 0, len(resources)) + for _, resource := range resources { + apiResources = append(apiResources, &api.RequestModeKubernetesResource{Kind: resource.Kind}) + } + + return apiResources +} + func convertToAPIResourceAccess(access services.ResourceAccess) *api.ResourceAccess { return &api.ResourceAccess{ List: access.List, diff --git a/proto/teleport/lib/teleterm/v1/cluster.proto b/proto/teleport/lib/teleterm/v1/cluster.proto index 638c41989c53..837552e16921 100644 --- a/proto/teleport/lib/teleterm/v1/cluster.proto +++ b/proto/teleport/lib/teleterm/v1/cluster.proto @@ -70,6 +70,21 @@ enum ShowResources { SHOW_RESOURCES_ACCESSIBLE_ONLY = 2; } +// RequestModeKubernetesResources is the Kubernetes resource identifier used +// in access request mode settings. +// Modeled after existing message KubernetesResource. +message RequestModeKubernetesResource { + // Kind specifies the Kubernetes Resource type. + string kind = 1; +} + +// AccessRequestMode describes request mode settings for applicable resources. +message AccessRequestMode { + // KubernetesResources defines which Kubernetes subresources a user can + // request during request creation. + repeated RequestModeKubernetesResource kubernetes_resources = 1; +} + // LoggedInUser describes a logged-in user message LoggedInUser { // name is the user name @@ -97,6 +112,8 @@ message LoggedInUser { USER_TYPE_SSO = 2; } UserType user_type = 8; + // RequestMode defines what resource kinds a user can request for applicable resources. + AccessRequestMode request_mode = 9; } // ACL is the access control list of the user diff --git a/web/packages/shared/components/AccessRequests/NewRequest/kube.ts b/web/packages/shared/components/AccessRequests/NewRequest/kube.ts index 5a43192082d8..60b1eb9f22b4 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/kube.ts +++ b/web/packages/shared/components/AccessRequests/NewRequest/kube.ts @@ -19,6 +19,7 @@ import { KubeResourceKind } from 'teleport/services/kube'; import { PendingListItem } from './RequestCheckout'; +import { RequestableResourceKind } from './resource'; export type KubeNamespaceRequest = { kubeCluster: string; @@ -78,3 +79,16 @@ export function getKubeResourceRequestMode( disableCheckoutFromKubeRestrictions, }; } + +export function requiresKubeResourceSelection({ + dryRun, + requestMode, + kind, +}: { + dryRun: boolean; + requestMode: KubeResourceKind[]; + kind: RequestableResourceKind; +}) { + const requiresKubeResourceSelection = requestMode.length > 0; + return dryRun && kind === 'kube_cluster' && requiresKubeResourceSelection; +} diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx index bf408a1a60d4..3aa2c5dad93a 100644 --- a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx +++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx @@ -32,6 +32,7 @@ import * as Icon from 'design/Icon'; import { pluralize } from 'shared/utils/text'; import { RequestCheckoutWithSlider } from 'shared/components/AccessRequests/NewRequest'; +import { excludeKubeClusterWithNamespaces } from 'shared/components/AccessRequests/NewRequest/kube'; import useAccessRequestCheckout from './useAccessRequestCheckout'; import { AssumedRolesBar } from './AssumedRolesBar'; @@ -102,6 +103,9 @@ export function AccessRequestCheckout() { pendingRequestTtlOptions, startTime, onStartTimeChange, + fetchKubeNamespaces, + bulkToggleKubeResources, + allowedKubeSubresourceKinds, } = useAccessRequestCheckout(); const isRoleRequest = data[0]?.kind === 'role'; @@ -110,12 +114,21 @@ export function AccessRequestCheckout() { setShowCheckout(false); } + const filteredData = data?.filter(d => + excludeKubeClusterWithNamespaces(d, data) + ); + + const numAddedResources = filteredData?.length; + // We should rather detect how much space we have, // but for simplicity we only count items. - const moreToShow = Math.max(data.length - MAX_RESOURCES_IN_BAR_TO_SHOW, 0); + const moreToShow = Math.max( + filteredData.length - MAX_RESOURCES_IN_BAR_TO_SHOW, + 0 + ); return ( <> - {data.length > 0 && !isCollapsed() && ( + {filteredData.length > 0 && !isCollapsed() && ( - {data.length}{' '} - {pluralize(data.length, isRoleRequest ? 'role' : 'resource')}{' '} + {numAddedResources}{' '} + {pluralize( + numAddedResources, + isRoleRequest ? 'role' : 'resource' + )}{' '} added to access request: - {data + {filteredData .slice(0, MAX_RESOURCES_IN_BAR_TO_SHOW) .map(c => { let resource = { - name: c.name, - key: `${c.clusterName}-${c.kind}-${c.id}`, + name: c.subResourceName + ? `${c.id}/${c.subResourceName}` + : c.name, + key: `${c.clusterName}-${c.kind}-${c.id}-${c.subResourceName}`, Icon: undefined, }; switch (c.kind) { @@ -158,6 +176,7 @@ export function AccessRequestCheckout() { resource.Icon = Icon.Database; break; case 'kube_cluster': + case 'namespace': resource.Icon = Icon.Kubernetes; break; case 'role': @@ -259,12 +278,9 @@ export function AccessRequestCheckout() { setPendingRequestTtl={setPendingRequestTtl} startTime={startTime} onStartTimeChange={onStartTimeChange} - // TODO: these are placeholders to satisy linters. - // There is a split PR that handles teleterm support - // that will be merged right after this one (once both are approved) - bulkToggleKubeResources={() => null} - fetchKubeNamespaces={() => null} - allowedKubeSubresourceKinds={[]} + fetchKubeNamespaces={fetchKubeNamespaces} + bulkToggleKubeResources={bulkToggleKubeResources} + allowedKubeSubresourceKinds={allowedKubeSubresourceKinds} /> )} diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts index 11f78ea5ebd0..5b810ed34f2c 100644 --- a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts +++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts @@ -20,6 +20,7 @@ import { useState, useEffect } from 'react'; import { Timestamp } from 'gen-proto-ts/google/protobuf/timestamp_pb'; import useAttempt from 'shared/hooks/useAttemptNext'; +import { Option } from 'shared/components/Select'; import { getDryRunMaxDuration, @@ -28,12 +29,20 @@ import { import { useSpecifiableFields } from 'shared/components/AccessRequests/NewRequest/useSpecifiableFields'; import { CreateRequest } from 'shared/components/AccessRequests/Shared/types'; +import { + excludeKubeClusterWithNamespaces, + KubeNamespaceRequest, + requiresKubeResourceSelection, +} from 'shared/components/AccessRequests/NewRequest/kube'; +import { PendingKubeResourceItem } from 'shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout'; +import { KubeResourceKind } from 'teleport/services/kube'; import { useAppContext } from 'teleterm/ui/appContextProvider'; import { PendingAccessRequest, extractResourceRequestProperties, ResourceRequest, + toResourceRequest, } from 'teleterm/ui/services/workspacesService/accessRequestsService'; import { retryWithRelogin } from 'teleterm/ui/utils'; import { @@ -55,6 +64,13 @@ export default function useAccessRequestCheckout() { ctx.workspacesService?.getActiveWorkspace()?.localClusterUri; const rootClusterUri = ctx.workspacesService?.getRootClusterUri(); + const loggedInUser = + ctx.clustersService.findCluster(rootClusterUri)?.loggedInUser; + const allowedKubeSubresourceKinds = + loggedInUser?.requestMode?.kubernetesResources?.map( + r => r.kind as KubeResourceKind + ) || []; + const { selectedReviewers, setSelectedReviewers, @@ -120,7 +136,7 @@ export default function useAccessRequestCheckout() { name: d.id, kind: d.kind, clusterName: d.clusterName, - subResourceName: '', + subResourceName: d.subResourceName || '', })), }); setResourceRequestRoles(response.applicableRoles); @@ -171,7 +187,7 @@ export default function useAccessRequestCheckout() { pendingRequest.resources.forEach(resourceRequest => { const { kind, id, name } = extractResourceRequestProperties(resourceRequest); - data.push({ + const item: PendingListItemWithOriginalItem = { kind, id, name, @@ -179,7 +195,12 @@ export default function useAccessRequestCheckout() { clusterName: ctx.clustersService.findClusterByResource( resourceRequest.resource.uri )?.name, - }); + }; + + if (kind === 'namespace') { + item.subResourceName = name; + } + data.push(item); }); } } @@ -204,6 +225,58 @@ export default function useAccessRequestCheckout() { await workspaceAccessRequest.addOrRemoveResource( pendingListItem.originalItem ); + + if (pendingListItem.kind === 'kube_cluster') { + deleteKubeClustersNamespaces({ + kubeClusterUri: pendingListItem.originalItem.resource.uri, + kubeClusterId: pendingListItem.id, + }); + } + } + + async function deleteKubeClustersNamespaces({ + kubeClusterUri, + kubeClusterId, + }: { + kubeClusterUri: string; + kubeClusterId: string; + }) { + const pending = workspaceAccessRequest.getPendingAccessRequest(); + if (pending.kind === 'role') return; + const hasInsertedItem = pending.resources.has(kubeClusterUri); + + if (!hasInsertedItem) { + const namespacesToDelete: ResourceRequest[] = []; + pending.resources.forEach(value => { + if (value.kind === 'namespace') { + const { kubeId } = routing.parseKubeResourceNamespaceUri( + value.resource.uri + ).params; + if (kubeId === kubeClusterId) { + namespacesToDelete.push(value); + } + } + }); + if (namespacesToDelete.length) { + await workspaceAccessRequest.addOrRemoveResources(namespacesToDelete); + } + } + } + + async function bulkToggleKubeResources( + items: PendingKubeResourceItem[], + kubeCluster: PendingListKubeClusterWithOriginalItem + ) { + await workspaceAccessRequest.addOrRemoveResources( + items.map(item => { + return toResourceRequest({ + kind: item.kind, + resourceId: item.id, + resourceName: item.subResourceName, + clusterUri: kubeCluster.originalItem.resource.uri, + }); + }) + ); } function getAssumedRequests() { @@ -222,6 +295,7 @@ export default function useAccessRequestCheckout() { */ function prepareAndCreateRequest(req: CreateRequest) { const data = getPendingAccessRequestsPerResource(pendingAccessRequest); + const params: CreateAccessRequestRequest = { rootClusterUri, reason: req.reason, @@ -229,18 +303,45 @@ export default function useAccessRequestCheckout() { dryRun: req.dryRun, resourceIds: data .filter(d => d.kind !== 'role') - .map(d => ({ - name: d.id, - clusterName: d.clusterName, - kind: d.kind, - subResourceName: '', - })), + .filter(d => excludeKubeClusterWithNamespaces(d, data)) + // Skip dry running with kube_cluster that requires + // subresource selection. Otherwise the user will see + // an error saying they can't make kube_cluster requests. + .filter( + d => + !requiresKubeResourceSelection({ + dryRun: req.dryRun, + kind: d.kind, + requestMode: allowedKubeSubresourceKinds, + }) + ) + .map(d => { + if (d.kind === 'namespace') { + return { + name: d.id, + kind: d.kind, + clusterName: d.clusterName, + subResourceName: d.subResourceName, + }; + } + return { + name: d.id, + clusterName: d.clusterName, + kind: d.kind, + subResourceName: '', + }; + }), roles: data.filter(d => d.kind === 'role').map(d => d.name), assumeStartTime: req.start && Timestamp.fromDate(req.start), maxDuration: req.maxDuration && Timestamp.fromDate(req.maxDuration), requestTtl: req.requestTTL && Timestamp.fromDate(req.requestTTL), }; + // Don't attempt creating anything if there are no resources selected. + if (!params.resourceIds.length && !params.roles.length) { + return; + } + // if we have a resource access request, we pass along the selected roles from the checkout if (params.resourceIds.length > 0) { params.roles = selectedResourceRequestRoles; @@ -250,7 +351,12 @@ export default function useAccessRequestCheckout() { return retryWithRelogin(ctx, clusterUri, () => ctx.clustersService.createAccessRequest(params).then(({ response }) => { - return { accessRequest: response.request, requestedCount: data.length }; + return { + accessRequest: response.request, + requestedCount: data.filter(d => + excludeKubeClusterWithNamespaces(d, data) + ).length, + }; }) ).catch(e => { setCreateRequestAttempt({ status: 'failed', statusText: e.message }); @@ -326,6 +432,30 @@ export default function useAccessRequestCheckout() { } } + async function fetchKubeNamespaces({ + kubeCluster, + search, + }: KubeNamespaceRequest): Promise { + const { response } = await ctx.tshd.listKubernetesResources({ + searchKeywords: search, + limit: 50, + useSearchAsRoles: true, + nextKey: '', + resourceType: 'namespace', + clusterUri, + predicateExpression: '', + kubernetesCluster: kubeCluster, + kubernetesNamespace: '', + }); + return response.resources.map(i => { + return { + kind: 'namespace', + value: i.name, + label: i.name, + }; + }); + } + const shouldShowClusterNameColumn = pendingAccessRequest?.kind === 'resource' && Array.from(pendingAccessRequest.resources.values()).some(a => @@ -365,6 +495,9 @@ export default function useAccessRequestCheckout() { pendingRequestTtlOptions, startTime, onStartTimeChange, + fetchKubeNamespaces, + bulkToggleKubeResources, + allowedKubeSubresourceKinds, }; } @@ -378,3 +511,8 @@ type PendingListItemWithOriginalItem = Omit & kind: 'role'; } ); + +type PendingListKubeClusterWithOriginalItem = Omit & { + kind: Extract; + originalItem: ResourceRequest; +}; diff --git a/web/packages/teleterm/src/ui/DocumentAccessRequests/NewRequest/useNewRequest.ts b/web/packages/teleterm/src/ui/DocumentAccessRequests/NewRequest/useNewRequest.ts index 323ed0ce89af..e6a024485732 100644 --- a/web/packages/teleterm/src/ui/DocumentAccessRequests/NewRequest/useNewRequest.ts +++ b/web/packages/teleterm/src/ui/DocumentAccessRequests/NewRequest/useNewRequest.ts @@ -22,6 +22,7 @@ import { FetchStatus, SortType } from 'design/DataTable/types'; import useAttempt from 'shared/hooks/useAttemptNext'; import { makeAdvancedSearchQueryForLabel } from 'shared/utils/advancedSearchLabelQuery'; +import { RequestableResourceKind } from 'shared/components/AccessRequests/NewRequest/resource'; import { ShowResources, @@ -49,7 +50,6 @@ import type { ResourceLabel, ResourceFilter as WeakAgentFilter, ResourcesResponse, - ResourceIdKind, UnifiedResource, } from 'teleport/services/agents'; import type * as teleportApps from 'teleport/services/apps'; @@ -347,8 +347,13 @@ function getDefaultSort(kind: ResourceKind): SortType { export type ResourceKind = | Extract< - ResourceIdKind, - 'node' | 'app' | 'db' | 'kube_cluster' | 'saml_idp_service_provider' + RequestableResourceKind, + | 'node' + | 'app' + | 'db' + | 'kube_cluster' + | 'saml_idp_service_provider' + | 'namespace' > | 'role'; diff --git a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx index 5c343b7a21db..f99a9899c593 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx +++ b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx @@ -225,7 +225,7 @@ export function UnifiedResources(props: { const bulkAddResources = useCallback( (resources: UnifiedResourceResponse[]) => { - accessRequestsService.addOrRemoveResources(resources); + accessRequestsService.addAllOrRemoveAllResources(resources); }, [accessRequestsService] ); diff --git a/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.test.ts b/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.test.ts index 751cab984905..c0b2f46f9921 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.test.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.test.ts @@ -124,7 +124,7 @@ test('getAddedItemsCount() returns added resource count for pending request', () expect(service.getAddedItemsCount()).toBe(0); }); -test('addOrRemoveResources() adds all resources to pending request', async () => { +test('addAllOrRemoveAllResources() adds all resources to pending request', async () => { const { accessRequestsService: service } = getTestSetup( getMockPendingResourceAccessRequest() ); @@ -138,7 +138,9 @@ test('addOrRemoveResources() adds all resources to pending request', async () => }); // add a single resource that isn't added should add to the request - await service.addOrRemoveResources([{ kind: 'server', resource: server }]); + await service.addAllOrRemoveAllResources([ + { kind: 'server', resource: server }, + ]); let pendingAccessRequest = service.getPendingAccessRequest(); expect( pendingAccessRequest.kind === 'resource' && @@ -149,7 +151,7 @@ test('addOrRemoveResources() adds all resources to pending request', async () => }); // padding an array that contains some resources already added and some that aren't should add them all - await service.addOrRemoveResources([ + await service.addAllOrRemoveAllResources([ { kind: 'server', resource: server }, { kind: 'server', resource: server2 }, ]); @@ -170,7 +172,7 @@ test('addOrRemoveResources() adds all resources to pending request', async () => }); // passing an array of resources that are all already added should remove all the passed resources - await service.addOrRemoveResources([ + await service.addAllOrRemoveAllResources([ { kind: 'server', resource: server }, { kind: 'server', resource: server2 }, ]); diff --git a/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.ts b/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.ts index f8ff36b9c249..81dae179d31f 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/accessRequestsService.ts @@ -24,6 +24,7 @@ import { DatabaseUri, KubeUri, AppUri, + KubeResourceNamespaceUri, } from 'teleterm/ui/uri'; import { ModalsService } from 'teleterm/ui/services/modals'; @@ -98,7 +99,41 @@ export class AccessRequestsService { }); } + /** + * Bulk action where if request is added, removes it or if request doesn't + * exist, adds it. + */ async addOrRemoveResources(requestedResources: ResourceRequest[]) { + if (!(await this.canUpdateRequest('resource'))) { + return; + } + this.setState(draftState => { + if (draftState.pending.kind !== 'resource') { + draftState.pending = { + kind: 'resource', + resources: new Map(), + }; + } + + const { resources } = draftState.pending; + + requestedResources.forEach(request => { + if (resources.has(request.resource.uri)) { + resources.delete(request.resource.uri); + } else { + resources.set(request.resource.uri, getRequiredProperties(request)); + } + }); + }); + } + + /** + * Removes all requested resources, if all the requested resources were already added + * or adds all requested resources, if not all requested resources were added. + * + * Typically used when user "selects all or deselects all" + */ + async addAllOrRemoveAllResources(requestedResources: ResourceRequest[]) { if (!(await this.canUpdateRequest('resource'))) { return; } @@ -209,6 +244,12 @@ function getRequiredProperties({ resource: { uri: resource.uri, samlApp: resource.samlApp }, }; } + if (kind === 'namespace') { + return { + kind, + resource: { uri: resource.uri }, + }; + } return { kind, resource: { uri: resource.uri }, @@ -258,6 +299,7 @@ export type ResourceRequest = kind: 'kube'; resource: { uri: KubeUri; + namespaces?: KubeResourceNamespaceUri[]; }; } | { @@ -266,6 +308,12 @@ export type ResourceRequest = uri: AppUri; samlApp: boolean; }; + } + | { + kind: 'namespace'; + resource: { + uri: KubeResourceNamespaceUri; + }; }; type SharedResourceAccessRequestKind = @@ -273,7 +321,8 @@ type SharedResourceAccessRequestKind = | 'db' | 'node' | 'kube_cluster' - | 'saml_idp_service_provider'; + | 'saml_idp_service_provider' + | 'namespace'; /** * Extracts `kind`, `id` and `name` from the resource request. @@ -287,8 +336,13 @@ export function extractResourceRequestProperties({ kind: SharedResourceAccessRequestKind; id: string; /** - * Pretty name of the resource (can be the same as `id`). - * For example, for nodes, we want to show hostname instead of its id. + * Can refer to a pretty name of the resource (can be the same as `id`) + * or refer to a subresource name. + * + * For example: + * - for nodes, we want to show hostname (pretty) instead of its id. + * - for a kube subresource like "namespace", it'll refer to its name + * */ name: string; } { @@ -312,6 +366,12 @@ export function extractResourceRequestProperties({ const { kubeId } = routing.parseKubeUri(resource.uri).params; return { kind: 'kube_cluster', id: kubeId, name: kubeId }; } + case 'namespace': { + const { kubeNamespaceId, kubeId } = routing.parseKubeResourceNamespaceUri( + resource.uri + ).params; + return { kind, id: kubeId, name: kubeNamespaceId }; + } default: kind satisfies never; } @@ -395,6 +455,18 @@ export function toResourceRequest({ }, kind: 'kube', }; + case 'namespace': + return { + resource: { + uri: routing.getKubeResourceNamespaceUri({ + rootClusterId, + leafClusterId, + kubeId: resourceId, + kubeNamespaceId: resourceName, + }), + }, + kind, + }; default: kind satisfies never; }