diff --git a/api/client/client.go b/api/client/client.go index f7066fe730274..c7cd6cfcbf8f7 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -46,6 +46,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" kubeproto "github.com/gravitational/teleport/api/gen/proto/go/teleport/kube/v1" @@ -69,6 +70,12 @@ func init() { } } +// AuthServiceClient keeps the interfaces implemented by the auth service. +type AuthServiceClient struct { + proto.AuthServiceClient + assist.AssistServiceClient +} + // Client is a gRPC Client that connects to a Teleport Auth server either // locally or over ssh through a Teleport web proxy or tunnel proxy. // @@ -85,7 +92,7 @@ type Client struct { // conn is a grpc connection to the auth server. conn *grpc.ClientConn // grpc is the gRPC client specification for the auth server. - grpc proto.AuthServiceClient + grpc AuthServiceClient // JoinServiceClient is a client for the JoinService, which runs on both the // auth and proxy. *JoinServiceClient @@ -432,7 +439,10 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error { } c.conn = conn - c.grpc = proto.NewAuthServiceClient(c.conn) + c.grpc = AuthServiceClient{ + AuthServiceClient: proto.NewAuthServiceClient(c.conn), + AssistServiceClient: assist.NewAssistServiceClient(c.conn), + } c.JoinServiceClient = NewJoinServiceClient(proto.NewJoinServiceClient(c.conn)) return nil @@ -3528,3 +3538,49 @@ func (c *Client) GetHeadlessAuthentication(ctx context.Context, id string) (*typ func (c *Client) PluginsClient() pluginspb.PluginServiceClient { return pluginspb.NewPluginServiceClient(c.conn) } + +// CreateAssistantConversation creates a new conversation entry in the backend. +func (c *Client) CreateAssistantConversation(ctx context.Context, req *assist.CreateAssistantConversationRequest) (*assist.CreateAssistantConversationResponse, error) { + resp, err := c.grpc.CreateAssistantConversation(ctx, req) + if err != nil { + return nil, trail.FromGRPC(err) + } + + return resp, nil +} + +// GetAssistantMessages retrieves assistant messages with given conversation ID. +func (c *Client) GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) { + messages, err := c.grpc.GetAssistantMessages(ctx, req) + if err != nil { + return nil, trail.FromGRPC(err) + } + return messages, nil +} + +// GetAssistantConversations returns all conversations started by a user. +func (c *Client) GetAssistantConversations(ctx context.Context, request *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) { + messages, err := c.grpc.GetAssistantConversations(ctx, request) + if err != nil { + return nil, trail.FromGRPC(err) + } + return messages, nil +} + +// CreateAssistantMessage saves a new conversation message. +func (c *Client) CreateAssistantMessage(ctx context.Context, in *assist.CreateAssistantMessageRequest) error { + _, err := c.grpc.CreateAssistantMessage(ctx, in) + if err != nil { + return trail.FromGRPC(err) + } + return nil +} + +// UpdateAssistantConversationInfo updates conversation info. +func (c *Client) UpdateAssistantConversationInfo(ctx context.Context, in *assist.UpdateAssistantConversationInfoRequest) error { + _, err := c.grpc.UpdateAssistantConversationInfo(ctx, in) + if err != nil { + return trail.FromGRPC(err) + } + return nil +} diff --git a/api/gen/proto/go/assist/v1/assist.pb.go b/api/gen/proto/go/assist/v1/assist.pb.go new file mode 100644 index 0000000000000..0ced27354494c --- /dev/null +++ b/api/gen/proto/go/assist/v1/assist.pb.go @@ -0,0 +1,945 @@ +// Copyright 2023 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc (unknown) +// source: teleport/assist/v1/assist.proto + +package assist + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// GetAssistantMessagesRequest is a request to the assistant service. +type GetAssistantMessagesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ConversationId identifies a conversation. + // It's used to tie all messages in a one conversation. + ConversationId string `protobuf:"bytes,1,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` + // username is a username of the user who sent the message. + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` +} + +func (x *GetAssistantMessagesRequest) Reset() { + *x = GetAssistantMessagesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAssistantMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAssistantMessagesRequest) ProtoMessage() {} + +func (x *GetAssistantMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[0] + 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 GetAssistantMessagesRequest.ProtoReflect.Descriptor instead. +func (*GetAssistantMessagesRequest) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{0} +} + +func (x *GetAssistantMessagesRequest) GetConversationId() string { + if x != nil { + return x.ConversationId + } + return "" +} + +func (x *GetAssistantMessagesRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +// AssistantMessage is a message sent to the assistant service. The conversation +// must be created first. +type AssistantMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // type is a type of message. It can be Chat response/query or a command to run. + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // CreatedTime is the time when the event occurred. + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` + // payload is a JSON message + Payload string `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *AssistantMessage) Reset() { + *x = AssistantMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssistantMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssistantMessage) ProtoMessage() {} + +func (x *AssistantMessage) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssistantMessage.ProtoReflect.Descriptor instead. +func (*AssistantMessage) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{1} +} + +func (x *AssistantMessage) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *AssistantMessage) GetCreatedTime() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +func (x *AssistantMessage) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +// CreateAssistantMessageRequest is a request to the assistant service. +type CreateAssistantMessageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // message is a message sent to the assistant service. + Message *AssistantMessage `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + // ConversationId is used to tie all messages into a conversation. + ConversationId string `protobuf:"bytes,2,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` + // username is a username of the user who sent the message. + Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` +} + +func (x *CreateAssistantMessageRequest) Reset() { + *x = CreateAssistantMessageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateAssistantMessageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAssistantMessageRequest) ProtoMessage() {} + +func (x *CreateAssistantMessageRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[2] + 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 CreateAssistantMessageRequest.ProtoReflect.Descriptor instead. +func (*CreateAssistantMessageRequest) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateAssistantMessageRequest) GetMessage() *AssistantMessage { + if x != nil { + return x.Message + } + return nil +} + +func (x *CreateAssistantMessageRequest) GetConversationId() string { + if x != nil { + return x.ConversationId + } + return "" +} + +func (x *CreateAssistantMessageRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +// GetAssistantMessagesResponse is a response from the assistant service. +type GetAssistantMessagesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // messages is a list of messages. + Messages []*AssistantMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` +} + +func (x *GetAssistantMessagesResponse) Reset() { + *x = GetAssistantMessagesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAssistantMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAssistantMessagesResponse) ProtoMessage() {} + +func (x *GetAssistantMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[3] + 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 GetAssistantMessagesResponse.ProtoReflect.Descriptor instead. +func (*GetAssistantMessagesResponse) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{3} +} + +func (x *GetAssistantMessagesResponse) GetMessages() []*AssistantMessage { + if x != nil { + return x.Messages + } + return nil +} + +// GetAssistantConversationsRequest is a request to get a list of conversations. +type GetAssistantConversationsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // username is a username of the user who created the conversation. + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` +} + +func (x *GetAssistantConversationsRequest) Reset() { + *x = GetAssistantConversationsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAssistantConversationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAssistantConversationsRequest) ProtoMessage() {} + +func (x *GetAssistantConversationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[4] + 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 GetAssistantConversationsRequest.ProtoReflect.Descriptor instead. +func (*GetAssistantConversationsRequest) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{4} +} + +func (x *GetAssistantConversationsRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +// ConversationInfo is a conversation info. It contains a conversation +// information like ID, title, created time. +type ConversationInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // id is a unique conversation ID. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // title is a title of the conversation. + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + // createdTime is the time when the conversation was created. + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` +} + +func (x *ConversationInfo) Reset() { + *x = ConversationInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConversationInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConversationInfo) ProtoMessage() {} + +func (x *ConversationInfo) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[5] + 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 ConversationInfo.ProtoReflect.Descriptor instead. +func (*ConversationInfo) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{5} +} + +func (x *ConversationInfo) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ConversationInfo) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *ConversationInfo) GetCreatedTime() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +// GetAssistantConversationsResponse is a response from the assistant service. +type GetAssistantConversationsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // conversations is a list of conversations. + Conversations []*ConversationInfo `protobuf:"bytes,1,rep,name=conversations,proto3" json:"conversations,omitempty"` +} + +func (x *GetAssistantConversationsResponse) Reset() { + *x = GetAssistantConversationsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAssistantConversationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAssistantConversationsResponse) ProtoMessage() {} + +func (x *GetAssistantConversationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[6] + 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 GetAssistantConversationsResponse.ProtoReflect.Descriptor instead. +func (*GetAssistantConversationsResponse) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{6} +} + +func (x *GetAssistantConversationsResponse) GetConversations() []*ConversationInfo { + if x != nil { + return x.Conversations + } + return nil +} + +// CreateAssistantConversationRequest is a request to create a new conversation. +type CreateAssistantConversationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // username is a username of the user who created the conversation. + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // createdTime is the time when the conversation was created. + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` +} + +func (x *CreateAssistantConversationRequest) Reset() { + *x = CreateAssistantConversationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateAssistantConversationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAssistantConversationRequest) ProtoMessage() {} + +func (x *CreateAssistantConversationRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[7] + 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 CreateAssistantConversationRequest.ProtoReflect.Descriptor instead. +func (*CreateAssistantConversationRequest) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{7} +} + +func (x *CreateAssistantConversationRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *CreateAssistantConversationRequest) GetCreatedTime() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +// CreateAssistantConversationResponse is a response from the assistant service. +type CreateAssistantConversationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // id is a unique conversation ID. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *CreateAssistantConversationResponse) Reset() { + *x = CreateAssistantConversationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateAssistantConversationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAssistantConversationResponse) ProtoMessage() {} + +func (x *CreateAssistantConversationResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[8] + 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 CreateAssistantConversationResponse.ProtoReflect.Descriptor instead. +func (*CreateAssistantConversationResponse) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{8} +} + +func (x *CreateAssistantConversationResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// UpdateAssistantConversationInfoRequest is a request to update the conversation info. +type UpdateAssistantConversationInfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // conversationId is a unique conversation ID. + ConversationId string `protobuf:"bytes,1,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` + // username is a username of the user who created the conversation. + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + // title is a title of the conversation. + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` +} + +func (x *UpdateAssistantConversationInfoRequest) Reset() { + *x = UpdateAssistantConversationInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateAssistantConversationInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAssistantConversationInfoRequest) ProtoMessage() {} + +func (x *UpdateAssistantConversationInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_assist_v1_assist_proto_msgTypes[9] + 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 UpdateAssistantConversationInfoRequest.ProtoReflect.Descriptor instead. +func (*UpdateAssistantConversationInfoRequest) Descriptor() ([]byte, []int) { + return file_teleport_assist_v1_assist_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateAssistantConversationInfoRequest) GetConversationId() string { + if x != nil { + return x.ConversationId + } + return "" +} + +func (x *UpdateAssistantConversationInfoRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *UpdateAssistantConversationInfoRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +var File_teleport_assist_v1_assist_proto protoreflect.FileDescriptor + +var file_teleport_assist_v1_assist_proto_rawDesc = []byte{ + 0x0a, 0x1f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x73, 0x73, 0x69, 0x73, + 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x12, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x62, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7f, 0x0a, 0x10, 0x41, 0x73, 0x73, 0x69, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x3d, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 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, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xa4, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x60, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, + 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x73, 0x22, 0x3e, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0x77, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 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, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x6f, 0x0a, 0x21, 0x47, 0x65, + 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4a, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x7f, 0x0a, 0x22, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 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, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, 0x23, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x26, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x73, + 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, + 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x32, 0x82, 0x05, 0x0a, 0x0d, 0x41, 0x73, + 0x73, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x1b, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, + 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, + 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x88, 0x01, 0x0a, + 0x19, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x73, + 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x63, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x75, 0x0a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x45, + 0x5a, 0x43, 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, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x61, 0x73, 0x73, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x3b, 0x61, + 0x73, 0x73, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_teleport_assist_v1_assist_proto_rawDescOnce sync.Once + file_teleport_assist_v1_assist_proto_rawDescData = file_teleport_assist_v1_assist_proto_rawDesc +) + +func file_teleport_assist_v1_assist_proto_rawDescGZIP() []byte { + file_teleport_assist_v1_assist_proto_rawDescOnce.Do(func() { + file_teleport_assist_v1_assist_proto_rawDescData = protoimpl.X.CompressGZIP(file_teleport_assist_v1_assist_proto_rawDescData) + }) + return file_teleport_assist_v1_assist_proto_rawDescData +} + +var file_teleport_assist_v1_assist_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_teleport_assist_v1_assist_proto_goTypes = []interface{}{ + (*GetAssistantMessagesRequest)(nil), // 0: teleport.assist.v1.GetAssistantMessagesRequest + (*AssistantMessage)(nil), // 1: teleport.assist.v1.AssistantMessage + (*CreateAssistantMessageRequest)(nil), // 2: teleport.assist.v1.CreateAssistantMessageRequest + (*GetAssistantMessagesResponse)(nil), // 3: teleport.assist.v1.GetAssistantMessagesResponse + (*GetAssistantConversationsRequest)(nil), // 4: teleport.assist.v1.GetAssistantConversationsRequest + (*ConversationInfo)(nil), // 5: teleport.assist.v1.ConversationInfo + (*GetAssistantConversationsResponse)(nil), // 6: teleport.assist.v1.GetAssistantConversationsResponse + (*CreateAssistantConversationRequest)(nil), // 7: teleport.assist.v1.CreateAssistantConversationRequest + (*CreateAssistantConversationResponse)(nil), // 8: teleport.assist.v1.CreateAssistantConversationResponse + (*UpdateAssistantConversationInfoRequest)(nil), // 9: teleport.assist.v1.UpdateAssistantConversationInfoRequest + (*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp + (*emptypb.Empty)(nil), // 11: google.protobuf.Empty +} +var file_teleport_assist_v1_assist_proto_depIdxs = []int32{ + 10, // 0: teleport.assist.v1.AssistantMessage.created_time:type_name -> google.protobuf.Timestamp + 1, // 1: teleport.assist.v1.CreateAssistantMessageRequest.message:type_name -> teleport.assist.v1.AssistantMessage + 1, // 2: teleport.assist.v1.GetAssistantMessagesResponse.messages:type_name -> teleport.assist.v1.AssistantMessage + 10, // 3: teleport.assist.v1.ConversationInfo.created_time:type_name -> google.protobuf.Timestamp + 5, // 4: teleport.assist.v1.GetAssistantConversationsResponse.conversations:type_name -> teleport.assist.v1.ConversationInfo + 10, // 5: teleport.assist.v1.CreateAssistantConversationRequest.created_time:type_name -> google.protobuf.Timestamp + 7, // 6: teleport.assist.v1.AssistService.CreateAssistantConversation:input_type -> teleport.assist.v1.CreateAssistantConversationRequest + 4, // 7: teleport.assist.v1.AssistService.GetAssistantConversations:input_type -> teleport.assist.v1.GetAssistantConversationsRequest + 0, // 8: teleport.assist.v1.AssistService.GetAssistantMessages:input_type -> teleport.assist.v1.GetAssistantMessagesRequest + 2, // 9: teleport.assist.v1.AssistService.CreateAssistantMessage:input_type -> teleport.assist.v1.CreateAssistantMessageRequest + 9, // 10: teleport.assist.v1.AssistService.UpdateAssistantConversationInfo:input_type -> teleport.assist.v1.UpdateAssistantConversationInfoRequest + 8, // 11: teleport.assist.v1.AssistService.CreateAssistantConversation:output_type -> teleport.assist.v1.CreateAssistantConversationResponse + 6, // 12: teleport.assist.v1.AssistService.GetAssistantConversations:output_type -> teleport.assist.v1.GetAssistantConversationsResponse + 3, // 13: teleport.assist.v1.AssistService.GetAssistantMessages:output_type -> teleport.assist.v1.GetAssistantMessagesResponse + 11, // 14: teleport.assist.v1.AssistService.CreateAssistantMessage:output_type -> google.protobuf.Empty + 11, // 15: teleport.assist.v1.AssistService.UpdateAssistantConversationInfo:output_type -> google.protobuf.Empty + 11, // [11:16] is the sub-list for method output_type + 6, // [6:11] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_teleport_assist_v1_assist_proto_init() } +func file_teleport_assist_v1_assist_proto_init() { + if File_teleport_assist_v1_assist_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_teleport_assist_v1_assist_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAssistantMessagesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AssistantMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateAssistantMessageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAssistantMessagesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAssistantConversationsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConversationInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAssistantConversationsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateAssistantConversationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateAssistantConversationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_assist_v1_assist_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateAssistantConversationInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_teleport_assist_v1_assist_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_teleport_assist_v1_assist_proto_goTypes, + DependencyIndexes: file_teleport_assist_v1_assist_proto_depIdxs, + MessageInfos: file_teleport_assist_v1_assist_proto_msgTypes, + }.Build() + File_teleport_assist_v1_assist_proto = out.File + file_teleport_assist_v1_assist_proto_rawDesc = nil + file_teleport_assist_v1_assist_proto_goTypes = nil + file_teleport_assist_v1_assist_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/assist/v1/assist_grpc.pb.go b/api/gen/proto/go/assist/v1/assist_grpc.pb.go new file mode 100644 index 0000000000000..8fe439e09400c --- /dev/null +++ b/api/gen/proto/go/assist/v1/assist_grpc.pb.go @@ -0,0 +1,282 @@ +// Copyright 2023 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: teleport/assist/v1/assist.proto + +package assist + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + AssistService_CreateAssistantConversation_FullMethodName = "/teleport.assist.v1.AssistService/CreateAssistantConversation" + AssistService_GetAssistantConversations_FullMethodName = "/teleport.assist.v1.AssistService/GetAssistantConversations" + AssistService_GetAssistantMessages_FullMethodName = "/teleport.assist.v1.AssistService/GetAssistantMessages" + AssistService_CreateAssistantMessage_FullMethodName = "/teleport.assist.v1.AssistService/CreateAssistantMessage" + AssistService_UpdateAssistantConversationInfo_FullMethodName = "/teleport.assist.v1.AssistService/UpdateAssistantConversationInfo" +) + +// AssistServiceClient is the client API for AssistService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type AssistServiceClient interface { + // CreateNewConversation creates a new conversation and returns the UUID of it. + CreateAssistantConversation(ctx context.Context, in *CreateAssistantConversationRequest, opts ...grpc.CallOption) (*CreateAssistantConversationResponse, error) + // GetAssistantConversations returns all conversations for the connected user. + GetAssistantConversations(ctx context.Context, in *GetAssistantConversationsRequest, opts ...grpc.CallOption) (*GetAssistantConversationsResponse, error) + // GetAssistantMessages returns all messages associated with the given conversation ID. + GetAssistantMessages(ctx context.Context, in *GetAssistantMessagesRequest, opts ...grpc.CallOption) (*GetAssistantMessagesResponse, error) + // CreateAssistantMessage creates a new message in the given conversation. + CreateAssistantMessage(ctx context.Context, in *CreateAssistantMessageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // UpdateAssistantConversationInfo updates the conversation info. + UpdateAssistantConversationInfo(ctx context.Context, in *UpdateAssistantConversationInfoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type assistServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAssistServiceClient(cc grpc.ClientConnInterface) AssistServiceClient { + return &assistServiceClient{cc} +} + +func (c *assistServiceClient) CreateAssistantConversation(ctx context.Context, in *CreateAssistantConversationRequest, opts ...grpc.CallOption) (*CreateAssistantConversationResponse, error) { + out := new(CreateAssistantConversationResponse) + err := c.cc.Invoke(ctx, AssistService_CreateAssistantConversation_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *assistServiceClient) GetAssistantConversations(ctx context.Context, in *GetAssistantConversationsRequest, opts ...grpc.CallOption) (*GetAssistantConversationsResponse, error) { + out := new(GetAssistantConversationsResponse) + err := c.cc.Invoke(ctx, AssistService_GetAssistantConversations_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *assistServiceClient) GetAssistantMessages(ctx context.Context, in *GetAssistantMessagesRequest, opts ...grpc.CallOption) (*GetAssistantMessagesResponse, error) { + out := new(GetAssistantMessagesResponse) + err := c.cc.Invoke(ctx, AssistService_GetAssistantMessages_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *assistServiceClient) CreateAssistantMessage(ctx context.Context, in *CreateAssistantMessageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, AssistService_CreateAssistantMessage_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *assistServiceClient) UpdateAssistantConversationInfo(ctx context.Context, in *UpdateAssistantConversationInfoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, AssistService_UpdateAssistantConversationInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AssistServiceServer is the server API for AssistService service. +// All implementations must embed UnimplementedAssistServiceServer +// for forward compatibility +type AssistServiceServer interface { + // CreateNewConversation creates a new conversation and returns the UUID of it. + CreateAssistantConversation(context.Context, *CreateAssistantConversationRequest) (*CreateAssistantConversationResponse, error) + // GetAssistantConversations returns all conversations for the connected user. + GetAssistantConversations(context.Context, *GetAssistantConversationsRequest) (*GetAssistantConversationsResponse, error) + // GetAssistantMessages returns all messages associated with the given conversation ID. + GetAssistantMessages(context.Context, *GetAssistantMessagesRequest) (*GetAssistantMessagesResponse, error) + // CreateAssistantMessage creates a new message in the given conversation. + CreateAssistantMessage(context.Context, *CreateAssistantMessageRequest) (*emptypb.Empty, error) + // UpdateAssistantConversationInfo updates the conversation info. + UpdateAssistantConversationInfo(context.Context, *UpdateAssistantConversationInfoRequest) (*emptypb.Empty, error) + mustEmbedUnimplementedAssistServiceServer() +} + +// UnimplementedAssistServiceServer must be embedded to have forward compatible implementations. +type UnimplementedAssistServiceServer struct { +} + +func (UnimplementedAssistServiceServer) CreateAssistantConversation(context.Context, *CreateAssistantConversationRequest) (*CreateAssistantConversationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAssistantConversation not implemented") +} +func (UnimplementedAssistServiceServer) GetAssistantConversations(context.Context, *GetAssistantConversationsRequest) (*GetAssistantConversationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAssistantConversations not implemented") +} +func (UnimplementedAssistServiceServer) GetAssistantMessages(context.Context, *GetAssistantMessagesRequest) (*GetAssistantMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAssistantMessages not implemented") +} +func (UnimplementedAssistServiceServer) CreateAssistantMessage(context.Context, *CreateAssistantMessageRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAssistantMessage not implemented") +} +func (UnimplementedAssistServiceServer) UpdateAssistantConversationInfo(context.Context, *UpdateAssistantConversationInfoRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateAssistantConversationInfo not implemented") +} +func (UnimplementedAssistServiceServer) mustEmbedUnimplementedAssistServiceServer() {} + +// UnsafeAssistServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AssistServiceServer will +// result in compilation errors. +type UnsafeAssistServiceServer interface { + mustEmbedUnimplementedAssistServiceServer() +} + +func RegisterAssistServiceServer(s grpc.ServiceRegistrar, srv AssistServiceServer) { + s.RegisterService(&AssistService_ServiceDesc, srv) +} + +func _AssistService_CreateAssistantConversation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAssistantConversationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssistServiceServer).CreateAssistantConversation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AssistService_CreateAssistantConversation_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssistServiceServer).CreateAssistantConversation(ctx, req.(*CreateAssistantConversationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AssistService_GetAssistantConversations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAssistantConversationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssistServiceServer).GetAssistantConversations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AssistService_GetAssistantConversations_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssistServiceServer).GetAssistantConversations(ctx, req.(*GetAssistantConversationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AssistService_GetAssistantMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAssistantMessagesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssistServiceServer).GetAssistantMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AssistService_GetAssistantMessages_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssistServiceServer).GetAssistantMessages(ctx, req.(*GetAssistantMessagesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AssistService_CreateAssistantMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAssistantMessageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssistServiceServer).CreateAssistantMessage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AssistService_CreateAssistantMessage_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssistServiceServer).CreateAssistantMessage(ctx, req.(*CreateAssistantMessageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AssistService_UpdateAssistantConversationInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAssistantConversationInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssistServiceServer).UpdateAssistantConversationInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AssistService_UpdateAssistantConversationInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssistServiceServer).UpdateAssistantConversationInfo(ctx, req.(*UpdateAssistantConversationInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AssistService_ServiceDesc is the grpc.ServiceDesc for AssistService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AssistService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "teleport.assist.v1.AssistService", + HandlerType: (*AssistServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateAssistantConversation", + Handler: _AssistService_CreateAssistantConversation_Handler, + }, + { + MethodName: "GetAssistantConversations", + Handler: _AssistService_GetAssistantConversations_Handler, + }, + { + MethodName: "GetAssistantMessages", + Handler: _AssistService_GetAssistantMessages_Handler, + }, + { + MethodName: "CreateAssistantMessage", + Handler: _AssistService_CreateAssistantMessage_Handler, + }, + { + MethodName: "UpdateAssistantConversationInfo", + Handler: _AssistService_UpdateAssistantConversationInfo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "teleport/assist/v1/assist.proto", +} diff --git a/api/proto/teleport/assist/v1/assist.proto b/api/proto/teleport/assist/v1/assist.proto new file mode 100644 index 0000000000000..b5ee8f381b93c --- /dev/null +++ b/api/proto/teleport/assist/v1/assist.proto @@ -0,0 +1,123 @@ +// Copyright 2023 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package teleport.assist.v1; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/gravitational/teleport/api/gen/proto/go/assist/v1;assist"; + +// GetAssistantMessagesRequest is a request to the assistant service. +message GetAssistantMessagesRequest { + // ConversationId identifies a conversation. + // It's used to tie all messages in a one conversation. + string conversation_id = 1; + // username is a username of the user who sent the message. + string username = 2; +} + +// AssistantMessage is a message sent to the assistant service. The conversation +// must be created first. +message AssistantMessage { + // type is a type of message. It can be Chat response/query or a command to run. + string type = 1; + // CreatedTime is the time when the event occurred. + google.protobuf.Timestamp created_time = 2; + // payload is a JSON message + string payload = 3; +} + +// CreateAssistantMessageRequest is a request to the assistant service. +message CreateAssistantMessageRequest { + // message is a message sent to the assistant service. + AssistantMessage message = 1; + // ConversationId is used to tie all messages into a conversation. + string conversation_id = 2; + // username is a username of the user who sent the message. + string username = 3; +} + +// GetAssistantMessagesResponse is a response from the assistant service. +message GetAssistantMessagesResponse { + // messages is a list of messages. + repeated AssistantMessage messages = 1; +} + +// GetAssistantConversationsRequest is a request to get a list of conversations. +message GetAssistantConversationsRequest { + // username is a username of the user who created the conversation. + string username = 1; +} + +// ConversationInfo is a conversation info. It contains a conversation +// information like ID, title, created time. +message ConversationInfo { + // id is a unique conversation ID. + string id = 1; + // title is a title of the conversation. + string title = 2; + // createdTime is the time when the conversation was created. + google.protobuf.Timestamp created_time = 3; +} + +// GetAssistantConversationsResponse is a response from the assistant service. +message GetAssistantConversationsResponse { + // conversations is a list of conversations. + repeated ConversationInfo conversations = 1; +} + +// CreateAssistantConversationRequest is a request to create a new conversation. +message CreateAssistantConversationRequest { + // username is a username of the user who created the conversation. + string username = 1; + // createdTime is the time when the conversation was created. + google.protobuf.Timestamp created_time = 2; +} + +// CreateAssistantConversationResponse is a response from the assistant service. +message CreateAssistantConversationResponse { + // id is a unique conversation ID. + string id = 1; +} + +// UpdateAssistantConversationInfoRequest is a request to update the conversation info. +message UpdateAssistantConversationInfoRequest { + // conversationId is a unique conversation ID. + string conversation_id = 1; + // username is a username of the user who created the conversation. + string username = 2; + // title is a title of the conversation. + string title = 3; +} + +// AssistService is a service that provides an ability to communicate with the Teleport Assist. +service AssistService { + // CreateNewConversation creates a new conversation and returns the UUID of it. + rpc CreateAssistantConversation(CreateAssistantConversationRequest) returns (CreateAssistantConversationResponse); + + // GetAssistantConversations returns all conversations for the connected user. + rpc GetAssistantConversations(GetAssistantConversationsRequest) returns (GetAssistantConversationsResponse); + + // GetAssistantMessages returns all messages associated with the given conversation ID. + rpc GetAssistantMessages(GetAssistantMessagesRequest) returns (GetAssistantMessagesResponse); + + // CreateAssistantMessage creates a new message in the given conversation. + rpc CreateAssistantMessage(CreateAssistantMessageRequest) returns (google.protobuf.Empty); + + // UpdateAssistantConversationInfo updates the conversation info. + rpc UpdateAssistantConversationInfo(UpdateAssistantConversationInfoRequest) returns (google.protobuf.Empty); +} diff --git a/api/types/constants.go b/api/types/constants.go index 33f8eb23d50b9..c47cc8641d96f 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -335,6 +335,10 @@ const ( // KindHeadlessAuthentication is a headless authentication resource. KindHeadlessAuthentication = "headless_authentication" + // KindAssistant is used to program RBAC for + // Teleport Assist resources. + KindAssistant = "assistant" + // KindIntegration is a connection to a 3rd party system API. KindIntegration = "integration" diff --git a/lib/auth/assist/assistv1/service.go b/lib/auth/assist/assistv1/service.go new file mode 100644 index 0000000000000..5e863c4ab03c5 --- /dev/null +++ b/lib/auth/assist/assistv1/service.go @@ -0,0 +1,87 @@ +/* + * + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package assistv1 + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" + "github.com/gravitational/teleport/lib/services" +) + +// ServiceConfig holds configuration options for +// the assist gRPC service. +type ServiceConfig struct { + Backend services.Assistant +} + +// Service implements the teleport.assist.v1.AssistService RPC service. +type Service struct { + assist.UnimplementedAssistServiceServer + + backend services.Assistant +} + +// NewService returns a new assist gRPC service. +func NewService(cfg *ServiceConfig) (*Service, error) { + switch { + case cfg.Backend == nil: + return nil, trace.BadParameter("backend is required") + } + + return &Service{ + backend: cfg.Backend, + }, nil +} + +// CreateAssistantConversation creates a new conversation entry in the backend. +func (a *Service) CreateAssistantConversation(ctx context.Context, req *assist.CreateAssistantConversationRequest) (*assist.CreateAssistantConversationResponse, error) { + resp, err := a.backend.CreateAssistantConversation(ctx, req) + return resp, trace.Wrap(err) +} + +// UpdateAssistantConversationInfo updates the conversation info for a conversation. +func (a *Service) UpdateAssistantConversationInfo(ctx context.Context, request *assist.UpdateAssistantConversationInfoRequest) (*emptypb.Empty, error) { + err := a.backend.UpdateAssistantConversationInfo(ctx, request) + if err != nil { + return &emptypb.Empty{}, trace.Wrap(err) + } + + return &emptypb.Empty{}, nil +} + +// GetAssistantConversations returns all conversations started by a user. +func (a *Service) GetAssistantConversations(ctx context.Context, req *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) { + resp, err := a.backend.GetAssistantConversations(ctx, req) + return resp, trace.Wrap(err) +} + +// GetAssistantMessages returns all messages with given conversation ID. +func (a *Service) GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) { + resp, err := a.backend.GetAssistantMessages(ctx, req) + return resp, trace.Wrap(err) +} + +// CreateAssistantMessage adds the message to the backend. +func (a *Service) CreateAssistantMessage(ctx context.Context, req *assist.CreateAssistantMessageRequest) (*emptypb.Empty, error) { + return &emptypb.Empty{}, trace.Wrap(a.backend.CreateAssistantMessage(ctx, req)) +} diff --git a/lib/auth/auth.go b/lib/auth/auth.go index 278abf5bebdcc..05582653feda4 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -57,6 +57,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" @@ -167,6 +168,9 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { if cfg.Status == nil { cfg.Status = local.NewStatusService(cfg.Backend) } + if cfg.Assist == nil { + cfg.Assist = local.NewAssistService(cfg.Backend) + } if cfg.Events == nil { cfg.Events = local.NewEventsService(cfg.Backend) } @@ -276,6 +280,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { Okta: cfg.Okta, StatusInternal: cfg.Status, UsageReporter: cfg.UsageReporter, + Assistant: cfg.Assist, } closeCtx, cancelFunc := context.WithCancel(context.TODO()) @@ -368,6 +373,7 @@ type Services struct { services.StatusInternal services.Integrations services.Okta + services.Assistant usagereporter.UsageReporter types.Events events.IAuditLog @@ -4914,6 +4920,34 @@ func (a *Server) GetHeadlessAuthentication(ctx context.Context, name string) (*t return headlessAuthn, trace.Wrap(err) } +// GetAssistantMessages returns all messages with given conversation ID. +func (a *Server) GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) { + resp, err := a.Services.GetAssistantMessages(ctx, req) + return resp, trace.Wrap(err) +} + +// CreateAssistantMessage adds the message to the backend. +func (a *Server) CreateAssistantMessage(ctx context.Context, msg *assist.CreateAssistantMessageRequest) error { + return trace.Wrap(a.Services.CreateAssistantMessage(ctx, msg)) +} + +// UpdateAssistantConversationInfo stores the given conversation title in the backend. +func (a *Server) UpdateAssistantConversationInfo(ctx context.Context, msg *assist.UpdateAssistantConversationInfoRequest) error { + return trace.Wrap(a.Services.UpdateAssistantConversationInfo(ctx, msg)) +} + +// CreateAssistantConversation creates a new conversation entry in the backend. +func (a *Server) CreateAssistantConversation(ctx context.Context, req *assist.CreateAssistantConversationRequest) (*assist.CreateAssistantConversationResponse, error) { + resp, err := a.Services.CreateAssistantConversation(ctx, req) + return resp, trace.Wrap(err) +} + +// GetAssistantConversations returns all conversations started by a user. +func (a *Server) GetAssistantConversations(ctx context.Context, request *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) { + resp, err := a.Services.GetAssistantConversations(ctx, request) + return resp, trace.Wrap(err) +} + // CompareAndSwapHeadlessAuthentication performs a compare // and swap replacement on a headless authentication resource. func (a *Server) CompareAndSwapHeadlessAuthentication(ctx context.Context, old, new *types.HeadlessAuthentication) (*types.HeadlessAuthentication, error) { diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 0259f317b458d..8c3fdcf1cb037 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" @@ -6056,6 +6057,51 @@ func (a *ServerWithRoles) UpdateHeadlessAuthenticationState(ctx context.Context, return trace.Wrap(err) } +// CreateAssistantConversation creates a new conversation entry in the backend. +func (a *ServerWithRoles) CreateAssistantConversation(ctx context.Context, req *assist.CreateAssistantConversationRequest) (*assist.CreateAssistantConversationResponse, error) { + if err := a.action(apidefaults.Namespace, types.KindAssistant, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + + return a.authServer.CreateAssistantConversation(ctx, req) +} + +// GetAssistantConversations returns all conversations started by a user. +func (a *ServerWithRoles) GetAssistantConversations(ctx context.Context, request *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) { + if err := a.action(apidefaults.Namespace, types.KindAssistant, types.VerbList); err != nil { + return nil, trace.Wrap(err) + } + + return a.authServer.GetAssistantConversations(ctx, request) +} + +// GetAssistantMessages returns all messages with given conversation ID. +func (a *ServerWithRoles) GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) { + if err := a.action(apidefaults.Namespace, types.KindAssistant, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + + return a.authServer.GetAssistantMessages(ctx, req) +} + +// CreateAssistantMessage adds the message to the backend. +func (a *ServerWithRoles) CreateAssistantMessage(ctx context.Context, msg *assist.CreateAssistantMessageRequest) error { + if err := a.action(apidefaults.Namespace, types.KindAssistant, types.VerbUpdate); err != nil { + return trace.Wrap(err) + } + + return a.authServer.CreateAssistantMessage(ctx, msg) +} + +// UpdateAssistantConversationInfo updates the conversation info. +func (a *ServerWithRoles) UpdateAssistantConversationInfo(ctx context.Context, msg *assist.UpdateAssistantConversationInfoRequest) error { + if err := a.action(apidefaults.Namespace, types.KindAssistant, types.VerbUpdate); err != nil { + return trace.Wrap(err) + } + + return a.authServer.UpdateAssistantConversationInfo(ctx, msg) +} + // CloneHTTPClient creates a new HTTP client with the same configuration. func (a *ServerWithRoles) CloneHTTPClient(params ...roundtrip.ClientParam) (*HTTPClient, error) { return nil, trace.NotImplemented("not implemented") diff --git a/lib/auth/clt.go b/lib/auth/clt.go index c9da4a0179f28..fcebb05174079 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -626,6 +626,7 @@ type ClientI interface { services.WindowsDesktops services.SAMLIdPServiceProviders services.UserGroups + services.Assistant WebService services.Status services.ClusterConfiguration diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 09da37c0d1c34..edb45ca906cf2 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -44,6 +44,7 @@ import ( "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" oktapb "github.com/gravitational/teleport/api/gen/proto/go/teleport/okta/v1" "github.com/gravitational/teleport/api/metadata" @@ -51,6 +52,7 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/installers" "github.com/gravitational/teleport/api/types/wrappers" + "github.com/gravitational/teleport/lib/auth/assist/assistv1" integrationService "github.com/gravitational/teleport/lib/auth/integration/integrationv1" "github.com/gravitational/teleport/lib/auth/okta" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" @@ -5111,6 +5113,15 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { proto.RegisterAuthServiceServer(server, authServer) collectortracepb.RegisterTraceServiceServer(server, authServer) + // Initialize and register the assist service. + assistSrv, err := assistv1.NewService(&assistv1.ServiceConfig{ + Backend: cfg.AuthServer.Services, + }) + if err != nil { + return nil, trace.Wrap(err) + } + assist.RegisterAssistServiceServer(server, assistSrv) + // create server with no-op role to pass to JoinService server serverWithNopRole, err := serverWithNopRole(cfg) if err != nil { diff --git a/lib/auth/init.go b/lib/auth/init.go index fa1bb8b0f3c4e..2bd9a47534e92 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -142,6 +142,9 @@ type InitConfig struct { // Status is a service that manages cluster status info. Status services.StatusInternal + // Assist is a service that implements the Teleport Assist functionality. + Assist services.Assistant + // Roles is a set of roles to create Roles []types.Role diff --git a/lib/auth/init_test.go b/lib/auth/init_test.go index f755afab6ae0b..dd022a6c84792 100644 --- a/lib/auth/init_test.go +++ b/lib/auth/init_test.go @@ -621,8 +621,8 @@ func TestPresets(t *testing.T) { err := createPresets(ctx, roleManager) require.NoError(t, err) - require.Equal(t, 0, roleManager.upsertRoleCallsCount, "unexpectd call to UpsertRole") - require.Equal(t, 0, roleManager.getRoleCallsCount, "unexpectd call to GetRole") + require.Equal(t, 0, roleManager.upsertRoleCallsCount, "unexpected call to UpsertRole") + require.Equal(t, 0, roleManager.getRoleCallsCount, "unexpected call to GetRole") require.Equal(t, presetRoleCount, roleManager.createRoleCallsCount, "unexpected number of calls to CreateRole, got %d calls", roleManager.createRoleCallsCount) // Running a second time should return Already Exists, so it fetches the role. @@ -632,7 +632,7 @@ func TestPresets(t *testing.T) { err = createPresets(ctx, roleManager) require.NoError(t, err) - require.Equal(t, 0, roleManager.upsertRoleCallsCount, "unexpectd call to UpsertRole") + require.Equal(t, 0, roleManager.upsertRoleCallsCount, "unexpected call to UpsertRole") require.Equal(t, presetRoleCount, roleManager.getRoleCallsCount, "unexpected number of calls to CreateRole, got %d calls", roleManager.getRoleCallsCount) require.Equal(t, presetRoleCount, roleManager.createRoleCallsCount, "unexpected number of calls to CreateRole, got %d calls", roleManager.createRoleCallsCount) @@ -647,13 +647,14 @@ func TestPresets(t *testing.T) { allowRulesWithoutConnectionDiag = append(allowRulesWithoutConnectionDiag, r) } editorRole.SetRules(types.Allow, allowRulesWithoutConnectionDiag) - roleManager.UpsertRole(ctx, editorRole) + err = roleManager.UpsertRole(ctx, editorRole) + require.NoError(t, err) roleManager.ResetCallCounters() err = createPresets(ctx, roleManager) require.NoError(t, err) - require.Equal(t, 1, roleManager.upsertRoleCallsCount, "unexpectd call to UpsertRole") + require.Equal(t, 1, roleManager.upsertRoleCallsCount, "unexpected call to UpsertRole") require.Equal(t, presetRoleCount, roleManager.getRoleCallsCount, "unexpected number of calls to CreateRole, got %d calls", roleManager.getRoleCallsCount) require.Equal(t, presetRoleCount, roleManager.createRoleCallsCount, "unexpected number of calls to CreateRole, got %d calls", roleManager.createRoleCallsCount) }) diff --git a/lib/reversetunnel/agent.go b/lib/reversetunnel/agent.go index 89cf7944e5838..5a49c4bd9e4da 100644 --- a/lib/reversetunnel/agent.go +++ b/lib/reversetunnel/agent.go @@ -116,7 +116,7 @@ type agentConfig struct { // localAuthAddresses is a list of auth servers to use when dialing back to // the local cluster. localAuthAddresses []string - // PROXYSigner is used to sign PROXY headers for securely propagating client IP address + // proxySigner is used to sign PROXY headers for securely propagating client IP address proxySigner multiplexer.PROXYHeaderSigner } @@ -189,7 +189,7 @@ type agent struct { // drainWG tracks transports and other concurrent operations required // to drain a connection are finished. drainWG sync.WaitGroup - // PROXYSigner is used to sign PROXY headers for securely propagating client IP address + // proxySigner is used to sign PROXY headers for securely propagating client IP address proxySigner multiplexer.PROXYHeaderSigner } diff --git a/lib/services/assist.go b/lib/services/assist.go new file mode 100644 index 0000000000000..2475133fe4d1b --- /dev/null +++ b/lib/services/assist.go @@ -0,0 +1,42 @@ +/* + * + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package services + +import ( + "context" + + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" +) + +type Assistant interface { + // GetAssistantMessages returns all messages with given conversation ID. + GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) + + // CreateAssistantMessage adds the message to the backend. + CreateAssistantMessage(ctx context.Context, msg *assist.CreateAssistantMessageRequest) error + + // CreateAssistantConversation creates a new conversation entry in the backend. + CreateAssistantConversation(ctx context.Context, req *assist.CreateAssistantConversationRequest) (*assist.CreateAssistantConversationResponse, error) + + // GetAssistantConversations returns all conversations started by a user. + GetAssistantConversations(ctx context.Context, request *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) + + // UpdateAssistantConversationInfo updates conversation info. + UpdateAssistantConversationInfo(ctx context.Context, msg *assist.UpdateAssistantConversationInfoRequest) error +} diff --git a/lib/services/local/assistant.go b/lib/services/local/assistant.go new file mode 100644 index 0000000000000..f36ef975d964e --- /dev/null +++ b/lib/services/local/assistant.go @@ -0,0 +1,236 @@ +/* + * + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package local + +import ( + "context" + "encoding/json" + "sort" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" + "github.com/gravitational/teleport/lib/backend" +) + +// Conversation is a conversation entry in the backend. +type Conversation struct { + Title string `json:"title,omitempty"` + ConversationID string `json:"conversation_id"` + CreatedTime time.Time `json:"created_time"` +} + +// AssistService is responsible for managing assist conversations. +type AssistService struct { + backend.Backend + log logrus.FieldLogger +} + +// NewAssistService returns a new instance of AssistService. +func NewAssistService(backend backend.Backend) *AssistService { + return &AssistService{ + Backend: backend, + log: logrus.WithField(trace.Component, "assist"), + } +} + +// CreateAssistantConversation creates a new conversation entry in the backend. +func (s *AssistService) CreateAssistantConversation(ctx context.Context, + req *assist.CreateAssistantConversationRequest, +) (*assist.CreateAssistantConversationResponse, error) { + if req.Username == "" { + return nil, trace.BadParameter("missing parameter username") + } + if req.CreatedTime == nil { + return nil, trace.BadParameter("missing parameter created time") + } + + conversationID := uuid.New().String() + payload := &Conversation{ + ConversationID: conversationID, + CreatedTime: req.GetCreatedTime().AsTime(), + } + + value, err := json.Marshal(payload) + if err != nil { + return nil, trace.Wrap(err) + } + + item := backend.Item{ + Key: backend.Key(assistantConversationPrefix, req.Username, conversationID), + Value: value, + } + + _, err = s.Create(ctx, item) + if err != nil { + return nil, trace.Wrap(err) + } + + return &assist.CreateAssistantConversationResponse{Id: conversationID}, nil +} + +func (s *AssistService) getConversation(ctx context.Context, username, conversationID string) (*Conversation, error) { + item, err := s.Get(ctx, backend.Key(assistantConversationPrefix, username, conversationID)) + if err != nil { + return nil, trace.Wrap(err) + } + + var conversation Conversation + if err := json.Unmarshal(item.Value, &conversation); err != nil { + return nil, trace.Wrap(err) + } + + return &conversation, nil +} + +// UpdateAssistantConversationInfo updates the conversation title. +func (s *AssistService) UpdateAssistantConversationInfo(ctx context.Context, request *assist.UpdateAssistantConversationInfoRequest) error { + if request.ConversationId == "" { + return trace.BadParameter("missing conversation ID") + } + if request.Username == "" { + return trace.BadParameter("missing username") + } + if request.Title == "" { + return trace.BadParameter("missing title") + } + + msg, err := s.getConversation(ctx, request.Username, request.GetConversationId()) + if err != nil { + return trace.Wrap(err) + } + + // Only update the title, leave the rest of the fields intact. + msg.Title = request.Title + + payload, err := json.Marshal(msg) + if err != nil { + return trace.Wrap(err) + } + + item := backend.Item{ + Key: backend.Key(assistantConversationPrefix, request.GetUsername(), request.GetConversationId()), + Value: payload, + } + + if _, err = s.Update(ctx, item); err != nil { + return trace.Wrap(err) + } + + return nil +} + +// GetAssistantConversations returns all conversations started by a user. +func (s *AssistService) GetAssistantConversations(ctx context.Context, req *assist.GetAssistantConversationsRequest) (*assist.GetAssistantConversationsResponse, error) { + if req.Username == "" { + return nil, trace.BadParameter("missing username") + } + startKey := backend.Key(assistantConversationPrefix, req.Username) + result, err := s.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit) + if err != nil { + return nil, trace.Wrap(err) + } + + conversationsIDs := make([]*assist.ConversationInfo, 0, len(result.Items)) + for _, item := range result.Items { + payload := &Conversation{} + if err := json.Unmarshal(item.Value, payload); err != nil { + return nil, trace.Wrap(err) + } + + conversationsIDs = append(conversationsIDs, &assist.ConversationInfo{ + Id: payload.ConversationID, + Title: payload.Title, + CreatedTime: timestamppb.New(payload.CreatedTime), + }) + } + + sort.Slice(conversationsIDs, func(i, j int) bool { + return conversationsIDs[i].CreatedTime.AsTime().Before(conversationsIDs[j].GetCreatedTime().AsTime()) + }) + + return &assist.GetAssistantConversationsResponse{ + Conversations: conversationsIDs, + }, nil +} + +// GetAssistantMessages returns all messages with given conversation ID. +func (s *AssistService) GetAssistantMessages(ctx context.Context, req *assist.GetAssistantMessagesRequest) (*assist.GetAssistantMessagesResponse, error) { + if req.Username == "" { + return nil, trace.BadParameter("missing username") + } + + if req.ConversationId == "" { + return nil, trace.BadParameter("missing conversation ID") + } + + startKey := backend.Key(assistantMessagePrefix, req.Username, req.ConversationId) + result, err := s.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit) + if err != nil { + return nil, trace.Wrap(err) + } + + out := make([]*assist.AssistantMessage, len(result.Items)) + for i, item := range result.Items { + var a assist.AssistantMessage + if err := json.Unmarshal(item.Value, &a); err != nil { + return nil, trace.Wrap(err) + } + out[i] = &a + } + + sort.Slice(out, func(i, j int) bool { + // Sort by created time. + return out[i].CreatedTime.AsTime().Before(out[j].GetCreatedTime().AsTime()) + }) + + return &assist.GetAssistantMessagesResponse{ + Messages: out, + }, nil +} + +// CreateAssistantMessage adds the message to the backend. +func (s *AssistService) CreateAssistantMessage(ctx context.Context, req *assist.CreateAssistantMessageRequest) error { + if req.Username == "" { + return trace.BadParameter("missing username") + } + if req.ConversationId == "" { + return trace.BadParameter("missing conversation ID") + } + + msg := req.GetMessage() + value, err := json.Marshal(msg) + if err != nil { + return trace.Wrap(err) + } + + messageID := uuid.New().String() + + item := backend.Item{ + Key: backend.Key(assistantMessagePrefix, req.Username, req.ConversationId, messageID), + Value: value, + } + + _, err = s.Create(ctx, item) + return trace.Wrap(err) +} diff --git a/lib/services/local/assistant_test.go b/lib/services/local/assistant_test.go new file mode 100644 index 0000000000000..5a5853bd8ec93 --- /dev/null +++ b/lib/services/local/assistant_test.go @@ -0,0 +1,143 @@ +/* + * + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package local_test + +import ( + "context" + "testing" + "time" + + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services/local" +) + +func newAssistService(t *testing.T) *local.AssistService { + t.Helper() + backend, err := memory.New(memory.Config{ + Context: context.Background(), + Clock: clockwork.NewFakeClock(), + }) + require.NoError(t, err) + return local.NewAssistService(backend) +} + +// TestAssistantCRUD tests the assistant CRUD operations. +func TestAssistantCRUD(t *testing.T) { + t.Parallel() + + identity := newAssistService(t) + ctx := context.Background() + + const username = "foo" + var conversationID string + + t.Run("create conversation", func(t *testing.T) { + req := &assist.CreateAssistantConversationRequest{ + Username: username, + CreatedTime: timestamppb.New(time.Now()), + } + + conversationResp, err := identity.CreateAssistantConversation(ctx, req) + require.NoError(t, err) + require.NotEmpty(t, conversationResp.Id) + + conversationID = conversationResp.Id + }) + + t.Run("get conversation", func(t *testing.T) { + req := &assist.GetAssistantConversationsRequest{ + Username: username, + } + conversations, err := identity.GetAssistantConversations(ctx, req) + require.NoError(t, err) + require.Len(t, conversations.Conversations, 1) + require.Equal(t, conversationID, conversations.Conversations[0].Id) + }) + + t.Run("create message", func(t *testing.T) { + msg := &assist.CreateAssistantMessageRequest{ + Username: username, + ConversationId: conversationID, + Message: &assist.AssistantMessage{ + CreatedTime: timestamppb.New(time.Now()), + Payload: "foo", + Type: "USER_MSG", + }, + } + err := identity.CreateAssistantMessage(ctx, msg) + require.NoError(t, err) + }) + + t.Run("get messages", func(t *testing.T) { + req := &assist.GetAssistantMessagesRequest{ + Username: username, + ConversationId: conversationID, + } + messages, err := identity.GetAssistantMessages(ctx, req) + require.NoError(t, err) + require.Len(t, messages.Messages, 1) + require.Equal(t, "foo", messages.Messages[0].Payload) + }) + + t.Run("set conversation title", func(t *testing.T) { + titleReq := &assist.UpdateAssistantConversationInfoRequest{ + Title: "bar", + Username: username, + ConversationId: conversationID, + } + title := "bar" + err := identity.UpdateAssistantConversationInfo(ctx, titleReq) + require.NoError(t, err) + + req := &assist.GetAssistantConversationsRequest{ + Username: username, + } + + conversations, err := identity.GetAssistantConversations(ctx, req) + require.NoError(t, err) + require.Len(t, conversations.Conversations, 1) + require.Equal(t, title, conversations.Conversations[0].Title) + }) + + t.Run("conversations are sorted by created_time", func(t *testing.T) { + req := &assist.CreateAssistantConversationRequest{ + Username: username, + CreatedTime: timestamppb.New(time.Now().Add(time.Hour)), + } + + conversationResp, err := identity.CreateAssistantConversation(ctx, req) + require.NoError(t, err) + require.NotEmpty(t, conversationResp.Id) + + reqConversations := &assist.GetAssistantConversationsRequest{ + Username: username, + } + + conversations, err := identity.GetAssistantConversations(ctx, reqConversations) + require.NoError(t, err) + require.Len(t, conversations.Conversations, 2) + require.Equal(t, conversationID, conversations.Conversations[0].Id) + require.Equal(t, conversationResp.Id, conversations.Conversations[1].Id) + }) +} diff --git a/lib/services/local/users.go b/lib/services/local/users.go index 92fea76704710..3438fae309d4b 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -1517,25 +1517,27 @@ func KeyAttestationDataFingerprintV11(pub crypto.PublicKey) (string, error) { } const ( - webPrefix = "web" - usersPrefix = "users" - sessionsPrefix = "sessions" - attemptsPrefix = "attempts" - pwdPrefix = "pwd" - connectorsPrefix = "connectors" - oidcPrefix = "oidc" - samlPrefix = "saml" - githubPrefix = "github" - requestsPrefix = "requests" - requestsTracePrefix = "requestsTrace" - usedTOTPPrefix = "used_totp" - usedTOTPTTL = 30 * time.Second - mfaDevicePrefix = "mfa" - webauthnPrefix = "webauthn" - webauthnGlobalSessionData = "sessionData" - webauthnLocalAuthPrefix = "webauthnlocalauth" - webauthnSessionData = "webauthnsessiondata" - recoveryCodesPrefix = "recoverycodes" - recoveryAttemptsPrefix = "recoveryattempts" - attestationsPrefix = "key_attestations" + webPrefix = "web" + usersPrefix = "users" + sessionsPrefix = "sessions" + attemptsPrefix = "attempts" + pwdPrefix = "pwd" + connectorsPrefix = "connectors" + oidcPrefix = "oidc" + samlPrefix = "saml" + githubPrefix = "github" + requestsPrefix = "requests" + requestsTracePrefix = "requestsTrace" + usedTOTPPrefix = "used_totp" + usedTOTPTTL = 30 * time.Second + mfaDevicePrefix = "mfa" + webauthnPrefix = "webauthn" + webauthnGlobalSessionData = "sessionData" + webauthnLocalAuthPrefix = "webauthnlocalauth" + webauthnSessionData = "webauthnsessiondata" + recoveryCodesPrefix = "recoverycodes" + recoveryAttemptsPrefix = "recoveryattempts" + attestationsPrefix = "key_attestations" + assistantMessagePrefix = "assistant_messages" + assistantConversationPrefix = "assistant_conversations" ) diff --git a/lib/services/presets.go b/lib/services/presets.go index b2c6217419ade..16c363a8d44bc 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -83,6 +83,7 @@ func NewPresetEditorRole() types.Role { types.NewRule(types.KindUserGroup, RW()), types.NewRule(types.KindOktaImportRule, RW()), types.NewRule(types.KindOktaAssignment, RW()), + types.NewRule(types.KindAssistant, append(RW(), types.VerbUse)), types.NewRule(types.KindPlugin, RW()), types.NewRule(types.KindIntegration, append(RW(), types.VerbUse)), types.NewRule(types.KindBilling, RW()), @@ -207,6 +208,7 @@ func defaultAllowRules() map[string][]types.Rule { types.NewRule(types.KindPlugin, RW()), types.NewRule(types.KindIntegration, append(RW(), types.VerbUse)), types.NewRule(types.KindBilling, RW()), + types.NewRule(types.KindAssistant, append(RW(), types.VerbUse)), }, } } diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index e63f846f0b935..2c7e3d1cea269 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -726,6 +726,25 @@ func (h *Handler) bindDefaultEndpoints() { h.PUT("/webapi/headless/:headless_authentication_id", h.WithAuth(h.putHeadlessState)) h.GET("/webapi/sites/:site/user-groups", h.WithClusterAuth(h.getUserGroups)) + + // WebSocket endpoint for the chat conversation + h.GET("/webapi/sites/:site/assistant", h.WithClusterAuth(h.assistant)) + + // Sets the title for the conversation. + h.POST("/webapi/assistant/conversations/:conversation_id/title", h.WithAuth(h.setAssistantTitle)) + h.POST("/webapi/assistant/title/summary", h.WithAuth(h.generateAssistantTitle)) + + // Creates a new conversation - the conversation ID is returned in the response. + h.POST("/webapi/assistant/conversations", h.WithAuth(h.createAssistantConversation)) + + // Returns all conversations for the given user. + h.GET("/webapi/assistant/conversations", h.WithAuth(h.getAssistantConversations)) + + // Returns all messages in the given conversation. + h.GET("/webapi/assistant/conversations/:conversation_id", h.WithAuth(h.getAssistantConversationByID)) + + // Allows executing an arbitrary command on multiple nodes. + h.GET("/webapi/command/:site/execute", h.WithClusterAuth(h.executeCommand)) } // GetProxyClient returns authenticated auth server client @@ -947,27 +966,27 @@ func deviceTrustDisabled(cap types.AuthPreference) bool { } func getAuthSettings(ctx context.Context, authClient auth.ClientI) (webclient.AuthenticationSettings, error) { - cap, err := authClient.GetAuthPreference(ctx) + authPreference, err := authClient.GetAuthPreference(ctx) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) } var as webclient.AuthenticationSettings - switch cap.GetType() { + switch authPreference.GetType() { case constants.Local: - as, err = localSettings(cap) + as, err = localSettings(authPreference) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) } case constants.OIDC: - if cap.GetConnectorName() != "" { - oidcConnector, err := authClient.GetOIDCConnector(ctx, cap.GetConnectorName(), false) + if authPreference.GetConnectorName() != "" { + oidcConnector, err := authClient.GetOIDCConnector(ctx, authPreference.GetConnectorName(), false) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) } - as = oidcSettings(oidcConnector, cap) + as = oidcSettings(oidcConnector, authPreference) } else { oidcConnectors, err := authClient.GetOIDCConnectors(ctx, false) if err != nil { @@ -977,16 +996,16 @@ func getAuthSettings(ctx context.Context, authClient auth.ClientI) (webclient.Au return webclient.AuthenticationSettings{}, trace.BadParameter("no oidc connectors found") } - as = oidcSettings(oidcConnectors[0], cap) + as = oidcSettings(oidcConnectors[0], authPreference) } case constants.SAML: - if cap.GetConnectorName() != "" { - samlConnector, err := authClient.GetSAMLConnector(ctx, cap.GetConnectorName(), false) + if authPreference.GetConnectorName() != "" { + samlConnector, err := authClient.GetSAMLConnector(ctx, authPreference.GetConnectorName(), false) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) } - as = samlSettings(samlConnector, cap) + as = samlSettings(samlConnector, authPreference) } else { samlConnectors, err := authClient.GetSAMLConnectors(ctx, false) if err != nil { @@ -996,15 +1015,15 @@ func getAuthSettings(ctx context.Context, authClient auth.ClientI) (webclient.Au return webclient.AuthenticationSettings{}, trace.BadParameter("no saml connectors found") } - as = samlSettings(samlConnectors[0], cap) + as = samlSettings(samlConnectors[0], authPreference) } case constants.Github: - if cap.GetConnectorName() != "" { - githubConnector, err := authClient.GetGithubConnector(ctx, cap.GetConnectorName(), false) + if authPreference.GetConnectorName() != "" { + githubConnector, err := authClient.GetGithubConnector(ctx, authPreference.GetConnectorName(), false) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) } - as = githubSettings(githubConnector, cap) + as = githubSettings(githubConnector, authPreference) } else { githubConnectors, err := authClient.GetGithubConnectors(ctx, false) if err != nil { @@ -1013,13 +1032,13 @@ func getAuthSettings(ctx context.Context, authClient auth.ClientI) (webclient.Au if len(githubConnectors) == 0 { return webclient.AuthenticationSettings{}, trace.BadParameter("no github connectors found") } - as = githubSettings(githubConnectors[0], cap) + as = githubSettings(githubConnectors[0], authPreference) } default: - return webclient.AuthenticationSettings{}, trace.BadParameter("unknown type %v", cap.GetType()) + return webclient.AuthenticationSettings{}, trace.BadParameter("unknown type %v", authPreference.GetType()) } - as.HasMessageOfTheDay = cap.GetMessageOfTheDay() != "" + as.HasMessageOfTheDay = authPreference.GetMessageOfTheDay() != "" pingResp, err := authClient.Ping(ctx) if err != nil { return webclient.AuthenticationSettings{}, trace.Wrap(err) @@ -2647,7 +2666,7 @@ func (h *Handler) fetchExistingSession(ctx context.Context, clt auth.ClientI, re // When joining an existing session use the specially handled // `SSHSessionJoinPrincipal` login instead of the provided login so that // users are able to join sessions without having permissions to create - // new ones themselves for auditing purposes. Otherwise the user would + // new ones themselves for auditing purposes. Otherwise, the user would // fail the SSH lib username validation step. sessionData.Login = teleport.SSHSessionJoinPrincipal // Using the Login above will then display `-teleport-internal-join` as the diff --git a/lib/web/assistant.go b/lib/web/assistant.go new file mode 100644 index 0000000000000..3de566138bed7 --- /dev/null +++ b/lib/web/assistant.go @@ -0,0 +1,64 @@ +/* + + Copyright 2023 Gravitational, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +package web + +import ( + "net/http" + + "github.com/gravitational/trace" + "github.com/julienschmidt/httprouter" + + "github.com/gravitational/teleport/lib/reversetunnel" +) + +func (h *Handler) createAssistantConversation(_ http.ResponseWriter, r *http.Request, + _ httprouter.Params, sctx *SessionContext, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} + +func (h *Handler) getAssistantConversationByID(_ http.ResponseWriter, r *http.Request, + p httprouter.Params, sctx *SessionContext, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} + +func (h *Handler) getAssistantConversations(_ http.ResponseWriter, r *http.Request, + _ httprouter.Params, sctx *SessionContext, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} + +func (h *Handler) setAssistantTitle(_ http.ResponseWriter, r *http.Request, + p httprouter.Params, sctx *SessionContext, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} + +func (h *Handler) generateAssistantTitle(_ http.ResponseWriter, r *http.Request, + p httprouter.Params, sctx *SessionContext, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} + +func (h *Handler) assistant(w http.ResponseWriter, r *http.Request, _ httprouter.Params, + sctx *SessionContext, site reversetunnel.RemoteSite, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +} diff --git a/lib/web/command.go b/lib/web/command.go new file mode 100644 index 0000000000000..5d21fc3037c9f --- /dev/null +++ b/lib/web/command.go @@ -0,0 +1,39 @@ +/* + + Copyright 2023 Gravitational, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +*/ + +package web + +import ( + "net/http" + + "github.com/gravitational/trace" + "github.com/julienschmidt/httprouter" + + "github.com/gravitational/teleport/lib/reversetunnel" +) + +func (h *Handler) executeCommand( + w http.ResponseWriter, + r *http.Request, + _ httprouter.Params, + sessionCtx *SessionContext, + site reversetunnel.RemoteSite, +) (any, error) { + return nil, trace.NotImplemented("not implemented") +}