From f6a0923e3518eb5310a73d3ed45034302d7648d3 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Thu, 30 Mar 2023 11:40:48 +0200 Subject: [PATCH 01/32] Fix prehog protos package name --- gen/proto/go/prehog/v1alpha/connect.pb.go | 7 ++++--- .../connect.connect.go | 2 +- .../tbot.connect.go | 2 +- .../teleport.connect.go | 2 +- gen/proto/go/prehog/v1alpha/tbot.pb.go | 7 ++++--- gen/proto/go/prehog/v1alpha/teleport.pb.go | 7 ++++--- lib/usagereporter/daemon/usagereporter.go | 2 +- lib/usagereporter/teleport/usagereporter.go | 2 +- proto/prehog/v1alpha/connect.proto | 2 +- proto/prehog/v1alpha/tbot.proto | 2 +- proto/prehog/v1alpha/teleport.proto | 2 +- tool/tbot/anonymous_telemetry.go | 2 +- 12 files changed, 21 insertions(+), 18 deletions(-) rename gen/proto/go/prehog/v1alpha/{v1alphaconnect => prehogv1alphaconnect}/connect.connect.go (99%) rename gen/proto/go/prehog/v1alpha/{v1alphaconnect => prehogv1alphaconnect}/tbot.connect.go (99%) rename gen/proto/go/prehog/v1alpha/{v1alphaconnect => prehogv1alphaconnect}/teleport.connect.go (99%) diff --git a/gen/proto/go/prehog/v1alpha/connect.pb.go b/gen/proto/go/prehog/v1alpha/connect.pb.go index 48518a2b278e6..a398438e9bb56 100644 --- a/gen/proto/go/prehog/v1alpha/connect.pb.go +++ b/gen/proto/go/prehog/v1alpha/connect.pb.go @@ -18,7 +18,7 @@ // protoc (unknown) // source: prehog/v1alpha/connect.proto -package v1alpha +package prehogv1alpha import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -821,11 +821,12 @@ var file_prehog_v1alpha_connect_proto_rawDesc = []byte{ 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/proto/go/prehog/v1alpha/v1alphaconnect/connect.connect.go b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/connect.connect.go similarity index 99% rename from gen/proto/go/prehog/v1alpha/v1alphaconnect/connect.connect.go rename to gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/connect.connect.go index 86e50b2b42fcb..3862625d2985b 100644 --- a/gen/proto/go/prehog/v1alpha/v1alphaconnect/connect.connect.go +++ b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/connect.connect.go @@ -2,7 +2,7 @@ // // Source: prehog/v1alpha/connect.proto -package v1alphaconnect +package prehogv1alphaconnect import ( context "context" diff --git a/gen/proto/go/prehog/v1alpha/v1alphaconnect/tbot.connect.go b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/tbot.connect.go similarity index 99% rename from gen/proto/go/prehog/v1alpha/v1alphaconnect/tbot.connect.go rename to gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/tbot.connect.go index 602372d02e8f9..58e9f5e860f78 100644 --- a/gen/proto/go/prehog/v1alpha/v1alphaconnect/tbot.connect.go +++ b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/tbot.connect.go @@ -2,7 +2,7 @@ // // Source: prehog/v1alpha/tbot.proto -package v1alphaconnect +package prehogv1alphaconnect import ( context "context" diff --git a/gen/proto/go/prehog/v1alpha/v1alphaconnect/teleport.connect.go b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go similarity index 99% rename from gen/proto/go/prehog/v1alpha/v1alphaconnect/teleport.connect.go rename to gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go index 7c154b1438eac..8ced4f3e66918 100644 --- a/gen/proto/go/prehog/v1alpha/v1alphaconnect/teleport.connect.go +++ b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go @@ -2,7 +2,7 @@ // // Source: prehog/v1alpha/teleport.proto -package v1alphaconnect +package prehogv1alphaconnect import ( context "context" diff --git a/gen/proto/go/prehog/v1alpha/tbot.pb.go b/gen/proto/go/prehog/v1alpha/tbot.pb.go index 2bfd67aafc601..4157b7cf5261a 100644 --- a/gen/proto/go/prehog/v1alpha/tbot.pb.go +++ b/gen/proto/go/prehog/v1alpha/tbot.pb.go @@ -18,7 +18,7 @@ // protoc (unknown) // source: prehog/v1alpha/tbot.proto -package v1alpha +package prehogv1alpha import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -384,11 +384,12 @@ var file_prehog_v1alpha_tbot_proto_rawDesc = []byte{ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x62, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index 96bd123564d64..160d2c15f2261 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -18,7 +18,7 @@ // protoc (unknown) // source: prehog/v1alpha/teleport.proto -package v1alpha +package prehogv1alpha import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -4036,11 +4036,12 @@ var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/lib/usagereporter/daemon/usagereporter.go b/lib/usagereporter/daemon/usagereporter.go index acde2e3f8669e..5a86a31ce7d40 100644 --- a/lib/usagereporter/daemon/usagereporter.go +++ b/lib/usagereporter/daemon/usagereporter.go @@ -24,7 +24,7 @@ import ( "github.com/gravitational/trace" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/v1alphaconnect" + prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" teletermv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/usagereporter" diff --git a/lib/usagereporter/teleport/usagereporter.go b/lib/usagereporter/teleport/usagereporter.go index 8a149242b1ab3..3dbb998bbdda4 100644 --- a/lib/usagereporter/teleport/usagereporter.go +++ b/lib/usagereporter/teleport/usagereporter.go @@ -31,7 +31,7 @@ import ( "github.com/gravitational/teleport/api/types" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/v1alphaconnect" + prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/observability/metrics" "github.com/gravitational/teleport/lib/usagereporter" diff --git a/proto/prehog/v1alpha/connect.proto b/proto/prehog/v1alpha/connect.proto index bbc744de10d3b..df4c16b392ac2 100644 --- a/proto/prehog/v1alpha/connect.proto +++ b/proto/prehog/v1alpha/connect.proto @@ -18,7 +18,7 @@ package prehog.v1alpha; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha"; +option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha;prehogv1alpha"; message ConnectClusterLoginEvent { // anonymized diff --git a/proto/prehog/v1alpha/tbot.proto b/proto/prehog/v1alpha/tbot.proto index 4bf533f97f37a..1eaeeec1bce1c 100644 --- a/proto/prehog/v1alpha/tbot.proto +++ b/proto/prehog/v1alpha/tbot.proto @@ -18,7 +18,7 @@ package prehog.v1alpha; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha"; +option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha;prehogv1alpha"; // TbotStartEvent is triggered whenever tbot starts, regardless of whether tbot // was able to successfully connect to or authenticate with a Teleport cluster. diff --git a/proto/prehog/v1alpha/teleport.proto b/proto/prehog/v1alpha/teleport.proto index fbeaca52cdb26..64a6bdea0875b 100644 --- a/proto/prehog/v1alpha/teleport.proto +++ b/proto/prehog/v1alpha/teleport.proto @@ -19,7 +19,7 @@ package prehog.v1alpha; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha"; +option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha;prehogv1alpha"; message UserLoginEvent { // anonymized diff --git a/tool/tbot/anonymous_telemetry.go b/tool/tbot/anonymous_telemetry.go index ba184b53ea636..a64959dc42316 100644 --- a/tool/tbot/anonymous_telemetry.go +++ b/tool/tbot/anonymous_telemetry.go @@ -29,7 +29,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/v1alphaconnect" + prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" "github.com/gravitational/teleport/lib/tbot/config" ) From 3bceb78ab44093357faa548cf741dc6adc7429a5 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 20 Mar 2023 23:22:23 +0100 Subject: [PATCH 02/32] sync prehog/v1alpha/teleport.proto from cloud --- .../prehogv1alphaconnect/teleport.connect.go | 28 +- gen/proto/go/prehog/v1alpha/teleport.pb.go | 661 +++++++-- .../js/prehog/v1alpha/teleport_grpc_pb.d.ts | 17 + .../js/prehog/v1alpha/teleport_grpc_pb.js | 33 + gen/proto/js/prehog/v1alpha/teleport_pb.d.ts | 157 +++ gen/proto/js/prehog/v1alpha/teleport_pb.js | 1229 +++++++++++++++++ proto/prehog/v1alpha/teleport.proto | 52 + 7 files changed, 2060 insertions(+), 117 deletions(-) diff --git a/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go index 8ced4f3e66918..753252fea4721 100644 --- a/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go +++ b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go @@ -32,6 +32,7 @@ type TeleportReportingServiceClient interface { SubmitEvent(context.Context, *connect_go.Request[v1alpha.SubmitEventRequest]) (*connect_go.Response[v1alpha.SubmitEventResponse], error) SubmitEvents(context.Context, *connect_go.Request[v1alpha.SubmitEventsRequest]) (*connect_go.Response[v1alpha.SubmitEventsResponse], error) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) + SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) } // NewTeleportReportingServiceClient constructs a client for the @@ -59,14 +60,20 @@ func NewTeleportReportingServiceClient(httpClient connect_go.HTTPClient, baseURL baseURL+"/prehog.v1alpha.TeleportReportingService/HelloTeleport", opts..., ), + submitUsageReports: connect_go.NewClient[v1alpha.SubmitUsageReportsRequest, v1alpha.SubmitUsageReportsResponse]( + httpClient, + baseURL+"/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", + opts..., + ), } } // teleportReportingServiceClient implements TeleportReportingServiceClient. type teleportReportingServiceClient struct { - submitEvent *connect_go.Client[v1alpha.SubmitEventRequest, v1alpha.SubmitEventResponse] - submitEvents *connect_go.Client[v1alpha.SubmitEventsRequest, v1alpha.SubmitEventsResponse] - helloTeleport *connect_go.Client[v1alpha.HelloTeleportRequest, v1alpha.HelloTeleportResponse] + submitEvent *connect_go.Client[v1alpha.SubmitEventRequest, v1alpha.SubmitEventResponse] + submitEvents *connect_go.Client[v1alpha.SubmitEventsRequest, v1alpha.SubmitEventsResponse] + helloTeleport *connect_go.Client[v1alpha.HelloTeleportRequest, v1alpha.HelloTeleportResponse] + submitUsageReports *connect_go.Client[v1alpha.SubmitUsageReportsRequest, v1alpha.SubmitUsageReportsResponse] } // SubmitEvent calls prehog.v1alpha.TeleportReportingService.SubmitEvent. @@ -86,6 +93,11 @@ func (c *teleportReportingServiceClient) HelloTeleport(ctx context.Context, req return c.helloTeleport.CallUnary(ctx, req) } +// SubmitUsageReports calls prehog.v1alpha.TeleportReportingService.SubmitUsageReports. +func (c *teleportReportingServiceClient) SubmitUsageReports(ctx context.Context, req *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) { + return c.submitUsageReports.CallUnary(ctx, req) +} + // TeleportReportingServiceHandler is an implementation of the // prehog.v1alpha.TeleportReportingService service. type TeleportReportingServiceHandler interface { @@ -93,6 +105,7 @@ type TeleportReportingServiceHandler interface { SubmitEvent(context.Context, *connect_go.Request[v1alpha.SubmitEventRequest]) (*connect_go.Response[v1alpha.SubmitEventResponse], error) SubmitEvents(context.Context, *connect_go.Request[v1alpha.SubmitEventsRequest]) (*connect_go.Response[v1alpha.SubmitEventsResponse], error) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) + SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) } // NewTeleportReportingServiceHandler builds an HTTP handler from the service implementation. It @@ -117,6 +130,11 @@ func NewTeleportReportingServiceHandler(svc TeleportReportingServiceHandler, opt svc.HelloTeleport, opts..., )) + mux.Handle("/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", connect_go.NewUnaryHandler( + "/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", + svc.SubmitUsageReports, + opts..., + )) return "/prehog.v1alpha.TeleportReportingService/", mux } @@ -134,3 +152,7 @@ func (UnimplementedTeleportReportingServiceHandler) SubmitEvents(context.Context func (UnimplementedTeleportReportingServiceHandler) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) { return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("prehog.v1alpha.TeleportReportingService.HelloTeleport is not implemented")) } + +func (UnimplementedTeleportReportingServiceHandler) SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) { + return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("prehog.v1alpha.TeleportReportingService.SubmitUsageReports is not implemented")) +} diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index 160d2c15f2261..485ca92dda9c0 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -3320,6 +3320,323 @@ func (*HelloTeleportResponse) Descriptor() ([]byte, []int) { return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{43} } +type UserActivityReport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // randomly generated UUID for this specific report, 16 bytes (in string order) + ReportUuid []byte `protobuf:"bytes,1,opt,name=report_uuid,json=reportUuid,proto3" json:"report_uuid,omitempty"` + // anonymized, 32 bytes (HMAC-SHA-256) + ClusterName []byte `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` + // anonymized, 32 bytes (HMAC-SHA-256) + ReporterHostid []byte `protobuf:"bytes,3,opt,name=reporter_hostid,json=reporterHostid,proto3" json:"reporter_hostid,omitempty"` + // beginning of the time window for this data; ending is not specified but is + // intended to be at most 15 minutes + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + Records []*UserActivityRecord `protobuf:"bytes,5,rep,name=records,proto3" json:"records,omitempty"` +} + +func (x *UserActivityReport) Reset() { + *x = UserActivityReport{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserActivityReport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserActivityReport) ProtoMessage() {} + +func (x *UserActivityReport) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[44] + 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 UserActivityReport.ProtoReflect.Descriptor instead. +func (*UserActivityReport) Descriptor() ([]byte, []int) { + return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{44} +} + +func (x *UserActivityReport) GetReportUuid() []byte { + if x != nil { + return x.ReportUuid + } + return nil +} + +func (x *UserActivityReport) GetClusterName() []byte { + if x != nil { + return x.ClusterName + } + return nil +} + +func (x *UserActivityReport) GetReporterHostid() []byte { + if x != nil { + return x.ReporterHostid + } + return nil +} + +func (x *UserActivityReport) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *UserActivityReport) GetRecords() []*UserActivityRecord { + if x != nil { + return x.Records + } + return nil +} + +type UserActivityRecord struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // anonymized, 32 bytes (HMAC-SHA-256) + UserName []byte `protobuf:"bytes,1,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` + // counter of user.login events + Logins uint64 `protobuf:"varint,2,opt,name=logins,proto3" json:"logins,omitempty"` + // counter of session.start events (non-Kube) + SshSessions uint64 `protobuf:"varint,3,opt,name=ssh_sessions,json=sshSessions,proto3" json:"ssh_sessions,omitempty"` + // counter of app.session.start events (non-TCP) + AppSessions uint64 `protobuf:"varint,4,opt,name=app_sessions,json=appSessions,proto3" json:"app_sessions,omitempty"` + // counter of session.start events (only Kube) + KubeSessions uint64 `protobuf:"varint,5,opt,name=kube_sessions,json=kubeSessions,proto3" json:"kube_sessions,omitempty"` + // counter of db.session.start events + DbSessions uint64 `protobuf:"varint,6,opt,name=db_sessions,json=dbSessions,proto3" json:"db_sessions,omitempty"` + // counter of windows.desktop.session.start events + DesktopSessions uint64 `protobuf:"varint,7,opt,name=desktop_sessions,json=desktopSessions,proto3" json:"desktop_sessions,omitempty"` + // counter of app.session.start events (only TCP) + AppTcpSessions uint64 `protobuf:"varint,8,opt,name=app_tcp_sessions,json=appTcpSessions,proto3" json:"app_tcp_sessions,omitempty"` + // counter of port events + SshPortSessions uint64 `protobuf:"varint,9,opt,name=ssh_port_sessions,json=sshPortSessions,proto3" json:"ssh_port_sessions,omitempty"` + // counter of kube.request events + KubeRequests uint64 `protobuf:"varint,10,opt,name=kube_requests,json=kubeRequests,proto3" json:"kube_requests,omitempty"` + // counter of sftp events + SftpEvents uint64 `protobuf:"varint,11,opt,name=sftp_events,json=sftpEvents,proto3" json:"sftp_events,omitempty"` +} + +func (x *UserActivityRecord) Reset() { + *x = UserActivityRecord{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserActivityRecord) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserActivityRecord) ProtoMessage() {} + +func (x *UserActivityRecord) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[45] + 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 UserActivityRecord.ProtoReflect.Descriptor instead. +func (*UserActivityRecord) Descriptor() ([]byte, []int) { + return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{45} +} + +func (x *UserActivityRecord) GetUserName() []byte { + if x != nil { + return x.UserName + } + return nil +} + +func (x *UserActivityRecord) GetLogins() uint64 { + if x != nil { + return x.Logins + } + return 0 +} + +func (x *UserActivityRecord) GetSshSessions() uint64 { + if x != nil { + return x.SshSessions + } + return 0 +} + +func (x *UserActivityRecord) GetAppSessions() uint64 { + if x != nil { + return x.AppSessions + } + return 0 +} + +func (x *UserActivityRecord) GetKubeSessions() uint64 { + if x != nil { + return x.KubeSessions + } + return 0 +} + +func (x *UserActivityRecord) GetDbSessions() uint64 { + if x != nil { + return x.DbSessions + } + return 0 +} + +func (x *UserActivityRecord) GetDesktopSessions() uint64 { + if x != nil { + return x.DesktopSessions + } + return 0 +} + +func (x *UserActivityRecord) GetAppTcpSessions() uint64 { + if x != nil { + return x.AppTcpSessions + } + return 0 +} + +func (x *UserActivityRecord) GetSshPortSessions() uint64 { + if x != nil { + return x.SshPortSessions + } + return 0 +} + +func (x *UserActivityRecord) GetKubeRequests() uint64 { + if x != nil { + return x.KubeRequests + } + return 0 +} + +func (x *UserActivityRecord) GetSftpEvents() uint64 { + if x != nil { + return x.SftpEvents + } + return 0 +} + +type SubmitUsageReportsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserActivity []*UserActivityReport `protobuf:"bytes,1,rep,name=user_activity,json=userActivity,proto3" json:"user_activity,omitempty"` +} + +func (x *SubmitUsageReportsRequest) Reset() { + *x = SubmitUsageReportsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitUsageReportsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitUsageReportsRequest) ProtoMessage() {} + +func (x *SubmitUsageReportsRequest) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[46] + 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 SubmitUsageReportsRequest.ProtoReflect.Descriptor instead. +func (*SubmitUsageReportsRequest) Descriptor() ([]byte, []int) { + return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{46} +} + +func (x *SubmitUsageReportsRequest) GetUserActivity() []*UserActivityReport { + if x != nil { + return x.UserActivity + } + return nil +} + +type SubmitUsageReportsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // randomly generated UUID for this specific batch, 16 bytes (in string order) + BatchUuid []byte `protobuf:"bytes,1,opt,name=batch_uuid,json=batchUuid,proto3" json:"batch_uuid,omitempty"` +} + +func (x *SubmitUsageReportsResponse) Reset() { + *x = SubmitUsageReportsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitUsageReportsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitUsageReportsResponse) ProtoMessage() {} + +func (x *SubmitUsageReportsResponse) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1alpha_teleport_proto_msgTypes[47] + 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 SubmitUsageReportsResponse.ProtoReflect.Descriptor instead. +func (*SubmitUsageReportsResponse) Descriptor() ([]byte, []int) { + return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{47} +} + +func (x *SubmitUsageReportsResponse) GetBatchUuid() []byte { + if x != nil { + return x.BatchUuid + } + return nil +} + var File_prehog_v1alpha_teleport_proto protoreflect.FileDescriptor var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ @@ -3941,107 +4258,166 @@ var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2a, 0xe2, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, - 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x5f, - 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, - 0x45, 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4f, - 0x50, 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xaa, 0x06, 0x0a, 0x10, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x0a, - 0x1d, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x1c, 0x0a, 0x18, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x20, - 0x0a, 0x1c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x52, 0x4e, 0x45, 0x54, 0x45, 0x53, 0x10, 0x02, - 0x12, 0x33, 0x0a, 0x2f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, - 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, - 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, - 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, - 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, - 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, - 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, - 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x53, 0x45, - 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x2b, 0x0a, 0x27, 0x44, - 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, - 0x45, 0x53, 0x5f, 0x52, 0x44, 0x53, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, - 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x52, 0x44, 0x53, - 0x10, 0x07, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x08, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x43, 0x50, 0x10, - 0x09, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, - 0x45, 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x0a, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, - 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x5f, 0x52, 0x44, 0x53, 0x10, 0x0b, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x6e, 0x73, 0x65, 0x22, 0xfa, 0x01, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, + 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x72, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x22, 0x9c, 0x03, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x62, 0x5f, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x64, 0x62, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x6b, + 0x74, 0x6f, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, + 0x70, 0x70, 0x54, 0x63, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, + 0x11, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x50, 0x6f, 0x72, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, + 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x66, 0x74, 0x70, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x66, 0x74, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x64, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0d, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x3b, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x55, 0x75, + 0x69, 0x64, 0x2a, 0xe2, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, + 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, 0x5f, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x5f, 0x53, + 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x52, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, 0x45, + 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4f, 0x50, + 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xaa, 0x06, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x1d, + 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x1c, 0x0a, 0x18, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x20, 0x0a, + 0x1c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x52, 0x4e, 0x45, 0x54, 0x45, 0x53, 0x10, 0x02, 0x12, + 0x33, 0x0a, 0x2f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, + 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, + 0x45, 0x44, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, + 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, + 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, + 0x53, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, - 0x44, 0x53, 0x48, 0x49, 0x46, 0x54, 0x10, 0x0c, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, - 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0d, 0x12, 0x30, - 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, - 0x49, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0e, - 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, - 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x0f, 0x12, 0x28, 0x0a, - 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, - 0x4c, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x10, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x4c, + 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, + 0x53, 0x5f, 0x52, 0x44, 0x53, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, + 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x52, 0x44, 0x53, 0x10, + 0x07, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x08, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, + 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, + 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x43, 0x50, 0x10, 0x09, + 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, 0x45, + 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x0a, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, + 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, + 0x52, 0x44, 0x53, 0x10, 0x0b, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, + 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, + 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x44, + 0x53, 0x48, 0x49, 0x46, 0x54, 0x10, 0x0c, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, - 0x47, 0x43, 0x50, 0x10, 0x11, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, - 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, - 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, - 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, - 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x1b, - 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xb4, 0x02, 0x0a, 0x18, - 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, - 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, - 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, - 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x65, + 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0d, 0x12, 0x30, 0x0a, + 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, 0x49, + 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0e, 0x12, + 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, + 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x0f, 0x12, 0x28, 0x0a, 0x24, + 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, + 0x5f, 0x47, 0x43, 0x50, 0x10, 0x10, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x47, + 0x43, 0x50, 0x10, 0x11, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, + 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, + 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, 0x44, + 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, + 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xa3, 0x03, 0x0a, 0x18, 0x54, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x65, 0x68, - 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, - 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, + 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, + 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4057,7 +4433,7 @@ func file_prehog_v1alpha_teleport_proto_rawDescGZIP() []byte { } var file_prehog_v1alpha_teleport_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_prehog_v1alpha_teleport_proto_msgTypes = make([]protoimpl.MessageInfo, 44) +var file_prehog_v1alpha_teleport_proto_msgTypes = make([]protoimpl.MessageInfo, 48) var file_prehog_v1alpha_teleport_proto_goTypes = []interface{}{ (ResourceKind)(0), // 0: prehog.v1alpha.ResourceKind (DiscoverResource)(0), // 1: prehog.v1alpha.DiscoverResource @@ -4106,12 +4482,16 @@ var file_prehog_v1alpha_teleport_proto_goTypes = []interface{}{ (*SubmitEventsResponse)(nil), // 44: prehog.v1alpha.SubmitEventsResponse (*HelloTeleportRequest)(nil), // 45: prehog.v1alpha.HelloTeleportRequest (*HelloTeleportResponse)(nil), // 46: prehog.v1alpha.HelloTeleportResponse - (*durationpb.Duration)(nil), // 47: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 48: google.protobuf.Timestamp + (*UserActivityReport)(nil), // 47: prehog.v1alpha.UserActivityReport + (*UserActivityRecord)(nil), // 48: prehog.v1alpha.UserActivityRecord + (*SubmitUsageReportsRequest)(nil), // 49: prehog.v1alpha.SubmitUsageReportsRequest + (*SubmitUsageReportsResponse)(nil), // 50: prehog.v1alpha.SubmitUsageReportsResponse + (*durationpb.Duration)(nil), // 51: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 52: google.protobuf.Timestamp } var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 0, // 0: prehog.v1alpha.ResourceHeartbeatEvent.resource_kind:type_name -> prehog.v1alpha.ResourceKind - 47, // 1: prehog.v1alpha.UserCertificateIssuedEvent.ttl:type_name -> google.protobuf.Duration + 51, // 1: prehog.v1alpha.UserCertificateIssuedEvent.ttl:type_name -> google.protobuf.Duration 1, // 2: prehog.v1alpha.DiscoverResourceMetadata.resource:type_name -> prehog.v1alpha.DiscoverResource 2, // 3: prehog.v1alpha.DiscoverStepStatus.status:type_name -> prehog.v1alpha.DiscoverStatus 18, // 4: prehog.v1alpha.UIDiscoverStartedEvent.metadata:type_name -> prehog.v1alpha.DiscoverMetadata @@ -4149,7 +4529,7 @@ var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 18, // 36: prehog.v1alpha.UIDiscoverCompletedEvent.metadata:type_name -> prehog.v1alpha.DiscoverMetadata 19, // 37: prehog.v1alpha.UIDiscoverCompletedEvent.resource:type_name -> prehog.v1alpha.DiscoverResourceMetadata 20, // 38: prehog.v1alpha.UIDiscoverCompletedEvent.status:type_name -> prehog.v1alpha.DiscoverStepStatus - 48, // 39: prehog.v1alpha.SubmitEventRequest.timestamp:type_name -> google.protobuf.Timestamp + 52, // 39: prehog.v1alpha.SubmitEventRequest.timestamp:type_name -> google.protobuf.Timestamp 3, // 40: prehog.v1alpha.SubmitEventRequest.user_login:type_name -> prehog.v1alpha.UserLoginEvent 4, // 41: prehog.v1alpha.SubmitEventRequest.sso_create:type_name -> prehog.v1alpha.SSOCreateEvent 5, // 42: prehog.v1alpha.SubmitEventRequest.resource_create:type_name -> prehog.v1alpha.ResourceCreateEvent @@ -4187,17 +4567,22 @@ var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 40, // 74: prehog.v1alpha.SubmitEventRequest.agent_metadata_event:type_name -> prehog.v1alpha.AgentMetadataEvent 6, // 75: prehog.v1alpha.SubmitEventRequest.resource_heartbeat:type_name -> prehog.v1alpha.ResourceHeartbeatEvent 41, // 76: prehog.v1alpha.SubmitEventsRequest.events:type_name -> prehog.v1alpha.SubmitEventRequest - 41, // 77: prehog.v1alpha.TeleportReportingService.SubmitEvent:input_type -> prehog.v1alpha.SubmitEventRequest - 43, // 78: prehog.v1alpha.TeleportReportingService.SubmitEvents:input_type -> prehog.v1alpha.SubmitEventsRequest - 45, // 79: prehog.v1alpha.TeleportReportingService.HelloTeleport:input_type -> prehog.v1alpha.HelloTeleportRequest - 42, // 80: prehog.v1alpha.TeleportReportingService.SubmitEvent:output_type -> prehog.v1alpha.SubmitEventResponse - 44, // 81: prehog.v1alpha.TeleportReportingService.SubmitEvents:output_type -> prehog.v1alpha.SubmitEventsResponse - 46, // 82: prehog.v1alpha.TeleportReportingService.HelloTeleport:output_type -> prehog.v1alpha.HelloTeleportResponse - 80, // [80:83] is the sub-list for method output_type - 77, // [77:80] is the sub-list for method input_type - 77, // [77:77] is the sub-list for extension type_name - 77, // [77:77] is the sub-list for extension extendee - 0, // [0:77] is the sub-list for field type_name + 52, // 77: prehog.v1alpha.UserActivityReport.start_time:type_name -> google.protobuf.Timestamp + 48, // 78: prehog.v1alpha.UserActivityReport.records:type_name -> prehog.v1alpha.UserActivityRecord + 47, // 79: prehog.v1alpha.SubmitUsageReportsRequest.user_activity:type_name -> prehog.v1alpha.UserActivityReport + 41, // 80: prehog.v1alpha.TeleportReportingService.SubmitEvent:input_type -> prehog.v1alpha.SubmitEventRequest + 43, // 81: prehog.v1alpha.TeleportReportingService.SubmitEvents:input_type -> prehog.v1alpha.SubmitEventsRequest + 45, // 82: prehog.v1alpha.TeleportReportingService.HelloTeleport:input_type -> prehog.v1alpha.HelloTeleportRequest + 49, // 83: prehog.v1alpha.TeleportReportingService.SubmitUsageReports:input_type -> prehog.v1alpha.SubmitUsageReportsRequest + 42, // 84: prehog.v1alpha.TeleportReportingService.SubmitEvent:output_type -> prehog.v1alpha.SubmitEventResponse + 44, // 85: prehog.v1alpha.TeleportReportingService.SubmitEvents:output_type -> prehog.v1alpha.SubmitEventsResponse + 46, // 86: prehog.v1alpha.TeleportReportingService.HelloTeleport:output_type -> prehog.v1alpha.HelloTeleportResponse + 50, // 87: prehog.v1alpha.TeleportReportingService.SubmitUsageReports:output_type -> prehog.v1alpha.SubmitUsageReportsResponse + 84, // [84:88] is the sub-list for method output_type + 80, // [80:84] is the sub-list for method input_type + 80, // [80:80] is the sub-list for extension type_name + 80, // [80:80] is the sub-list for extension extendee + 0, // [0:80] is the sub-list for field type_name } func init() { file_prehog_v1alpha_teleport_proto_init() } @@ -4734,6 +5119,54 @@ func file_prehog_v1alpha_teleport_proto_init() { return nil } } + file_prehog_v1alpha_teleport_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserActivityReport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1alpha_teleport_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserActivityRecord); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1alpha_teleport_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitUsageReportsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1alpha_teleport_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitUsageReportsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_prehog_v1alpha_teleport_proto_msgTypes[38].OneofWrappers = []interface{}{ (*SubmitEventRequest_UserLogin)(nil), @@ -4779,7 +5212,7 @@ func file_prehog_v1alpha_teleport_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_prehog_v1alpha_teleport_proto_rawDesc, NumEnums: 3, - NumMessages: 44, + NumMessages: 48, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts index b058fb8710cef..c806029b03eff 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts @@ -13,6 +13,7 @@ interface ITeleportReportingServiceService extends grpc.ServiceDefinition { @@ -42,6 +43,15 @@ interface ITeleportReportingServiceService_IHelloTeleport extends grpc.MethodDef responseSerialize: grpc.serialize; responseDeserialize: grpc.deserialize; } +interface ITeleportReportingServiceService_ISubmitUsageReports extends grpc.MethodDefinition { + path: "/prehog.v1alpha.TeleportReportingService/SubmitUsageReports"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} export const TeleportReportingServiceService: ITeleportReportingServiceService; @@ -49,6 +59,7 @@ export interface ITeleportReportingServiceServer { submitEvent: grpc.handleUnaryCall; submitEvents: grpc.handleUnaryCall; helloTeleport: grpc.handleUnaryCall; + submitUsageReports: grpc.handleUnaryCall; } export interface ITeleportReportingServiceClient { @@ -61,6 +72,9 @@ export interface ITeleportReportingServiceClient { helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; + submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; } export class TeleportReportingServiceClient extends grpc.Client implements ITeleportReportingServiceClient { @@ -74,4 +88,7 @@ export class TeleportReportingServiceClient extends grpc.Client implements ITele public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; + public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; } diff --git a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js index b075793a9573b..ae8470c38a463 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js +++ b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js @@ -87,6 +87,28 @@ function deserialize_prehog_v1alpha_SubmitEventsResponse(buffer_arg) { return prehog_v1alpha_teleport_pb.SubmitEventsResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_prehog_v1alpha_SubmitUsageReportsRequest(arg) { + if (!(arg instanceof prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest)) { + throw new Error('Expected argument of type prehog.v1alpha.SubmitUsageReportsRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_prehog_v1alpha_SubmitUsageReportsRequest(buffer_arg) { + return prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_prehog_v1alpha_SubmitUsageReportsResponse(arg) { + if (!(arg instanceof prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse)) { + throw new Error('Expected argument of type prehog.v1alpha.SubmitUsageReportsResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_prehog_v1alpha_SubmitUsageReportsResponse(buffer_arg) { + return prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + var TeleportReportingServiceService = exports.TeleportReportingServiceService = { submitEvent: { @@ -122,6 +144,17 @@ var TeleportReportingServiceService = exports.TeleportReportingServiceService = responseSerialize: serialize_prehog_v1alpha_HelloTeleportResponse, responseDeserialize: deserialize_prehog_v1alpha_HelloTeleportResponse, }, + submitUsageReports: { + path: '/prehog.v1alpha.TeleportReportingService/SubmitUsageReports', + requestStream: false, + responseStream: false, + requestType: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, + responseType: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse, + requestSerialize: serialize_prehog_v1alpha_SubmitUsageReportsRequest, + requestDeserialize: deserialize_prehog_v1alpha_SubmitUsageReportsRequest, + responseSerialize: serialize_prehog_v1alpha_SubmitUsageReportsResponse, + responseDeserialize: deserialize_prehog_v1alpha_SubmitUsageReportsResponse, + }, }; exports.TeleportReportingServiceClient = grpc.makeGenericClientConstructor(TeleportReportingServiceService); diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts index bdd589f7b78be..0d6b914d68b78 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts @@ -1583,6 +1583,163 @@ export namespace HelloTeleportResponse { } } +export class UserActivityReport extends jspb.Message { + getReportUuid(): Uint8Array | string; + getReportUuid_asU8(): Uint8Array; + getReportUuid_asB64(): string; + setReportUuid(value: Uint8Array | string): UserActivityReport; + + getClusterName(): Uint8Array | string; + getClusterName_asU8(): Uint8Array; + getClusterName_asB64(): string; + setClusterName(value: Uint8Array | string): UserActivityReport; + + getReporterHostid(): Uint8Array | string; + getReporterHostid_asU8(): Uint8Array; + getReporterHostid_asB64(): string; + setReporterHostid(value: Uint8Array | string): UserActivityReport; + + + hasStartTime(): boolean; + clearStartTime(): void; + getStartTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setStartTime(value?: google_protobuf_timestamp_pb.Timestamp): UserActivityReport; + + clearRecordsList(): void; + getRecordsList(): Array; + setRecordsList(value: Array): UserActivityReport; + addRecords(value?: UserActivityRecord, index?: number): UserActivityRecord; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UserActivityReport.AsObject; + static toObject(includeInstance: boolean, msg: UserActivityReport): UserActivityReport.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UserActivityReport, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UserActivityReport; + static deserializeBinaryFromReader(message: UserActivityReport, reader: jspb.BinaryReader): UserActivityReport; +} + +export namespace UserActivityReport { + export type AsObject = { + reportUuid: Uint8Array | string, + clusterName: Uint8Array | string, + reporterHostid: Uint8Array | string, + startTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + recordsList: Array, + } +} + +export class UserActivityRecord extends jspb.Message { + getUserName(): Uint8Array | string; + getUserName_asU8(): Uint8Array; + getUserName_asB64(): string; + setUserName(value: Uint8Array | string): UserActivityRecord; + + getLogins(): number; + setLogins(value: number): UserActivityRecord; + + getSshSessions(): number; + setSshSessions(value: number): UserActivityRecord; + + getAppSessions(): number; + setAppSessions(value: number): UserActivityRecord; + + getKubeSessions(): number; + setKubeSessions(value: number): UserActivityRecord; + + getDbSessions(): number; + setDbSessions(value: number): UserActivityRecord; + + getDesktopSessions(): number; + setDesktopSessions(value: number): UserActivityRecord; + + getAppTcpSessions(): number; + setAppTcpSessions(value: number): UserActivityRecord; + + getSshPortSessions(): number; + setSshPortSessions(value: number): UserActivityRecord; + + getKubeRequests(): number; + setKubeRequests(value: number): UserActivityRecord; + + getSftpEvents(): number; + setSftpEvents(value: number): UserActivityRecord; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UserActivityRecord.AsObject; + static toObject(includeInstance: boolean, msg: UserActivityRecord): UserActivityRecord.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UserActivityRecord, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UserActivityRecord; + static deserializeBinaryFromReader(message: UserActivityRecord, reader: jspb.BinaryReader): UserActivityRecord; +} + +export namespace UserActivityRecord { + export type AsObject = { + userName: Uint8Array | string, + logins: number, + sshSessions: number, + appSessions: number, + kubeSessions: number, + dbSessions: number, + desktopSessions: number, + appTcpSessions: number, + sshPortSessions: number, + kubeRequests: number, + sftpEvents: number, + } +} + +export class SubmitUsageReportsRequest extends jspb.Message { + clearUserActivityList(): void; + getUserActivityList(): Array; + setUserActivityList(value: Array): SubmitUsageReportsRequest; + addUserActivity(value?: UserActivityReport, index?: number): UserActivityReport; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SubmitUsageReportsRequest.AsObject; + static toObject(includeInstance: boolean, msg: SubmitUsageReportsRequest): SubmitUsageReportsRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: SubmitUsageReportsRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsRequest; + static deserializeBinaryFromReader(message: SubmitUsageReportsRequest, reader: jspb.BinaryReader): SubmitUsageReportsRequest; +} + +export namespace SubmitUsageReportsRequest { + export type AsObject = { + userActivityList: Array, + } +} + +export class SubmitUsageReportsResponse extends jspb.Message { + getBatchUuid(): Uint8Array | string; + getBatchUuid_asU8(): Uint8Array; + getBatchUuid_asB64(): string; + setBatchUuid(value: Uint8Array | string): SubmitUsageReportsResponse; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SubmitUsageReportsResponse.AsObject; + static toObject(includeInstance: boolean, msg: SubmitUsageReportsResponse): SubmitUsageReportsResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: SubmitUsageReportsResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsResponse; + static deserializeBinaryFromReader(message: SubmitUsageReportsResponse, reader: jspb.BinaryReader): SubmitUsageReportsResponse; +} + +export namespace SubmitUsageReportsResponse { + export type AsObject = { + batchUuid: Uint8Array | string, + } +} + export enum ResourceKind { RESOURCE_KIND_UNSPECIFIED = 0, RESOURCE_KIND_NODE = 1, diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.js b/gen/proto/js/prehog/v1alpha/teleport_pb.js index 0000f91125da6..74cc68dad2c94 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.js +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.js @@ -40,6 +40,8 @@ goog.exportSymbol('proto.prehog.v1alpha.SubmitEventRequest.EventCase', null, glo goog.exportSymbol('proto.prehog.v1alpha.SubmitEventResponse', null, global); goog.exportSymbol('proto.prehog.v1alpha.SubmitEventsRequest', null, global); goog.exportSymbol('proto.prehog.v1alpha.SubmitEventsResponse', null, global); +goog.exportSymbol('proto.prehog.v1alpha.SubmitUsageReportsRequest', null, global); +goog.exportSymbol('proto.prehog.v1alpha.SubmitUsageReportsResponse', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIBannerClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UICreateNewRoleCancelClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UICreateNewRoleClickEvent', null, global); @@ -65,6 +67,8 @@ goog.exportSymbol('proto.prehog.v1alpha.UIOnboardSetCredentialSubmitEvent', null goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesContinueClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesCopyClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesPrintClickEvent', null, global); +goog.exportSymbol('proto.prehog.v1alpha.UserActivityRecord', null, global); +goog.exportSymbol('proto.prehog.v1alpha.UserActivityReport', null, global); goog.exportSymbol('proto.prehog.v1alpha.UserCertificateIssuedEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UserLoginEvent', null, global); /** @@ -991,6 +995,90 @@ if (goog.DEBUG && !COMPILED) { */ proto.prehog.v1alpha.HelloTeleportResponse.displayName = 'proto.prehog.v1alpha.HelloTeleportResponse'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1alpha.UserActivityReport = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1alpha.UserActivityReport.repeatedFields_, null); +}; +goog.inherits(proto.prehog.v1alpha.UserActivityReport, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1alpha.UserActivityReport.displayName = 'proto.prehog.v1alpha.UserActivityReport'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1alpha.UserActivityRecord = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.prehog.v1alpha.UserActivityRecord, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1alpha.UserActivityRecord.displayName = 'proto.prehog.v1alpha.UserActivityRecord'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1alpha.SubmitUsageReportsRequest.repeatedFields_, null); +}; +goog.inherits(proto.prehog.v1alpha.SubmitUsageReportsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1alpha.SubmitUsageReportsRequest.displayName = 'proto.prehog.v1alpha.SubmitUsageReportsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.prehog.v1alpha.SubmitUsageReportsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1alpha.SubmitUsageReportsResponse.displayName = 'proto.prehog.v1alpha.SubmitUsageReportsResponse'; +} @@ -10987,6 +11075,1147 @@ proto.prehog.v1alpha.HelloTeleportResponse.serializeBinaryToWriter = function(me }; + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.prehog.v1alpha.UserActivityReport.repeatedFields_ = [5]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1alpha.UserActivityReport.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1alpha.UserActivityReport} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.UserActivityReport.toObject = function(includeInstance, msg) { + var f, obj = { + reportUuid: msg.getReportUuid_asB64(), + clusterName: msg.getClusterName_asB64(), + reporterHostid: msg.getReporterHostid_asB64(), + startTime: (f = msg.getStartTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), + recordsList: jspb.Message.toObjectList(msg.getRecordsList(), + proto.prehog.v1alpha.UserActivityRecord.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1alpha.UserActivityReport} + */ +proto.prehog.v1alpha.UserActivityReport.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1alpha.UserActivityReport; + return proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1alpha.UserActivityReport} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1alpha.UserActivityReport} + */ +proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setReportUuid(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setClusterName(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setReporterHostid(value); + break; + case 4: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setStartTime(value); + break; + case 5: + var value = new proto.prehog.v1alpha.UserActivityRecord; + reader.readMessage(value,proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader); + msg.addRecords(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1alpha.UserActivityReport} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getReportUuid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getClusterName_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getReporterHostid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getStartTime(); + if (f != null) { + writer.writeMessage( + 4, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } + f = message.getRecordsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 5, + f, + proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes report_uuid = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes report_uuid = 1; + * This is a type-conversion wrapper around `getReportUuid()` + * @return {string} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getReportUuid())); +}; + + +/** + * optional bytes report_uuid = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getReportUuid()` + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getReportUuid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this + */ +proto.prehog.v1alpha.UserActivityReport.prototype.setReportUuid = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes cluster_name = 2; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes cluster_name = 2; + * This is a type-conversion wrapper around `getClusterName()` + * @return {string} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getClusterName())); +}; + + +/** + * optional bytes cluster_name = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getClusterName()` + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getClusterName())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this + */ +proto.prehog.v1alpha.UserActivityReport.prototype.setClusterName = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes reporter_hostid = 3; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes reporter_hostid = 3; + * This is a type-conversion wrapper around `getReporterHostid()` + * @return {string} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getReporterHostid())); +}; + + +/** + * optional bytes reporter_hostid = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getReporterHostid()` + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getReporterHostid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this + */ +proto.prehog.v1alpha.UserActivityReport.prototype.setReporterHostid = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional google.protobuf.Timestamp start_time = 4; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getStartTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 4)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this +*/ +proto.prehog.v1alpha.UserActivityReport.prototype.setStartTime = function(value) { + return jspb.Message.setWrapperField(this, 4, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this + */ +proto.prehog.v1alpha.UserActivityReport.prototype.clearStartTime = function() { + return this.setStartTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.hasStartTime = function() { + return jspb.Message.getField(this, 4) != null; +}; + + +/** + * repeated UserActivityRecord records = 5; + * @return {!Array} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.getRecordsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1alpha.UserActivityRecord, 5)); +}; + + +/** + * @param {!Array} value + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this +*/ +proto.prehog.v1alpha.UserActivityReport.prototype.setRecordsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 5, value); +}; + + +/** + * @param {!proto.prehog.v1alpha.UserActivityRecord=} opt_value + * @param {number=} opt_index + * @return {!proto.prehog.v1alpha.UserActivityRecord} + */ +proto.prehog.v1alpha.UserActivityReport.prototype.addRecords = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.prehog.v1alpha.UserActivityRecord, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.prehog.v1alpha.UserActivityReport} returns this + */ +proto.prehog.v1alpha.UserActivityReport.prototype.clearRecordsList = function() { + return this.setRecordsList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1alpha.UserActivityRecord.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1alpha.UserActivityRecord} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.UserActivityRecord.toObject = function(includeInstance, msg) { + var f, obj = { + userName: msg.getUserName_asB64(), + logins: jspb.Message.getFieldWithDefault(msg, 2, 0), + sshSessions: jspb.Message.getFieldWithDefault(msg, 3, 0), + appSessions: jspb.Message.getFieldWithDefault(msg, 4, 0), + kubeSessions: jspb.Message.getFieldWithDefault(msg, 5, 0), + dbSessions: jspb.Message.getFieldWithDefault(msg, 6, 0), + desktopSessions: jspb.Message.getFieldWithDefault(msg, 7, 0), + appTcpSessions: jspb.Message.getFieldWithDefault(msg, 8, 0), + sshPortSessions: jspb.Message.getFieldWithDefault(msg, 9, 0), + kubeRequests: jspb.Message.getFieldWithDefault(msg, 10, 0), + sftpEvents: jspb.Message.getFieldWithDefault(msg, 11, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1alpha.UserActivityRecord} + */ +proto.prehog.v1alpha.UserActivityRecord.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1alpha.UserActivityRecord; + return proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1alpha.UserActivityRecord} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1alpha.UserActivityRecord} + */ +proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setUserName(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setLogins(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSshSessions(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint64()); + msg.setAppSessions(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setKubeSessions(value); + break; + case 6: + var value = /** @type {number} */ (reader.readUint64()); + msg.setDbSessions(value); + break; + case 7: + var value = /** @type {number} */ (reader.readUint64()); + msg.setDesktopSessions(value); + break; + case 8: + var value = /** @type {number} */ (reader.readUint64()); + msg.setAppTcpSessions(value); + break; + case 9: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSshPortSessions(value); + break; + case 10: + var value = /** @type {number} */ (reader.readUint64()); + msg.setKubeRequests(value); + break; + case 11: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSftpEvents(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1alpha.UserActivityRecord} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserName_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getLogins(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getSshSessions(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getAppSessions(); + if (f !== 0) { + writer.writeUint64( + 4, + f + ); + } + f = message.getKubeSessions(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } + f = message.getDbSessions(); + if (f !== 0) { + writer.writeUint64( + 6, + f + ); + } + f = message.getDesktopSessions(); + if (f !== 0) { + writer.writeUint64( + 7, + f + ); + } + f = message.getAppTcpSessions(); + if (f !== 0) { + writer.writeUint64( + 8, + f + ); + } + f = message.getSshPortSessions(); + if (f !== 0) { + writer.writeUint64( + 9, + f + ); + } + f = message.getKubeRequests(); + if (f !== 0) { + writer.writeUint64( + 10, + f + ); + } + f = message.getSftpEvents(); + if (f !== 0) { + writer.writeUint64( + 11, + f + ); + } +}; + + +/** + * optional bytes user_name = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes user_name = 1; + * This is a type-conversion wrapper around `getUserName()` + * @return {string} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getUserName())); +}; + + +/** + * optional bytes user_name = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getUserName()` + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getUserName())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setUserName = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional uint64 logins = 2; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getLogins = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setLogins = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 ssh_sessions = 3; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getSshSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setSshSessions = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint64 app_sessions = 4; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getAppSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setAppSessions = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional uint64 kube_sessions = 5; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getKubeSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setKubeSessions = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional uint64 db_sessions = 6; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getDbSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setDbSessions = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional uint64 desktop_sessions = 7; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getDesktopSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setDesktopSessions = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + +/** + * optional uint64 app_tcp_sessions = 8; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getAppTcpSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setAppTcpSessions = function(value) { + return jspb.Message.setProto3IntField(this, 8, value); +}; + + +/** + * optional uint64 ssh_port_sessions = 9; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getSshPortSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setSshPortSessions = function(value) { + return jspb.Message.setProto3IntField(this, 9, value); +}; + + +/** + * optional uint64 kube_requests = 10; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getKubeRequests = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setKubeRequests = function(value) { + return jspb.Message.setProto3IntField(this, 10, value); +}; + + +/** + * optional uint64 sftp_events = 11; + * @return {number} + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.getSftpEvents = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this + */ +proto.prehog.v1alpha.UserActivityRecord.prototype.setSftpEvents = function(value) { + return jspb.Message.setProto3IntField(this, 11, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1alpha.SubmitUsageReportsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + userActivityList: jspb.Message.toObjectList(msg.getUserActivityList(), + proto.prehog.v1alpha.UserActivityReport.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1alpha.SubmitUsageReportsRequest; + return proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.prehog.v1alpha.UserActivityReport; + reader.readMessage(value,proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader); + msg.addUserActivity(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1alpha.SubmitUsageReportsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserActivityList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated UserActivityReport user_activity = 1; + * @return {!Array} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.getUserActivityList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1alpha.UserActivityReport, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} returns this +*/ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.setUserActivityList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.prehog.v1alpha.UserActivityReport=} opt_value + * @param {number=} opt_index + * @return {!proto.prehog.v1alpha.UserActivityReport} + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.addUserActivity = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.prehog.v1alpha.UserActivityReport, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} returns this + */ +proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.clearUserActivityList = function() { + return this.setUserActivityList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1alpha.SubmitUsageReportsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + batchUuid: msg.getBatchUuid_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1alpha.SubmitUsageReportsResponse; + return proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBatchUuid(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1alpha.SubmitUsageReportsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBatchUuid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes batch_uuid = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes batch_uuid = 1; + * This is a type-conversion wrapper around `getBatchUuid()` + * @return {string} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBatchUuid())); +}; + + +/** + * optional bytes batch_uuid = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBatchUuid()` + * @return {!Uint8Array} + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBatchUuid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} returns this + */ +proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.setBatchUuid = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + /** * @enum {number} */ diff --git a/proto/prehog/v1alpha/teleport.proto b/proto/prehog/v1alpha/teleport.proto index 64a6bdea0875b..eab9c44fe58a1 100644 --- a/proto/prehog/v1alpha/teleport.proto +++ b/proto/prehog/v1alpha/teleport.proto @@ -422,10 +422,62 @@ message SubmitEventsResponse {} message HelloTeleportRequest {} message HelloTeleportResponse {} +message UserActivityReport { + // randomly generated UUID for this specific report, 16 bytes (in string order) + bytes report_uuid = 1; + // anonymized, 32 bytes (HMAC-SHA-256) + bytes cluster_name = 2; + // anonymized, 32 bytes (HMAC-SHA-256) + bytes reporter_hostid = 3; + // beginning of the time window for this data; ending is not specified but is + // intended to be at most 15 minutes + google.protobuf.Timestamp start_time = 4; + + repeated UserActivityRecord records = 5; +} + +message UserActivityRecord { + // anonymized, 32 bytes (HMAC-SHA-256) + bytes user_name = 1; + + // counter of user.login events + uint64 logins = 2; + + // counter of session.start events (non-Kube) + uint64 ssh_sessions = 3; + // counter of app.session.start events (non-TCP) + uint64 app_sessions = 4; + // counter of session.start events (only Kube) + uint64 kube_sessions = 5; + // counter of db.session.start events + uint64 db_sessions = 6; + // counter of windows.desktop.session.start events + uint64 desktop_sessions = 7; + // counter of app.session.start events (only TCP) + uint64 app_tcp_sessions = 8; + // counter of port events + uint64 ssh_port_sessions = 9; + + // counter of kube.request events + uint64 kube_requests = 10; + // counter of sftp events + uint64 sftp_events = 11; +} + +message SubmitUsageReportsRequest { + repeated UserActivityReport user_activity = 1; +} +message SubmitUsageReportsResponse { + // randomly generated UUID for this specific batch, 16 bytes (in string order) + bytes batch_uuid = 1; +} + service TeleportReportingService { rpc SubmitEvent(SubmitEventRequest) returns (SubmitEventResponse) { option deprecated = true; } rpc SubmitEvents(SubmitEventsRequest) returns (SubmitEventsResponse) {} rpc HelloTeleport(HelloTeleportRequest) returns (HelloTeleportResponse) {} + + rpc SubmitUsageReports(SubmitUsageReportsRequest) returns (SubmitUsageReportsResponse) {} } From d3e36b3f6f86b7ed623e99e30f4b0fabc4e95ddb Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 20 Mar 2023 23:22:31 +0100 Subject: [PATCH 03/32] AggregatingUsageReporter --- lib/usagereporter/teleport/usagereporter.go | 21 +- .../teleport/usagereporter_aggregating.go | 332 ++++++++++++++++++ 2 files changed, 345 insertions(+), 8 deletions(-) create mode 100644 lib/usagereporter/teleport/usagereporter_aggregating.go diff --git a/lib/usagereporter/teleport/usagereporter.go b/lib/usagereporter/teleport/usagereporter.go index 3dbb998bbdda4..8c45b26acf8ad 100644 --- a/lib/usagereporter/teleport/usagereporter.go +++ b/lib/usagereporter/teleport/usagereporter.go @@ -74,9 +74,9 @@ type UsageReporter interface { AnonymizeAndSubmit(event ...Anonymizable) } -// TeleportUsageReporter submits Teleport usage events -// anonymized with the cluster name. -type TeleportUsageReporter struct { +// StreamingUsageReporter submits all Teleport usage events anonymized with the +// cluster name, with a very short buffer for batches and no persistency. +type StreamingUsageReporter struct { // usageReporter is an actual reporter that batches and sends events usageReporter *usagereporter.UsageReporter[prehogv1.SubmitEventRequest] // anonymizer is the anonymizer used for filtered audit events. @@ -87,9 +87,9 @@ type TeleportUsageReporter struct { clock clockwork.Clock } -var _ UsageReporter = (*TeleportUsageReporter)(nil) +var _ UsageReporter = (*StreamingUsageReporter)(nil) -func (t *TeleportUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { +func (t *StreamingUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { for _, e := range events { req := e.Anonymize(t.anonymizer) req.Timestamp = timestamppb.New(t.clock.Now()) @@ -98,13 +98,18 @@ func (t *TeleportUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { } } -func (t *TeleportUsageReporter) Run(ctx context.Context) { +func (t *StreamingUsageReporter) Run(ctx context.Context) { t.usageReporter.Run(ctx) } type SubmitFunc = usagereporter.SubmitFunc[prehogv1.SubmitEventRequest] -func NewTeleportUsageReporter(log logrus.FieldLogger, clusterName types.ClusterName, submitter SubmitFunc) (*TeleportUsageReporter, error) { +// TODO(espadolini): change the call in e/lib/prehog/prehog.go:InitPreHogUsageReporting +func NewTeleportUsageReporter(log logrus.FieldLogger, clusterName types.ClusterName, submitter SubmitFunc) (*StreamingUsageReporter, error) { + return NewStreamingUsageReporter(log, clusterName, submitter) +} + +func NewStreamingUsageReporter(log logrus.FieldLogger, clusterName types.ClusterName, submitter SubmitFunc) (*StreamingUsageReporter, error) { if log == nil { log = logrus.StandardLogger() } @@ -133,7 +138,7 @@ func NewTeleportUsageReporter(log logrus.FieldLogger, clusterName types.ClusterN Clock: clock, }) - return &TeleportUsageReporter{ + return &StreamingUsageReporter{ usageReporter: reporter, anonymizer: anonymizer, clusterName: clusterName, diff --git a/lib/usagereporter/teleport/usagereporter_aggregating.go b/lib/usagereporter/teleport/usagereporter_aggregating.go new file mode 100644 index 0000000000000..0776c82c2e799 --- /dev/null +++ b/lib/usagereporter/teleport/usagereporter_aggregating.go @@ -0,0 +1,332 @@ +// 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 usagereporter + +import ( + "context" + "sync" + "time" + + "github.com/bufbuild/connect-go" + "github.com/google/uuid" + "github.com/gravitational/trace" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/retryutils" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/v1alphaconnect" + "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/interval" +) + +const aggregateTimeGranularity = 15 * time.Minute + +const userActivityTTL = 60 * 24 * time.Hour + +const ( + userActivityReportsPrefix = "userActivityReports" + userActivityReportsLock = userActivityReportsPrefix +) + +// AggregatingUsageReporter aggregates and persists all user activity events to +// the backend, then periodically submits them. +type AggregatingUsageReporter struct { + anonymizer utils.Anonymizer + backend backend.Backend + client prehogv1c.TeleportReportingServiceClient + + clusterName []byte + reporterHostID []byte + + mu sync.Mutex + startTime time.Time + records map[string]*prehogv1.UserActivityRecord +} + +func NewAggregatingUsageReporter( + ctx context.Context, + anonymizer utils.Anonymizer, + backend backend.Backend, + clusterName string, + reporterHostID string, +) (*AggregatingUsageReporter, error) { + r := &AggregatingUsageReporter{ + anonymizer: anonymizer, + backend: backend, + clusterName: anonymizer.AnonymizeNonEmpty(clusterName), + reporterHostID: anonymizer.AnonymizeNonEmpty(reporterHostID), + } + + go r.runPeriodicSubmit(ctx) + go r.runPeriodicFinalize(ctx) + + return r, nil +} + +var _ UsageReporter = (*AggregatingUsageReporter)(nil) + +// AnonymizeAndSubmit implements [UsageReporter]. +func (r *AggregatingUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { + filtered := events[:0] + for _, event := range events { + switch event.(type) { + case *UserLoginEvent, *SessionStartEvent, *KubeRequestEvent, *SFTPEvent: + filtered = append(filtered, event) + } + } + if len(filtered) == 0 { + return + } + + // this function must not block + go r.anonymizeAndSubmit(filtered) +} + +func (r *AggregatingUsageReporter) anonymizeAndSubmit(events []Anonymizable) { + r.mu.Lock() + defer r.mu.Unlock() + + r.maybeFinalizeReportLocked() + + getRecord := func(userName string) *prehogv1.UserActivityRecord { + record := r.records[userName] + if record == nil { + record = &prehogv1.UserActivityRecord{} + r.records[userName] = record + } + return record + } + + for _, ae := range events { + switch te := ae.(type) { + case *UserLoginEvent: + getRecord(te.UserName).Logins++ + case *SessionStartEvent: + switch te.SessionType { + case string(types.SSHSessionKind): + getRecord(te.UserName).SshSessions++ + case string(types.AppSessionKind): + getRecord(te.UserName).AppSessions++ + case string(types.KubernetesSessionKind): + getRecord(te.UserName).KubeSessions++ + case string(types.DatabaseSessionKind): + getRecord(te.UserName).DbSessions++ + case string(types.WindowsDesktopSessionKind): + getRecord(te.UserName).DesktopSessions++ + case portSessionType: + getRecord(te.UserName).SshPortSessions++ + case tcpSessionType: + getRecord(te.UserName).AppTcpSessions++ + } + case *KubeRequestEvent: + getRecord(te.UserName).KubeRequests++ + case *SFTPEvent: + getRecord(te.UserName).SftpEvents++ + } + } +} + +func (r *AggregatingUsageReporter) maybeFinalizeReport() { + // if r.mu is locked then something has just called + // maybeFinalizeReportLocked anyway + if !r.mu.TryLock() { + return + } + defer r.mu.Unlock() + r.maybeFinalizeReportLocked() +} + +func (r *AggregatingUsageReporter) maybeFinalizeReportLocked() { + now := time.Now().UTC() + + if !r.startTime.Add(aggregateTimeGranularity).Before(now) { + return + } + + startTime := r.startTime + records := r.records + r.startTime = now.Truncate(aggregateTimeGranularity) + r.records = make(map[string]*prehogv1.UserActivityRecord, len(records)) + + if len(records) > 0 { + go r.persistReport(startTime, records) + } +} + +//nolint:unused // will be needed for graceful stopping +func (r *AggregatingUsageReporter) blockingAlwaysFinalizeReport() { + now := time.Now().UTC() + + startTime := r.startTime + r.startTime = now.Truncate(aggregateTimeGranularity) + records := r.records + r.records = make(map[string]*prehogv1.UserActivityRecord, len(records)) + + if len(records) > 0 { + r.persistReport(startTime, records) + } +} + +func (r *AggregatingUsageReporter) persistReport(startTime time.Time, records map[string]*prehogv1.UserActivityRecord) { + if len(records) < 1 { + return + } + + pbRecords := make([]*prehogv1.UserActivityRecord, 0, len(records)) + for userName, record := range records { + record.UserName = r.anonymizer.AnonymizeNonEmpty(userName) + pbRecords = append(pbRecords, record) + } + + pbStartTime := timestamppb.New(startTime) + expiry := startTime.Add(userActivityTTL) + + for len(pbRecords) > 0 { + reportID, wire, rem, err := prepareReport(r.clusterName, r.reporterHostID, pbStartTime, pbRecords) + if err != nil { + // TODO + return + } + pbRecords = rem + + if _, err := r.backend.Put(context.TODO(), backend.Item{ + Key: backend.Key(userActivityReportsPrefix, reportID.String()), + Value: wire, + Expires: expiry, + }); err != nil { + // backend error, can't do much about that + continue + } + } +} + +func prepareReport( + clusterName, reporterHostID []byte, + pbStartTime *timestamppb.Timestamp, + pbRecords []*prehogv1.UserActivityRecord, +) (uuid.UUID, []byte, []*prehogv1.UserActivityRecord, error) { + reportID := uuid.New() + report := &prehogv1.UserActivityReport{ + ReportUuid: reportID[:], + ClusterName: clusterName, + ReporterHostid: reporterHostID, + StartTime: pbStartTime, + Records: pbRecords, + } + + wire, err := proto.Marshal(report) + if err != nil { + return uuid.Nil, nil, pbRecords, trace.Wrap(err) + } + + for len(wire) > 131072 { + if len(report.Records) <= 32 { + return uuid.Nil, nil, pbRecords, trace.LimitExceeded("failed to marshal user activity report within size limit") + } + + report.Records = report.Records[:len(report.Records)/2] + + wire, err = protojson.Marshal(report) + if err != nil { + return uuid.Nil, nil, pbRecords, trace.Wrap(err) + } + } + + return reportID, wire, pbRecords[len(report.Records):], nil +} + +func (r *AggregatingUsageReporter) runPeriodicFinalize(ctx context.Context) { + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + + r.maybeFinalizeReport() + } +} + +func (r *AggregatingUsageReporter) runPeriodicSubmit(ctx context.Context) { + iv := interval.New(interval.Config{ + FirstDuration: utils.HalfJitter(10 * time.Minute), + Duration: 5 * time.Minute, + Jitter: retryutils.NewSeventhJitter(), + }) + defer iv.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-iv.Next(): + } + + r.doSubmit(ctx) + } +} + +func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { + rangeStart := backend.ExactKey(userActivityReportsPrefix) + getResult, err := r.backend.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), 10) + if err != nil { + return + } + if len(getResult.Items) < 1 { + return + } + + reports := make([]*prehogv1.UserActivityReport, 0, len(getResult.Items)) + for _, item := range getResult.Items { + report := new(prehogv1.UserActivityReport) + if err := proto.Unmarshal(item.Value, report); err != nil { + // TODO: delete broken values? just skip and fetch more? if we only + // skip we can get in a situation where the first 10 items are + // broken with low UUIDs, and then we can't submit anything + return + } + } + + if _, err := backend.AcquireLock(ctx, r.backend, userActivityReportsLock, 75*time.Second); err != nil { + // TODO: log that some other auth is already submitting + return + } + + lockCtx, cancel := context.WithTimeout(ctx, 60*time.Second) + defer cancel() + + if _, err = r.client.SubmitUsageReports(lockCtx, + connect.NewRequest(&prehogv1.SubmitUsageReportsRequest{ + UserActivity: reports, + }), + ); err != nil { + // TODO: log + return + } + + for _, item := range getResult.Items { + if err := r.backend.Delete(ctx, item.Key); err != nil { + // TODO: log and try to delete the other items, might as well + continue + } + } +} From 0f3c0b8e0e6a185d3f1ae7692b6891b26a130542 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 22 Mar 2023 21:39:48 +0100 Subject: [PATCH 04/32] graceful stopping --- lib/service/service.go | 11 +- lib/usagereporter/teleport/usagereporter.go | 11 + .../teleport/usagereporter_aggregating.go | 215 +++++++++++------- 3 files changed, 155 insertions(+), 82 deletions(-) diff --git a/lib/service/service.go b/lib/service/service.go index 975f2554bbb87..419607ec15069 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -117,6 +117,7 @@ import ( "github.com/gravitational/teleport/lib/srv/ingress" "github.com/gravitational/teleport/lib/srv/regular" "github.com/gravitational/teleport/lib/system" + usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/cert" "github.com/gravitational/teleport/lib/web" @@ -1828,7 +1829,7 @@ func (process *TeleportProcess) initAuthService() error { } process.RegisterFunc("auth.heartbeat", heartbeat.Run) // execute this when process is asked to exit: - process.OnExit("auth.shutdown", func(payload interface{}) { + process.OnExit("auth.shutdown", func(payload any) { // The listeners have to be closed here, because if shutdown // was called before the start of the http server, // the http server would have not started tracking the listeners @@ -1839,7 +1840,7 @@ func (process *TeleportProcess) initAuthService() error { if listener != nil { warnOnErr(listener.Close(), log) } - if payload == nil { + if ctx, _ := payload.(context.Context); ctx == nil { log.Info("Shutting down immediately.") warnOnErr(tlsServer.Close(), log) } else { @@ -1851,6 +1852,12 @@ func (process *TeleportProcess) initAuthService() error { // such as access workflow plugins from normal users. Without this, a graceful shutdown // of the auth server basically never exits. warnOnErr(tlsServer.Close(), log) + + if g, ok := authServer.Services.UsageReporter.(usagereporter.GracefulStopper); ok { + if err := g.GracefulStop(ctx); err != nil { + log.WithError(err).Warn("Error while gracefully stopping usage reporter.") + } + } } log.Info("Exited.") }) diff --git a/lib/usagereporter/teleport/usagereporter.go b/lib/usagereporter/teleport/usagereporter.go index 8c45b26acf8ad..fe6cc633121b2 100644 --- a/lib/usagereporter/teleport/usagereporter.go +++ b/lib/usagereporter/teleport/usagereporter.go @@ -74,6 +74,16 @@ type UsageReporter interface { AnonymizeAndSubmit(event ...Anonymizable) } +// GracefulStopper is a UsageReporter that needs to do some work before +// stopping; this is a separate interface because [UsageReporter] is embedded in +// auth.Server.Services, and we don't want to expose extraneous methods as part +// of auth.Server. +type GracefulStopper interface { + UsageReporter + + GracefulStop(context.Context) error +} + // StreamingUsageReporter submits all Teleport usage events anonymized with the // cluster name, with a very short buffer for batches and no persistency. type StreamingUsageReporter struct { @@ -206,6 +216,7 @@ type DiscardUsageReporter struct{} var _ UsageReporter = DiscardUsageReporter{} +// AnonymizeAndSubmit implements [UsageReporter] func (DiscardUsageReporter) AnonymizeAndSubmit(...Anonymizable) { // do nothing } diff --git a/lib/usagereporter/teleport/usagereporter_aggregating.go b/lib/usagereporter/teleport/usagereporter_aggregating.go index 0776c82c2e799..bd512acb1ada7 100644 --- a/lib/usagereporter/teleport/usagereporter_aggregating.go +++ b/lib/usagereporter/teleport/usagereporter_aggregating.go @@ -22,14 +22,12 @@ import ( "github.com/bufbuild/connect-go" "github.com/google/uuid" "github.com/gravitational/trace" - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/retryutils" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/v1alphaconnect" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/interval" @@ -37,49 +35,116 @@ import ( const aggregateTimeGranularity = 15 * time.Minute -const userActivityTTL = 60 * 24 * time.Hour - const ( userActivityReportsPrefix = "userActivityReports" - userActivityReportsLock = userActivityReportsPrefix -) + userActivityReportsLock = "userActivityReportsLock" -// AggregatingUsageReporter aggregates and persists all user activity events to -// the backend, then periodically submits them. -type AggregatingUsageReporter struct { - anonymizer utils.Anonymizer - backend backend.Backend - client prehogv1c.TeleportReportingServiceClient + // userActivityReportMaxSize is the maximum size for a backend value; + // dynamodb has a limit of 400KiB per item, we store values in base64, + // there's some framing, and a healthy 50% margin gets us to about 128KiB. + userActivityReportMaxSize = 131072 - clusterName []byte - reporterHostID []byte + userActivityReportTTL = 60 * 24 * time.Hour +) - mu sync.Mutex - startTime time.Time - records map[string]*prehogv1.UserActivityRecord +// userActivityReportKey returns the backend key for a user activity report with +// a given UUID and start time, such that reports with an earlier start time +// will appear earlier in lexicographic ordering. +func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { + return backend.Key(userActivityReportsPrefix, startTime.Format(time.RFC3339), reportUUID.String()) } func NewAggregatingUsageReporter( ctx context.Context, anonymizer utils.Anonymizer, backend backend.Backend, + submitter UsageReportsSubmitter, clusterName string, reporterHostID string, ) (*AggregatingUsageReporter, error) { + baseCtx, baseCancel := context.WithCancel(ctx) + periodicCtx, periodicCancel := context.WithCancel(baseCtx) + r := &AggregatingUsageReporter{ - anonymizer: anonymizer, - backend: backend, + anonymizer: anonymizer, + backend: backend, + submitter: submitter, + clusterName: anonymizer.AnonymizeNonEmpty(clusterName), reporterHostID: anonymizer.AnonymizeNonEmpty(reporterHostID), + + baseCtx: baseCtx, + baseCancel: baseCancel, + periodicCancel: periodicCancel, } - go r.runPeriodicSubmit(ctx) - go r.runPeriodicFinalize(ctx) + r.wg.Add(2) + go r.runPeriodicSubmit(periodicCtx) + go r.runPeriodicFinalize(periodicCtx) return r, nil } -var _ UsageReporter = (*AggregatingUsageReporter)(nil) +type UsageReportsSubmitter interface { + SubmitUsageReports(context.Context, *connect.Request[prehogv1.SubmitUsageReportsRequest]) (*connect.Response[prehogv1.SubmitUsageReportsResponse], error) +} + +// AggregatingUsageReporter aggregates and persists all user activity events to +// the backend, then periodically submits them. +type AggregatingUsageReporter struct { + anonymizer utils.Anonymizer + backend backend.Backend + submitter UsageReportsSubmitter + + clusterName []byte + reporterHostID []byte + + wg sync.WaitGroup + baseCtx context.Context + baseCancel context.CancelFunc + periodicCancel context.CancelFunc + + mu sync.Mutex + stopping bool + startTime time.Time + records map[string]*prehogv1.UserActivityRecord +} + +var _ GracefulStopper = (*AggregatingUsageReporter)(nil) + +// GracefulStop implements [GracefulStopper] +func (r *AggregatingUsageReporter) GracefulStop(ctx context.Context) error { + defer r.baseCancel() + r.periodicCancel() + + r.mu.Lock() + + r.stopping = true + + startTime := r.startTime + records := r.records + // don't hold up memory + r.records = nil + + r.mu.Unlock() + + if len(records) > 0 { + r.wg.Add(1) + go r.persistReport(startTime, records) + } + + go func() { + select { + case <-ctx.Done(): + r.baseCancel() + case <-r.baseCtx.Done(): + } + }() + + r.wg.Wait() + + return r.baseCtx.Err() +} // AnonymizeAndSubmit implements [UsageReporter]. func (r *AggregatingUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { @@ -102,6 +167,10 @@ func (r *AggregatingUsageReporter) anonymizeAndSubmit(events []Anonymizable) { r.mu.Lock() defer r.mu.Unlock() + if r.stopping { + return + } + r.maybeFinalizeReportLocked() getRecord := func(userName string) *prehogv1.UserActivityRecord { @@ -142,16 +211,6 @@ func (r *AggregatingUsageReporter) anonymizeAndSubmit(events []Anonymizable) { } } -func (r *AggregatingUsageReporter) maybeFinalizeReport() { - // if r.mu is locked then something has just called - // maybeFinalizeReportLocked anyway - if !r.mu.TryLock() { - return - } - defer r.mu.Unlock() - r.maybeFinalizeReportLocked() -} - func (r *AggregatingUsageReporter) maybeFinalizeReportLocked() { now := time.Now().UTC() @@ -165,28 +224,13 @@ func (r *AggregatingUsageReporter) maybeFinalizeReportLocked() { r.records = make(map[string]*prehogv1.UserActivityRecord, len(records)) if len(records) > 0 { + r.wg.Add(1) go r.persistReport(startTime, records) } } -//nolint:unused // will be needed for graceful stopping -func (r *AggregatingUsageReporter) blockingAlwaysFinalizeReport() { - now := time.Now().UTC() - - startTime := r.startTime - r.startTime = now.Truncate(aggregateTimeGranularity) - records := r.records - r.records = make(map[string]*prehogv1.UserActivityRecord, len(records)) - - if len(records) > 0 { - r.persistReport(startTime, records) - } -} - func (r *AggregatingUsageReporter) persistReport(startTime time.Time, records map[string]*prehogv1.UserActivityRecord) { - if len(records) < 1 { - return - } + defer r.wg.Done() pbRecords := make([]*prehogv1.UserActivityRecord, 0, len(records)) for userName, record := range records { @@ -195,18 +239,18 @@ func (r *AggregatingUsageReporter) persistReport(startTime time.Time, records ma } pbStartTime := timestamppb.New(startTime) - expiry := startTime.Add(userActivityTTL) + expiry := startTime.Add(userActivityReportTTL) for len(pbRecords) > 0 { - reportID, wire, rem, err := prepareReport(r.clusterName, r.reporterHostID, pbStartTime, pbRecords) + reportUUID, wire, rem, err := prepareReport(r.clusterName, r.reporterHostID, pbStartTime, pbRecords) if err != nil { // TODO return } pbRecords = rem - if _, err := r.backend.Put(context.TODO(), backend.Item{ - Key: backend.Key(userActivityReportsPrefix, reportID.String()), + if _, err := r.backend.Put(r.baseCtx, backend.Item{ + Key: userActivityReportKey(reportUUID, startTime), Value: wire, Expires: expiry, }); err != nil { @@ -218,40 +262,38 @@ func (r *AggregatingUsageReporter) persistReport(startTime time.Time, records ma func prepareReport( clusterName, reporterHostID []byte, - pbStartTime *timestamppb.Timestamp, - pbRecords []*prehogv1.UserActivityRecord, + startTime *timestamppb.Timestamp, + records []*prehogv1.UserActivityRecord, ) (uuid.UUID, []byte, []*prehogv1.UserActivityRecord, error) { - reportID := uuid.New() + reportUUID := uuid.New() report := &prehogv1.UserActivityReport{ - ReportUuid: reportID[:], + ReportUuid: reportUUID[:], ClusterName: clusterName, ReporterHostid: reporterHostID, - StartTime: pbStartTime, - Records: pbRecords, - } - - wire, err := proto.Marshal(report) - if err != nil { - return uuid.Nil, nil, pbRecords, trace.Wrap(err) + StartTime: startTime, + Records: records, } - for len(wire) > 131072 { - if len(report.Records) <= 32 { - return uuid.Nil, nil, pbRecords, trace.LimitExceeded("failed to marshal user activity report within size limit") + for proto.Size(report) > userActivityReportMaxSize { + if len(report.Records) <= 1 { + return uuid.Nil, nil, records, trace.LimitExceeded("failed to marshal user activity report within size limit") } report.Records = report.Records[:len(report.Records)/2] + } - wire, err = protojson.Marshal(report) - if err != nil { - return uuid.Nil, nil, pbRecords, trace.Wrap(err) - } + wire, err := proto.Marshal(report) + if err != nil { + // TODO: sad marshal noises + return uuid.Nil, nil, records, trace.Wrap(err) } - return reportID, wire, pbRecords[len(report.Records):], nil + return reportUUID, wire, records[len(report.Records):], nil } func (r *AggregatingUsageReporter) runPeriodicFinalize(ctx context.Context) { + defer r.wg.Done() + ticker := time.NewTicker(time.Minute) defer ticker.Stop() @@ -262,11 +304,20 @@ func (r *AggregatingUsageReporter) runPeriodicFinalize(ctx context.Context) { case <-ticker.C: } - r.maybeFinalizeReport() + // if r.mu is locked then something has just called + // maybeFinalizeReportLocked anyway + if r.mu.TryLock() { + if !r.stopping { + r.maybeFinalizeReportLocked() + } + r.mu.Unlock() + } } } func (r *AggregatingUsageReporter) runPeriodicSubmit(ctx context.Context) { + defer r.wg.Done() + iv := interval.New(interval.Config{ FirstDuration: utils.HalfJitter(10 * time.Minute), Duration: 5 * time.Minute, @@ -299,22 +350,26 @@ func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { for _, item := range getResult.Items { report := new(prehogv1.UserActivityReport) if err := proto.Unmarshal(item.Value, report); err != nil { - // TODO: delete broken values? just skip and fetch more? if we only - // skip we can get in a situation where the first 10 items are - // broken with low UUIDs, and then we can't submit anything return } } - if _, err := backend.AcquireLock(ctx, r.backend, userActivityReportsLock, 75*time.Second); err != nil { - // TODO: log that some other auth is already submitting + if _, err := r.backend.Create(ctx, backend.Item{ + Key: backend.Key(userActivityReportsLock), + Value: []byte("null"), + Expires: r.backend.Clock().Now().UTC().Add(75 * time.Second), + }); err != nil { + if trace.IsAlreadyExists(err) { + // TODO: log that some other auth is already submitting + _ = 0 + } return } lockCtx, cancel := context.WithTimeout(ctx, 60*time.Second) defer cancel() - if _, err = r.client.SubmitUsageReports(lockCtx, + if _, err = r.submitter.SubmitUsageReports(lockCtx, connect.NewRequest(&prehogv1.SubmitUsageReportsRequest{ UserActivity: reports, }), @@ -326,7 +381,7 @@ func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { for _, item := range getResult.Items { if err := r.backend.Delete(ctx, item.Key); err != nil { // TODO: log and try to delete the other items, might as well - continue + _ = 0 } } } From bf2d392b70297cebec8817bb5b67ca4105596a15 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 22 Mar 2023 21:57:09 +0100 Subject: [PATCH 05/32] alerts WIP --- .../teleport/usagereporter_aggregating.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/usagereporter/teleport/usagereporter_aggregating.go b/lib/usagereporter/teleport/usagereporter_aggregating.go index bd512acb1ada7..64694dd796e87 100644 --- a/lib/usagereporter/teleport/usagereporter_aggregating.go +++ b/lib/usagereporter/teleport/usagereporter_aggregating.go @@ -29,6 +29,7 @@ import ( "github.com/gravitational/teleport/api/utils/retryutils" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/interval" ) @@ -59,6 +60,7 @@ func NewAggregatingUsageReporter( anonymizer utils.Anonymizer, backend backend.Backend, submitter UsageReportsSubmitter, + status services.StatusInternal, clusterName string, reporterHostID string, ) (*AggregatingUsageReporter, error) { @@ -69,6 +71,7 @@ func NewAggregatingUsageReporter( anonymizer: anonymizer, backend: backend, submitter: submitter, + status: status, clusterName: anonymizer.AnonymizeNonEmpty(clusterName), reporterHostID: anonymizer.AnonymizeNonEmpty(reporterHostID), @@ -95,6 +98,7 @@ type AggregatingUsageReporter struct { anonymizer utils.Anonymizer backend backend.Backend submitter UsageReportsSubmitter + status services.StatusInternal clusterName []byte reporterHostID []byte @@ -343,6 +347,7 @@ func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { return } if len(getResult.Items) < 1 { + _ = r.status.DeleteClusterAlert(ctx, "reporting-failed") return } @@ -374,6 +379,17 @@ func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { UserActivity: reports, }), ); err != nil { + if time.Since(reports[0].StartTime.AsTime()) > time.Hour*24 { + alert, err := types.NewClusterAlert( + "reporting-failed", + "Failed to sync usage data for over 24 hours", + types.WithAlertLabel(types.AlertOnLogin, "yes"), + types.WithAlertLabel(types.AlertPermitAll, "yes"), + ) + if err == nil { + _ = r.status.UpsertClusterAlert(ctx, alert) + } + } // TODO: log return } From 094217526138b01c407d6ea932e95277e15d8aba Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 11:18:33 +0200 Subject: [PATCH 06/32] Split reporter and submitter --- .../teleport/aggregating/reporter.go | 355 +++++++++++++++ .../teleport/aggregating/submitter.go | 198 +++++++++ lib/usagereporter/teleport/audit.go | 12 +- .../teleport/usagereporter_aggregating.go | 403 ------------------ 4 files changed, 559 insertions(+), 409 deletions(-) create mode 100644 lib/usagereporter/teleport/aggregating/reporter.go create mode 100644 lib/usagereporter/teleport/aggregating/submitter.go delete mode 100644 lib/usagereporter/teleport/usagereporter_aggregating.go diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go new file mode 100644 index 0000000000000..616553f776479 --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -0,0 +1,355 @@ +// 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 aggregating + +import ( + "context" + "sync" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport/api/types" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + "github.com/gravitational/teleport/lib/backend" + usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" + "github.com/gravitational/teleport/lib/utils" +) + +const aggregateTimeGranularity = 15 * time.Minute + +const ( + userActivityReportsPrefix = "userActivityReports" + userActivityReportsLock = "userActivityReportsLock" + + // maxSize is the maximum size for a backend value; dynamodb has a limit of + // 400KiB per item, we store values in base64, there's some framing, and a + // healthy 50% margin gets us to about 128KiB. + maxSize = 131072 + + reportTTL = 60 * 24 * time.Hour +) + +// userActivityReportKey returns the backend key for a user activity report with +// a given UUID and start time, such that reports with an earlier start time +// will appear earlier in lexicographic ordering. +func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { + return backend.Key(userActivityReportsPrefix, startTime.Format(time.RFC3339), reportUUID.String()) +} + +type ReporterConfig struct { + Anonymizer utils.Anonymizer + Backend backend.Backend + Log logrus.FieldLogger + + ClusterName string + ReporterHostID string +} + +func (cfg *ReporterConfig) CheckAndSetDefaults() error { + if cfg.Anonymizer == nil { + return trace.BadParameter("missing Anonymizer") + } + if cfg.Backend == nil { + return trace.BadParameter("missing Backend") + } + if cfg.Log == nil { + return trace.BadParameter("missing Log") + } + if cfg.ClusterName == "" { + return trace.BadParameter("missing ClusterName") + } + if cfg.ReporterHostID == "" { + return trace.BadParameter("missing ReporterHostID") + } + return nil +} + +// NewReporter returns a new running [Reporter]. To avoid resource leaks, +// GracefulStop must be called or the base context must be closed. The context +// will be used for all backend operations. +func NewReporter( + ctx context.Context, + cfg ReporterConfig, +) (*Reporter, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + baseCtx, baseCancel := context.WithCancel(ctx) + periodicCtx, periodicCancel := context.WithCancel(baseCtx) + + r := &Reporter{ + anonymizer: cfg.Anonymizer, + backend: cfg.Backend, + log: cfg.Log, + + clusterName: cfg.Anonymizer.AnonymizeNonEmpty(cfg.ClusterName), + reporterHostID: cfg.Anonymizer.AnonymizeNonEmpty(cfg.ReporterHostID), + + baseCtx: baseCtx, + baseCancel: baseCancel, + periodicCancel: periodicCancel, + } + + r.wg.Add(1) + go r.runPeriodicFinalize(periodicCtx) + + return r, nil +} + +// Reporter aggregates and persists usage events to the backend. +type Reporter struct { + anonymizer utils.Anonymizer + backend backend.Backend + log logrus.FieldLogger + + clusterName []byte + reporterHostID []byte + + wg sync.WaitGroup + baseCtx context.Context + baseCancel context.CancelFunc + periodicCancel context.CancelFunc + + mu sync.Mutex + stopped bool + startTime time.Time + userActivity map[string]*prehogv1.UserActivityRecord +} + +var _ usagereporter.GracefulStopper = (*Reporter)(nil) + +// GracefulStop implements [ur.GracefulStopper]. Can only be called at most once. +func (r *Reporter) GracefulStop(ctx context.Context) error { + r.periodicCancel() + + r.mu.Lock() + + r.stopped = true + + startTime := r.startTime + userActivity := r.userActivity + // release memory + r.userActivity = nil + + r.mu.Unlock() + + if len(userActivity) > 0 { + // we're allowed to do this because we know that we'll only Wait on r.wg + // later on in this function's body; anywhere else we're not allowed to + // Add while r.stopped is true, as Wait can't be called concurrently + // with an Add from 0, and if we began shutting down we can't assume + // that the count is above 0 + r.wg.Add(1) + go r.persistUserActivity(startTime, userActivity) + } + + go func() { + select { + case <-ctx.Done(): + r.baseCancel() + case <-r.baseCtx.Done(): + } + }() + + r.wg.Wait() + r.baseCancel() + return r.baseCtx.Err() +} + +// AnonymizeAndSubmit implements [ur.UsageReporter]. +func (r *Reporter) AnonymizeAndSubmit(events ...usagereporter.Anonymizable) { + filtered := events[:0] + for _, event := range events { + // this should drop all events that we don't care about + switch event.(type) { + case *usagereporter.UserLoginEvent, + *usagereporter.SessionStartEvent, + *usagereporter.KubeRequestEvent, + *usagereporter.SFTPEvent: + filtered = append(filtered, event) + } + } + if len(filtered) == 0 { + return + } + + // this function must not block + go r.anonymizeAndSubmit(filtered) +} + +func (r *Reporter) anonymizeAndSubmit(events []usagereporter.Anonymizable) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.stopped { + return + } + + r.maybeFinalizeReportsLocked() + + userRecord := func(userName string) *prehogv1.UserActivityRecord { + record := r.userActivity[userName] + if record == nil { + record = &prehogv1.UserActivityRecord{} + r.userActivity[userName] = record + } + return record + } + + for _, ae := range events { + switch te := ae.(type) { + case *usagereporter.UserLoginEvent: + userRecord(te.UserName).Logins++ + case *usagereporter.SessionStartEvent: + switch te.SessionType { + case string(types.SSHSessionKind): + userRecord(te.UserName).SshSessions++ + case string(types.AppSessionKind): + userRecord(te.UserName).AppSessions++ + case string(types.KubernetesSessionKind): + userRecord(te.UserName).KubeSessions++ + case string(types.DatabaseSessionKind): + userRecord(te.UserName).DbSessions++ + case string(types.WindowsDesktopSessionKind): + userRecord(te.UserName).DesktopSessions++ + case usagereporter.PortSessionType: + userRecord(te.UserName).SshPortSessions++ + case usagereporter.TCPSessionType: + userRecord(te.UserName).AppTcpSessions++ + } + case *usagereporter.KubeRequestEvent: + userRecord(te.UserName).KubeRequests++ + case *usagereporter.SFTPEvent: + userRecord(te.UserName).SftpEvents++ + } + } +} + +func (r *Reporter) maybeFinalizeReportsLocked() { + now := time.Now().UTC() + + if !r.startTime.Add(aggregateTimeGranularity).Before(now) { + return + } + + startTime := r.startTime + userActivity := r.userActivity + r.startTime = now.Truncate(aggregateTimeGranularity) + // we expect the amount of users to not vary a lot between one time window + // and the next + r.userActivity = make(map[string]*prehogv1.UserActivityRecord, len(userActivity)) + + if len(userActivity) > 0 { + r.wg.Add(1) + go r.persistUserActivity(startTime, userActivity) + } +} + +func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[string]*prehogv1.UserActivityRecord) { + defer r.wg.Done() + + records := make([]*prehogv1.UserActivityRecord, 0, len(userActivity)) + for userName, record := range userActivity { + record.UserName = r.anonymizer.AnonymizeNonEmpty(userName) + records = append(records, record) + } + + pbStartTime := timestamppb.New(startTime) + expiry := startTime.Add(reportTTL) + + for len(records) > 0 { + reportUUID, data, rem, err := prepareUserActivityReport(r.clusterName, r.reporterHostID, pbStartTime, records) + if err != nil { + r.log.WithError(err).WithFields(logrus.Fields{ + "start_time": startTime, + "lost_records": len(records), + }).Error("Failed to prepare user activity report, dropping data.") + return + } + reportRecords := len(records) - len(rem) + records = rem + + if _, err := r.backend.Put(r.baseCtx, backend.Item{ + Key: userActivityReportKey(reportUUID, startTime), + Value: data, + Expires: expiry, + }); err != nil { + r.log.WithError(err).WithFields(logrus.Fields{ + "start_time": startTime, + "lost_records": reportRecords, + }).Error("Failed to persist user activity report, dropping data.") + } + } +} + +func prepareUserActivityReport( + clusterName, reporterHostID []byte, + startTime *timestamppb.Timestamp, + records []*prehogv1.UserActivityRecord, +) (reportUUID uuid.UUID, data []byte, remaining []*prehogv1.UserActivityRecord, err error) { + reportUUID = uuid.New() + report := &prehogv1.UserActivityReport{ + ReportUuid: reportUUID[:], + ClusterName: clusterName, + ReporterHostid: reporterHostID, + StartTime: startTime, + Records: records, + } + + for proto.Size(report) > maxSize { + if len(report.Records) <= 1 { + return uuid.Nil, nil, nil, trace.LimitExceeded("failed to marshal user activity report within size limit (this is a bug)") + } + + report.Records = report.Records[:len(report.Records)/2] + } + + wire, err := proto.Marshal(report) + if err != nil { + return uuid.Nil, nil, nil, trace.Wrap(err) + } + + return reportUUID, wire, records[len(report.Records):], nil +} + +func (r *Reporter) runPeriodicFinalize(ctx context.Context) { + defer r.wg.Done() + + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + + // if r.mu is locked then something has just called + // maybeFinalizeReportLocked anyway + if r.mu.TryLock() { + if !r.stopped { + r.maybeFinalizeReportsLocked() + } + r.mu.Unlock() + } + } +} diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go new file mode 100644 index 0000000000000..ccd5d84d23322 --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -0,0 +1,198 @@ +// 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 aggregating + +import ( + "context" + "fmt" + "time" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/retryutils" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/interval" +) + +// UsageReportsSubmitter is almost SubmitUsageReports from +// prehog.v1alpha.TeleportReportingService, but instead of returning a response +// (and requiring wrapping the request in a [connect.Request]) it should do its +// own error checking on the response. +type UsageReportsSubmitter func(context.Context, *prehogv1.SubmitUsageReportsRequest) error + +const ( + submitInterval = 5 * time.Minute + submitLockDuration = time.Minute + submitBatch = 10 + + alertGraceHours = 24 + alertGraceDuration = alertGraceHours * time.Hour + alertName = "reporting-failed" +) + +const DefaultEndpoint = "https://reporting-teleport.teleportinfra.sh" + +var alertMessage = fmt.Sprintf("Teleport has failed to contact the usage reporting server for more than %v hours. "+ + "Please make sure that the Teleport Auth Server can connect to (%v). "+ + "Otherwise, contact Teleport Support at (https://support.goteleport.com).", + alertGraceHours, DefaultEndpoint) + +type SubmitterConfig struct { + Backend backend.Backend + Log logrus.FieldLogger + Status services.StatusInternal + Submitter UsageReportsSubmitter +} + +func (cfg *SubmitterConfig) CheckAndSetDefaults() error { + if cfg.Backend == nil { + return trace.BadParameter("missing Backend") + } + if cfg.Log == nil { + return trace.BadParameter("missing Log") + } + if cfg.Status == nil { + return trace.BadParameter("missing Status") + } + if cfg.Submitter == nil { + return trace.BadParameter("missing Submitter") + } + return nil +} + +// RunSubmitter periodically fetches aggregated reports from the backend and +// sends them in small batches with the provided submitter, before deleting +// them. Must only be called after validating the config object with +// CheckAndSetDefaults, and should probably be called in a goroutine. +func RunSubmitter(ctx context.Context, cfg SubmitterConfig) { + iv := interval.New(interval.Config{ + FirstDuration: utils.HalfJitter(2 * submitInterval), + Duration: submitInterval, + Jitter: retryutils.NewSeventhJitter(), + }) + defer iv.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-iv.Next(): + } + + submitOnce(ctx, cfg) + } +} + +func submitOnce(ctx context.Context, c SubmitterConfig) { + rangeStart := backend.ExactKey(userActivityReportsPrefix) + getResult, err := c.Backend.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), submitBatch) + if err != nil { + c.Log.WithError(err).Errorf("Failed to load usage reports for submission.") + return + } + + if len(getResult.Items) < 1 { + if _, err := c.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ + AlertID: alertName, + }); err != nil && trace.IsNotFound(err) { + // if we can confirm that there's no cluster alert we go ahead and + // exit here without attempting the delete (reads are cheaper than + // writes) + return + } + err := c.Status.DeleteClusterAlert(ctx, alertName) + if err == nil { + c.Log.Infof("Deleted cluster alert %v after successfully clearing usage report backlog.", alertName) + } else if !trace.IsNotFound(err) { + c.Log.WithError(err).Errorf("Failed to delete cluster alert %v.", alertName) + } + return + } + + reports := make([]*prehogv1.UserActivityReport, 0, len(getResult.Items)) + for _, item := range getResult.Items { + report := &prehogv1.UserActivityReport{} + if err := proto.Unmarshal(item.Value, report); err != nil { + c.Log.WithError(err).WithField("key", item.Key).Error("Failed to parse usage report from the backend.") + return + } + reports = append(reports, report) + } + + if _, err := c.Backend.Create(ctx, backend.Item{ + Key: backend.Key(userActivityReportsLock), + Value: []byte("null"), + Expires: c.Backend.Clock().Now().UTC().Add(submitLockDuration), + }); err != nil { + if trace.IsAlreadyExists(err) { + c.Log.Debugf("Failed to acquire lock %v, already held.", userActivityReportsLock) + } else { + c.Log.WithError(err).Errorf("Failed to acquire lock %v.", userActivityReportsLock) + } + return + } + + lockCtx, cancel := context.WithTimeout(ctx, submitLockDuration*9/10) + defer cancel() + + if err := c.Submitter(lockCtx, &prehogv1.SubmitUsageReportsRequest{ + UserActivity: reports, + }); err != nil { + c.Log.WithError(err).WithFields(logrus.Fields{ + "reports": len(reports), + "oldest_report": reports[0].GetStartTime().AsTime(), + "newest_report": reports[len(reports)-1].GetStartTime().AsTime(), + }).Errorf("Failed to send usage reports.") + + if time.Since(reports[0].StartTime.AsTime()) <= alertGraceDuration { + return + } + alert, err := types.NewClusterAlert( + alertName, + alertMessage, + types.WithAlertLabel(types.AlertOnLogin, "yes"), + types.WithAlertLabel(types.AlertPermitAll, "yes"), + ) + if err != nil { + c.Log.WithError(err).Errorf("Failed to create cluster alert %v.", alertName) + return + } + if err := c.Status.UpsertClusterAlert(ctx, alert); err != nil { + c.Log.WithError(err).Errorf("Failed to upsert cluster alert %v.", alertName) + } + return + } + + c.Log.WithFields(logrus.Fields{ + "reports": len(reports), + "oldest_report": reports[0].GetStartTime().AsTime(), + }).Info("Successfully sent usage reports.") + + var lastErr error + for _, item := range getResult.Items { + if err := c.Backend.Delete(ctx, item.Key); err != nil { + lastErr = err + } + } + if lastErr != nil { + c.Log.WithField("last_error", lastErr).Warn("Failed to delete some usage reports after successful send.") + } +} diff --git a/lib/usagereporter/teleport/audit.go b/lib/usagereporter/teleport/audit.go index b3ac0331a18da..13845eb2cdded 100644 --- a/lib/usagereporter/teleport/audit.go +++ b/lib/usagereporter/teleport/audit.go @@ -22,12 +22,12 @@ import ( ) const ( - // tcpSessionType is the session_type in tp.session.start for TCP + // TCPSessionType is the session_type in tp.session.start for TCP // Application Access. - tcpSessionType = "app_tcp" - // portSessionType is the session_type in tp.session.start for SSH port + TCPSessionType = "app_tcp" + // PortSessionType is the session_type in tp.session.start for SSH port // forwarding. - portSessionType = "ssh_port" + PortSessionType = "ssh_port" ) func ConvertAuditEvent(event apievents.AuditEvent) Anonymizable { @@ -66,7 +66,7 @@ func ConvertAuditEvent(event apievents.AuditEvent) Anonymizable { case *apievents.PortForward: return &SessionStartEvent{ UserName: e.User, - SessionType: portSessionType, + SessionType: PortSessionType, } case *apievents.DatabaseSessionStart: return &SessionStartEvent{ @@ -76,7 +76,7 @@ func ConvertAuditEvent(event apievents.AuditEvent) Anonymizable { case *apievents.AppSessionStart: sessionType := string(types.AppSessionKind) if types.IsAppTCP(e.AppURI) { - sessionType = tcpSessionType + sessionType = TCPSessionType } return &SessionStartEvent{ UserName: e.User, diff --git a/lib/usagereporter/teleport/usagereporter_aggregating.go b/lib/usagereporter/teleport/usagereporter_aggregating.go deleted file mode 100644 index 64694dd796e87..0000000000000 --- a/lib/usagereporter/teleport/usagereporter_aggregating.go +++ /dev/null @@ -1,403 +0,0 @@ -// 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 usagereporter - -import ( - "context" - "sync" - "time" - - "github.com/bufbuild/connect-go" - "github.com/google/uuid" - "github.com/gravitational/trace" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/api/utils/retryutils" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - "github.com/gravitational/teleport/lib/backend" - "github.com/gravitational/teleport/lib/services" - "github.com/gravitational/teleport/lib/utils" - "github.com/gravitational/teleport/lib/utils/interval" -) - -const aggregateTimeGranularity = 15 * time.Minute - -const ( - userActivityReportsPrefix = "userActivityReports" - userActivityReportsLock = "userActivityReportsLock" - - // userActivityReportMaxSize is the maximum size for a backend value; - // dynamodb has a limit of 400KiB per item, we store values in base64, - // there's some framing, and a healthy 50% margin gets us to about 128KiB. - userActivityReportMaxSize = 131072 - - userActivityReportTTL = 60 * 24 * time.Hour -) - -// userActivityReportKey returns the backend key for a user activity report with -// a given UUID and start time, such that reports with an earlier start time -// will appear earlier in lexicographic ordering. -func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { - return backend.Key(userActivityReportsPrefix, startTime.Format(time.RFC3339), reportUUID.String()) -} - -func NewAggregatingUsageReporter( - ctx context.Context, - anonymizer utils.Anonymizer, - backend backend.Backend, - submitter UsageReportsSubmitter, - status services.StatusInternal, - clusterName string, - reporterHostID string, -) (*AggregatingUsageReporter, error) { - baseCtx, baseCancel := context.WithCancel(ctx) - periodicCtx, periodicCancel := context.WithCancel(baseCtx) - - r := &AggregatingUsageReporter{ - anonymizer: anonymizer, - backend: backend, - submitter: submitter, - status: status, - - clusterName: anonymizer.AnonymizeNonEmpty(clusterName), - reporterHostID: anonymizer.AnonymizeNonEmpty(reporterHostID), - - baseCtx: baseCtx, - baseCancel: baseCancel, - periodicCancel: periodicCancel, - } - - r.wg.Add(2) - go r.runPeriodicSubmit(periodicCtx) - go r.runPeriodicFinalize(periodicCtx) - - return r, nil -} - -type UsageReportsSubmitter interface { - SubmitUsageReports(context.Context, *connect.Request[prehogv1.SubmitUsageReportsRequest]) (*connect.Response[prehogv1.SubmitUsageReportsResponse], error) -} - -// AggregatingUsageReporter aggregates and persists all user activity events to -// the backend, then periodically submits them. -type AggregatingUsageReporter struct { - anonymizer utils.Anonymizer - backend backend.Backend - submitter UsageReportsSubmitter - status services.StatusInternal - - clusterName []byte - reporterHostID []byte - - wg sync.WaitGroup - baseCtx context.Context - baseCancel context.CancelFunc - periodicCancel context.CancelFunc - - mu sync.Mutex - stopping bool - startTime time.Time - records map[string]*prehogv1.UserActivityRecord -} - -var _ GracefulStopper = (*AggregatingUsageReporter)(nil) - -// GracefulStop implements [GracefulStopper] -func (r *AggregatingUsageReporter) GracefulStop(ctx context.Context) error { - defer r.baseCancel() - r.periodicCancel() - - r.mu.Lock() - - r.stopping = true - - startTime := r.startTime - records := r.records - // don't hold up memory - r.records = nil - - r.mu.Unlock() - - if len(records) > 0 { - r.wg.Add(1) - go r.persistReport(startTime, records) - } - - go func() { - select { - case <-ctx.Done(): - r.baseCancel() - case <-r.baseCtx.Done(): - } - }() - - r.wg.Wait() - - return r.baseCtx.Err() -} - -// AnonymizeAndSubmit implements [UsageReporter]. -func (r *AggregatingUsageReporter) AnonymizeAndSubmit(events ...Anonymizable) { - filtered := events[:0] - for _, event := range events { - switch event.(type) { - case *UserLoginEvent, *SessionStartEvent, *KubeRequestEvent, *SFTPEvent: - filtered = append(filtered, event) - } - } - if len(filtered) == 0 { - return - } - - // this function must not block - go r.anonymizeAndSubmit(filtered) -} - -func (r *AggregatingUsageReporter) anonymizeAndSubmit(events []Anonymizable) { - r.mu.Lock() - defer r.mu.Unlock() - - if r.stopping { - return - } - - r.maybeFinalizeReportLocked() - - getRecord := func(userName string) *prehogv1.UserActivityRecord { - record := r.records[userName] - if record == nil { - record = &prehogv1.UserActivityRecord{} - r.records[userName] = record - } - return record - } - - for _, ae := range events { - switch te := ae.(type) { - case *UserLoginEvent: - getRecord(te.UserName).Logins++ - case *SessionStartEvent: - switch te.SessionType { - case string(types.SSHSessionKind): - getRecord(te.UserName).SshSessions++ - case string(types.AppSessionKind): - getRecord(te.UserName).AppSessions++ - case string(types.KubernetesSessionKind): - getRecord(te.UserName).KubeSessions++ - case string(types.DatabaseSessionKind): - getRecord(te.UserName).DbSessions++ - case string(types.WindowsDesktopSessionKind): - getRecord(te.UserName).DesktopSessions++ - case portSessionType: - getRecord(te.UserName).SshPortSessions++ - case tcpSessionType: - getRecord(te.UserName).AppTcpSessions++ - } - case *KubeRequestEvent: - getRecord(te.UserName).KubeRequests++ - case *SFTPEvent: - getRecord(te.UserName).SftpEvents++ - } - } -} - -func (r *AggregatingUsageReporter) maybeFinalizeReportLocked() { - now := time.Now().UTC() - - if !r.startTime.Add(aggregateTimeGranularity).Before(now) { - return - } - - startTime := r.startTime - records := r.records - r.startTime = now.Truncate(aggregateTimeGranularity) - r.records = make(map[string]*prehogv1.UserActivityRecord, len(records)) - - if len(records) > 0 { - r.wg.Add(1) - go r.persistReport(startTime, records) - } -} - -func (r *AggregatingUsageReporter) persistReport(startTime time.Time, records map[string]*prehogv1.UserActivityRecord) { - defer r.wg.Done() - - pbRecords := make([]*prehogv1.UserActivityRecord, 0, len(records)) - for userName, record := range records { - record.UserName = r.anonymizer.AnonymizeNonEmpty(userName) - pbRecords = append(pbRecords, record) - } - - pbStartTime := timestamppb.New(startTime) - expiry := startTime.Add(userActivityReportTTL) - - for len(pbRecords) > 0 { - reportUUID, wire, rem, err := prepareReport(r.clusterName, r.reporterHostID, pbStartTime, pbRecords) - if err != nil { - // TODO - return - } - pbRecords = rem - - if _, err := r.backend.Put(r.baseCtx, backend.Item{ - Key: userActivityReportKey(reportUUID, startTime), - Value: wire, - Expires: expiry, - }); err != nil { - // backend error, can't do much about that - continue - } - } -} - -func prepareReport( - clusterName, reporterHostID []byte, - startTime *timestamppb.Timestamp, - records []*prehogv1.UserActivityRecord, -) (uuid.UUID, []byte, []*prehogv1.UserActivityRecord, error) { - reportUUID := uuid.New() - report := &prehogv1.UserActivityReport{ - ReportUuid: reportUUID[:], - ClusterName: clusterName, - ReporterHostid: reporterHostID, - StartTime: startTime, - Records: records, - } - - for proto.Size(report) > userActivityReportMaxSize { - if len(report.Records) <= 1 { - return uuid.Nil, nil, records, trace.LimitExceeded("failed to marshal user activity report within size limit") - } - - report.Records = report.Records[:len(report.Records)/2] - } - - wire, err := proto.Marshal(report) - if err != nil { - // TODO: sad marshal noises - return uuid.Nil, nil, records, trace.Wrap(err) - } - - return reportUUID, wire, records[len(report.Records):], nil -} - -func (r *AggregatingUsageReporter) runPeriodicFinalize(ctx context.Context) { - defer r.wg.Done() - - ticker := time.NewTicker(time.Minute) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - } - - // if r.mu is locked then something has just called - // maybeFinalizeReportLocked anyway - if r.mu.TryLock() { - if !r.stopping { - r.maybeFinalizeReportLocked() - } - r.mu.Unlock() - } - } -} - -func (r *AggregatingUsageReporter) runPeriodicSubmit(ctx context.Context) { - defer r.wg.Done() - - iv := interval.New(interval.Config{ - FirstDuration: utils.HalfJitter(10 * time.Minute), - Duration: 5 * time.Minute, - Jitter: retryutils.NewSeventhJitter(), - }) - defer iv.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-iv.Next(): - } - - r.doSubmit(ctx) - } -} - -func (r *AggregatingUsageReporter) doSubmit(ctx context.Context) { - rangeStart := backend.ExactKey(userActivityReportsPrefix) - getResult, err := r.backend.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), 10) - if err != nil { - return - } - if len(getResult.Items) < 1 { - _ = r.status.DeleteClusterAlert(ctx, "reporting-failed") - return - } - - reports := make([]*prehogv1.UserActivityReport, 0, len(getResult.Items)) - for _, item := range getResult.Items { - report := new(prehogv1.UserActivityReport) - if err := proto.Unmarshal(item.Value, report); err != nil { - return - } - } - - if _, err := r.backend.Create(ctx, backend.Item{ - Key: backend.Key(userActivityReportsLock), - Value: []byte("null"), - Expires: r.backend.Clock().Now().UTC().Add(75 * time.Second), - }); err != nil { - if trace.IsAlreadyExists(err) { - // TODO: log that some other auth is already submitting - _ = 0 - } - return - } - - lockCtx, cancel := context.WithTimeout(ctx, 60*time.Second) - defer cancel() - - if _, err = r.submitter.SubmitUsageReports(lockCtx, - connect.NewRequest(&prehogv1.SubmitUsageReportsRequest{ - UserActivity: reports, - }), - ); err != nil { - if time.Since(reports[0].StartTime.AsTime()) > time.Hour*24 { - alert, err := types.NewClusterAlert( - "reporting-failed", - "Failed to sync usage data for over 24 hours", - types.WithAlertLabel(types.AlertOnLogin, "yes"), - types.WithAlertLabel(types.AlertPermitAll, "yes"), - ) - if err == nil { - _ = r.status.UpsertClusterAlert(ctx, alert) - } - } - // TODO: log - return - } - - for _, item := range getResult.Items { - if err := r.backend.Delete(ctx, item.Key); err != nil { - // TODO: log and try to delete the other items, might as well - _ = 0 - } - } -} From 047f2338389814f4a879ebe526110b0395ca924b Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 12:17:40 +0200 Subject: [PATCH 07/32] batch_uuid --- .../teleport/aggregating/submitter.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index ccd5d84d23322..b5dcd61ef61a5 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -19,6 +19,7 @@ import ( "fmt" "time" + "github.com/google/uuid" "github.com/gravitational/trace" "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" @@ -33,10 +34,10 @@ import ( ) // UsageReportsSubmitter is almost SubmitUsageReports from -// prehog.v1alpha.TeleportReportingService, but instead of returning a response -// (and requiring wrapping the request in a [connect.Request]) it should do its -// own error checking on the response. -type UsageReportsSubmitter func(context.Context, *prehogv1.SubmitUsageReportsRequest) error +// prehog.v1alpha.TeleportReportingService, but instead of returning the raw +// response (and requiring wrapping the request in a [connect.Request]) it +// should parse the response and return the batch UUID. +type UsageReportsSubmitter func(context.Context, *prehogv1.SubmitUsageReportsRequest) (uuid.UUID, error) const ( submitInterval = 5 * time.Minute @@ -153,9 +154,10 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { lockCtx, cancel := context.WithTimeout(ctx, submitLockDuration*9/10) defer cancel() - if err := c.Submitter(lockCtx, &prehogv1.SubmitUsageReportsRequest{ + batchUUID, err := c.Submitter(lockCtx, &prehogv1.SubmitUsageReportsRequest{ UserActivity: reports, - }); err != nil { + }) + if err != nil { c.Log.WithError(err).WithFields(logrus.Fields{ "reports": len(reports), "oldest_report": reports[0].GetStartTime().AsTime(), @@ -182,6 +184,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { } c.Log.WithFields(logrus.Fields{ + "batch_uuid": batchUUID, "reports": len(reports), "oldest_report": reports[0].GetStartTime().AsTime(), }).Info("Successfully sent usage reports.") From 600b81a85298ae4c3d0607851033cb2238f7af23 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 16:29:42 +0200 Subject: [PATCH 08/32] Naming --- .../teleport/aggregating/reporter.go | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 616553f776479..514e424fbe659 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -54,25 +54,21 @@ func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { } type ReporterConfig struct { - Anonymizer utils.Anonymizer - Backend backend.Backend - Log logrus.FieldLogger + Backend backend.Backend + Log logrus.FieldLogger - ClusterName string + ClusterName types.ClusterName ReporterHostID string } func (cfg *ReporterConfig) CheckAndSetDefaults() error { - if cfg.Anonymizer == nil { - return trace.BadParameter("missing Anonymizer") - } if cfg.Backend == nil { return trace.BadParameter("missing Backend") } if cfg.Log == nil { return trace.BadParameter("missing Log") } - if cfg.ClusterName == "" { + if cfg.ClusterName == nil { return trace.BadParameter("missing ClusterName") } if cfg.ReporterHostID == "" { @@ -92,16 +88,21 @@ func NewReporter( return nil, trace.Wrap(err) } + anonymizer, err := utils.NewHMACAnonymizer(cfg.ClusterName.GetClusterID()) + if err != nil { + return nil, trace.Wrap(err) + } + baseCtx, baseCancel := context.WithCancel(ctx) periodicCtx, periodicCancel := context.WithCancel(baseCtx) r := &Reporter{ - anonymizer: cfg.Anonymizer, + anonymizer: anonymizer, backend: cfg.Backend, log: cfg.Log, - clusterName: cfg.Anonymizer.AnonymizeNonEmpty(cfg.ClusterName), - reporterHostID: cfg.Anonymizer.AnonymizeNonEmpty(cfg.ReporterHostID), + clusterName: anonymizer.AnonymizeNonEmpty(cfg.ClusterName.GetClusterName()), + reporterHostID: anonymizer.AnonymizeNonEmpty(cfg.ReporterHostID), baseCtx: baseCtx, baseCancel: baseCancel, @@ -136,7 +137,8 @@ type Reporter struct { var _ usagereporter.GracefulStopper = (*Reporter)(nil) -// GracefulStop implements [ur.GracefulStopper]. Can only be called at most once. +// GracefulStop implements [usagereporter.GracefulStopper]. Can only be called +// at most once. func (r *Reporter) GracefulStop(ctx context.Context) error { r.periodicCancel() @@ -174,7 +176,7 @@ func (r *Reporter) GracefulStop(ctx context.Context) error { return r.baseCtx.Err() } -// AnonymizeAndSubmit implements [ur.UsageReporter]. +// AnonymizeAndSubmit implements [usagereporter.UsageReporter]. func (r *Reporter) AnonymizeAndSubmit(events ...usagereporter.Anonymizable) { filtered := events[:0] for _, event := range events { From de740236fd952593a2eddf1de8bde219b93ee42c Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 16:33:27 +0200 Subject: [PATCH 09/32] more naming --- lib/usagereporter/teleport/aggregating/reporter.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 514e424fbe659..86f0a3d213e0d 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -32,8 +32,6 @@ import ( "github.com/gravitational/teleport/lib/utils" ) -const aggregateTimeGranularity = 15 * time.Minute - const ( userActivityReportsPrefix = "userActivityReports" userActivityReportsLock = "userActivityReportsLock" @@ -41,9 +39,10 @@ const ( // maxSize is the maximum size for a backend value; dynamodb has a limit of // 400KiB per item, we store values in base64, there's some framing, and a // healthy 50% margin gets us to about 128KiB. - maxSize = 131072 + maxSize = 128 * 1024 - reportTTL = 60 * 24 * time.Hour + reportGranularity = 15 * time.Minute + reportTTL = 60 * 24 * time.Hour ) // userActivityReportKey returns the backend key for a user activity report with @@ -248,13 +247,13 @@ func (r *Reporter) anonymizeAndSubmit(events []usagereporter.Anonymizable) { func (r *Reporter) maybeFinalizeReportsLocked() { now := time.Now().UTC() - if !r.startTime.Add(aggregateTimeGranularity).Before(now) { + if !r.startTime.Add(reportGranularity).Before(now) { return } startTime := r.startTime userActivity := r.userActivity - r.startTime = now.Truncate(aggregateTimeGranularity) + r.startTime = now.Truncate(reportGranularity) // we expect the amount of users to not vary a lot between one time window // and the next r.userActivity = make(map[string]*prehogv1.UserActivityRecord, len(userActivity)) From 3ba29cd972fe0cb78b15819e6d2089ab7a612aa3 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 17:22:46 +0200 Subject: [PATCH 10/32] Move backend stuff in a "service" object --- .../teleport/aggregating/reporter.go | 70 +------- .../teleport/aggregating/service.go | 152 ++++++++++++++++++ .../teleport/aggregating/submitter.go | 30 +--- 3 files changed, 167 insertions(+), 85 deletions(-) create mode 100644 lib/usagereporter/teleport/aggregating/service.go diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 86f0a3d213e0d..4636ffec42402 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -19,11 +19,8 @@ import ( "sync" "time" - "github.com/google/uuid" "github.com/gravitational/trace" "github.com/sirupsen/logrus" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport/api/types" prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" @@ -33,25 +30,10 @@ import ( ) const ( - userActivityReportsPrefix = "userActivityReports" - userActivityReportsLock = "userActivityReportsLock" - - // maxSize is the maximum size for a backend value; dynamodb has a limit of - // 400KiB per item, we store values in base64, there's some framing, and a - // healthy 50% margin gets us to about 128KiB. - maxSize = 128 * 1024 - reportGranularity = 15 * time.Minute reportTTL = 60 * 24 * time.Hour ) -// userActivityReportKey returns the backend key for a user activity report with -// a given UUID and start time, such that reports with an earlier start time -// will appear earlier in lexicographic ordering. -func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { - return backend.Key(userActivityReportsPrefix, startTime.Format(time.RFC3339), reportUUID.String()) -} - type ReporterConfig struct { Backend backend.Backend Log logrus.FieldLogger @@ -97,7 +79,7 @@ func NewReporter( r := &Reporter{ anonymizer: anonymizer, - backend: cfg.Backend, + svc: reportService{cfg.Backend}, log: cfg.Log, clusterName: anonymizer.AnonymizeNonEmpty(cfg.ClusterName.GetClusterName()), @@ -117,7 +99,7 @@ func NewReporter( // Reporter aggregates and persists usage events to the backend. type Reporter struct { anonymizer utils.Anonymizer - backend backend.Backend + svc reportService log logrus.FieldLogger clusterName []byte @@ -273,11 +255,8 @@ func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[str records = append(records, record) } - pbStartTime := timestamppb.New(startTime) - expiry := startTime.Add(reportTTL) - for len(records) > 0 { - reportUUID, data, rem, err := prepareUserActivityReport(r.clusterName, r.reporterHostID, pbStartTime, records) + report, err := prepareUserActivityReport(r.clusterName, r.reporterHostID, startTime, records) if err != nil { r.log.WithError(err).WithFields(logrus.Fields{ "start_time": startTime, @@ -285,52 +264,17 @@ func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[str }).Error("Failed to prepare user activity report, dropping data.") return } - reportRecords := len(records) - len(rem) - records = rem - - if _, err := r.backend.Put(r.baseCtx, backend.Item{ - Key: userActivityReportKey(reportUUID, startTime), - Value: data, - Expires: expiry, - }); err != nil { + records = records[len(report.Records):] + + if err := r.svc.upsertUserActivityReport(r.baseCtx, report, reportTTL); err != nil { r.log.WithError(err).WithFields(logrus.Fields{ "start_time": startTime, - "lost_records": reportRecords, + "lost_records": len(report.Records), }).Error("Failed to persist user activity report, dropping data.") } } } -func prepareUserActivityReport( - clusterName, reporterHostID []byte, - startTime *timestamppb.Timestamp, - records []*prehogv1.UserActivityRecord, -) (reportUUID uuid.UUID, data []byte, remaining []*prehogv1.UserActivityRecord, err error) { - reportUUID = uuid.New() - report := &prehogv1.UserActivityReport{ - ReportUuid: reportUUID[:], - ClusterName: clusterName, - ReporterHostid: reporterHostID, - StartTime: startTime, - Records: records, - } - - for proto.Size(report) > maxSize { - if len(report.Records) <= 1 { - return uuid.Nil, nil, nil, trace.LimitExceeded("failed to marshal user activity report within size limit (this is a bug)") - } - - report.Records = report.Records[:len(report.Records)/2] - } - - wire, err := proto.Marshal(report) - if err != nil { - return uuid.Nil, nil, nil, trace.Wrap(err) - } - - return reportUUID, wire, records[len(report.Records):], nil -} - func (r *Reporter) runPeriodicFinalize(ctx context.Context) { defer r.wg.Done() diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go new file mode 100644 index 0000000000000..63899e5c3dfc8 --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -0,0 +1,152 @@ +// 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 aggregating + +import ( + "context" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + "github.com/gravitational/teleport/lib/backend" +) + +const ( + // maxItemSize is the maximum size for a backend value; dynamodb has a limit of + // 400KiB per item, we store values in base64, there's some framing, and a + // healthy 50% margin gets us to about 128KiB. + maxItemSize = 128 * 1024 +) + +const ( + userActivityReportsPrefix = "userActivityReports" + userActivityReportsLock = "userActivityReportsLock" +) + +// userActivityReportKey returns the backend key for a user activity report with +// a given UUID and start time, such that reports with an earlier start time +// will appear earlier in lexicographic ordering. +func userActivityReportKey(reportUUID uuid.UUID, startTime time.Time) []byte { + return backend.Key(userActivityReportsPrefix, startTime.Format(time.RFC3339), reportUUID.String()) +} + +func prepareUserActivityReport( + clusterName, reporterHostID []byte, + startTime time.Time, records []*prehogv1.UserActivityRecord, +) (*prehogv1.UserActivityReport, error) { + reportUUID := uuid.New() + report := &prehogv1.UserActivityReport{ + ReportUuid: reportUUID[:], + ClusterName: clusterName, + ReporterHostid: reporterHostID, + StartTime: timestamppb.New(startTime), + Records: records, + } + + for proto.Size(report) > maxItemSize { + if len(report.Records) <= 1 { + return nil, trace.LimitExceeded("failed to marshal user activity report within size limit (this is a bug)") + } + + report.Records = report.Records[:len(report.Records)/2] + } + + return report, nil +} + +// reportService is a [backend.Backend] wrapper that handles usage reports. +type reportService struct { + b backend.Backend +} + +func (r reportService) upsertUserActivityReport(ctx context.Context, report *prehogv1.UserActivityReport, ttl time.Duration) error { + wire, err := proto.Marshal(report) + if err != nil { + return trace.Wrap(err) + } + + reportUUID, err := uuid.FromBytes(report.GetReportUuid()) + if err != nil { + return trace.Wrap(err) + } + + startTime := report.GetStartTime().AsTime() + if startTime.IsZero() { + return trace.BadParameter("missing start_time") + } + + if _, err := r.b.Put(ctx, backend.Item{ + Key: userActivityReportKey(reportUUID, startTime), + Value: wire, + Expires: startTime.Add(ttl), + }); err != nil { + return trace.Wrap(err) + } + + return nil +} + +func (r reportService) deleteUserActivityReport(ctx context.Context, report *prehogv1.UserActivityReport) error { + reportUUID, err := uuid.FromBytes(report.GetReportUuid()) + if err != nil { + return trace.Wrap(err) + } + + startTime := report.GetStartTime().AsTime() + if startTime.IsZero() { + return trace.BadParameter("missing start_time") + } + + if err := r.b.Delete(ctx, userActivityReportKey(reportUUID, startTime)); err != nil { + return trace.Wrap(err) + } + + return nil +} + +func (r reportService) listUserActivityReports(ctx context.Context, count int) ([]*prehogv1.UserActivityReport, error) { + rangeStart := backend.ExactKey(userActivityReportsPrefix) + result, err := r.b.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), submitBatchSize) + if err != nil { + return nil, trace.Wrap(err) + } + + reports := make([]*prehogv1.UserActivityReport, 0, len(result.Items)) + for _, item := range result.Items { + report := &prehogv1.UserActivityReport{} + if err := proto.Unmarshal(item.Value, report); err != nil { + return nil, trace.Wrap(err) + } + reports = append(reports, report) + } + + return reports, nil +} + +func (r reportService) createUserActivityReportsLock(ctx context.Context, ttl time.Duration) error { + if _, err := r.b.Create(ctx, backend.Item{ + Key: backend.Key(userActivityReportsLock), + Value: []byte("null"), + Expires: r.b.Clock().Now().UTC().Add(submitLockDuration), + }); err != nil { + return trace.Wrap(err) + } + + return nil +} diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index b5dcd61ef61a5..f3c20d45bc05e 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -22,7 +22,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/sirupsen/logrus" - "google.golang.org/protobuf/proto" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/retryutils" @@ -42,7 +41,7 @@ type UsageReportsSubmitter func(context.Context, *prehogv1.SubmitUsageReportsReq const ( submitInterval = 5 * time.Minute submitLockDuration = time.Minute - submitBatch = 10 + submitBatchSize = 10 alertGraceHours = 24 alertGraceDuration = alertGraceHours * time.Hour @@ -103,14 +102,15 @@ func RunSubmitter(ctx context.Context, cfg SubmitterConfig) { } func submitOnce(ctx context.Context, c SubmitterConfig) { - rangeStart := backend.ExactKey(userActivityReportsPrefix) - getResult, err := c.Backend.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), submitBatch) + svc := reportService{c.Backend} + + reports, err := svc.listUserActivityReports(ctx, submitBatchSize) if err != nil { c.Log.WithError(err).Errorf("Failed to load usage reports for submission.") return } - if len(getResult.Items) < 1 { + if len(reports) < 1 { if _, err := c.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ AlertID: alertName, }); err != nil && trace.IsNotFound(err) { @@ -128,21 +128,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { return } - reports := make([]*prehogv1.UserActivityReport, 0, len(getResult.Items)) - for _, item := range getResult.Items { - report := &prehogv1.UserActivityReport{} - if err := proto.Unmarshal(item.Value, report); err != nil { - c.Log.WithError(err).WithField("key", item.Key).Error("Failed to parse usage report from the backend.") - return - } - reports = append(reports, report) - } - - if _, err := c.Backend.Create(ctx, backend.Item{ - Key: backend.Key(userActivityReportsLock), - Value: []byte("null"), - Expires: c.Backend.Clock().Now().UTC().Add(submitLockDuration), - }); err != nil { + if err := svc.createUserActivityReportsLock(ctx, submitLockDuration); err != nil { if trace.IsAlreadyExists(err) { c.Log.Debugf("Failed to acquire lock %v, already held.", userActivityReportsLock) } else { @@ -190,8 +176,8 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { }).Info("Successfully sent usage reports.") var lastErr error - for _, item := range getResult.Items { - if err := c.Backend.Delete(ctx, item.Key); err != nil { + for _, report := range reports { + if err := svc.deleteUserActivityReport(ctx, report); err != nil { lastErr = err } } From bb5125d26482211c991d599e0abbb93bc0178754 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 19:23:28 +0200 Subject: [PATCH 11/32] Return the correct err from gracefulstop --- lib/usagereporter/teleport/aggregating/reporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 4636ffec42402..36bbe6f939faf 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -154,7 +154,7 @@ func (r *Reporter) GracefulStop(ctx context.Context) error { r.wg.Wait() r.baseCancel() - return r.baseCtx.Err() + return ctx.Err() } // AnonymizeAndSubmit implements [usagereporter.UsageReporter]. From a586d25e1c62f304e2fc944c8f3edfed3fc8b415 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Fri, 31 Mar 2023 19:24:10 +0200 Subject: [PATCH 12/32] Tweak alert link --- lib/usagereporter/teleport/aggregating/submitter.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index f3c20d45bc05e..b0fc16a25610c 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -46,14 +46,18 @@ const ( alertGraceHours = 24 alertGraceDuration = alertGraceHours * time.Hour alertName = "reporting-failed" + alertLink = "https://goteleport.com/support/" ) -const DefaultEndpoint = "https://reporting-teleport.teleportinfra.sh" +const ( + defaultEndpointHostname = "reporting-teleport.teleportinfra.sh" + DefaultEndpoint = "https://" + defaultEndpointHostname +) var alertMessage = fmt.Sprintf("Teleport has failed to contact the usage reporting server for more than %v hours. "+ "Please make sure that the Teleport Auth Server can connect to (%v). "+ - "Otherwise, contact Teleport Support at (https://support.goteleport.com).", - alertGraceHours, DefaultEndpoint) + "Otherwise, contact Teleport Support at (%v).", + alertGraceHours, defaultEndpointHostname, alertLink) type SubmitterConfig struct { Backend backend.Backend @@ -158,6 +162,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { alertMessage, types.WithAlertLabel(types.AlertOnLogin, "yes"), types.WithAlertLabel(types.AlertPermitAll, "yes"), + types.WithAlertLabel(types.AlertLink, alertLink), ) if err != nil { c.Log.WithError(err).Errorf("Failed to create cluster alert %v.", alertName) From 6aa64d11a1d480c4e0790c13f16a0746422d0418 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 09:30:24 +0200 Subject: [PATCH 13/32] Better field comments --- .../teleport/aggregating/reporter.go | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 36bbe6f939faf..2bd72ed055ef9 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -102,17 +102,33 @@ type Reporter struct { svc reportService log logrus.FieldLogger - clusterName []byte + // clusterName is the anonymized cluster name. + clusterName []byte + // reporterHostID is the anonymized host ID of the reporter (this agent). reporterHostID []byte - wg sync.WaitGroup - baseCtx context.Context - baseCancel context.CancelFunc + // wg tracks all long-lived or I/O bound goroutines, to be waited on during + // GracefulStop. + wg sync.WaitGroup + // baseCtx is the parent context for all operations (backend operations + // being the most important). + baseCtx context.Context + // baseCancel cancels baseCtx. + baseCancel context.CancelFunc + // periodicCancel cancels the context used by the periodic finalizer; child + // of baseCtx. periodicCancel context.CancelFunc - mu sync.Mutex - stopped bool - startTime time.Time + // mu protects stopped, startTime and userActivity. + mu sync.Mutex + // stopped indicates that startTime and userActivity should not be touched + // anymore, that all events should be dropped, and that no goroutines + // tracked by wg should be started. + stopped bool + + // startTime is the start time of the currently active data collection. + startTime time.Time + // userActivity is a map of username (non-anonymized) to UserActivityRecord. userActivity map[string]*prehogv1.UserActivityRecord } From ff4d27c79781b66e05b318b535e99f0a8972bc38 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 14:55:17 +0200 Subject: [PATCH 14/32] payloadContext rather than a manual type assertion --- lib/service/service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/service/service.go b/lib/service/service.go index 419607ec15069..da106f3580270 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -1840,10 +1840,11 @@ func (process *TeleportProcess) initAuthService() error { if listener != nil { warnOnErr(listener.Close(), log) } - if ctx, _ := payload.(context.Context); ctx == nil { + if payload == nil { log.Info("Shutting down immediately.") warnOnErr(tlsServer.Close(), log) } else { + ctx := payloadContext(payload, log) log.Info("Shutting down immediately (auth service does not currently support graceful shutdown).") // NOTE: Graceful shutdown of auth.TLSServer is disabled right now, because we don't // have a good model for performing it. In particular, watchers and other GRPC streams From 59103b279e2757fc4ed69a143ddd7bf9e171a071 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 16:55:22 +0200 Subject: [PATCH 15/32] Rework reporter logic --- .../teleport/aggregating/reporter.go | 197 +++++++----------- 1 file changed, 80 insertions(+), 117 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 2bd72ed055ef9..c6b198db8f2f6 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -31,6 +31,7 @@ import ( const ( reportGranularity = 15 * time.Minute + rollbackGrace = time.Minute reportTTL = 60 * 24 * time.Hour ) @@ -61,10 +62,7 @@ func (cfg *ReporterConfig) CheckAndSetDefaults() error { // NewReporter returns a new running [Reporter]. To avoid resource leaks, // GracefulStop must be called or the base context must be closed. The context // will be used for all backend operations. -func NewReporter( - ctx context.Context, - cfg ReporterConfig, -) (*Reporter, error) { +func NewReporter(ctx context.Context, cfg ReporterConfig) (*Reporter, error) { if err := cfg.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } @@ -75,23 +73,23 @@ func NewReporter( } baseCtx, baseCancel := context.WithCancel(ctx) - periodicCtx, periodicCancel := context.WithCancel(baseCtx) r := &Reporter{ anonymizer: anonymizer, svc: reportService{cfg.Backend}, log: cfg.Log, + ingest: make(chan usagereporter.Anonymizable), + closing: make(chan struct{}), + done: make(chan struct{}), + clusterName: anonymizer.AnonymizeNonEmpty(cfg.ClusterName.GetClusterName()), reporterHostID: anonymizer.AnonymizeNonEmpty(cfg.ReporterHostID), - baseCtx: baseCtx, - baseCancel: baseCancel, - periodicCancel: periodicCancel, + baseCancel: baseCancel, } - r.wg.Add(1) - go r.runPeriodicFinalize(periodicCtx) + go r.run(baseCtx) return r, nil } @@ -102,74 +100,41 @@ type Reporter struct { svc reportService log logrus.FieldLogger + // ingest collects events from calls to [AnonymizeAndSubmit] to the + // background goroutine. + ingest chan usagereporter.Anonymizable + // closing is closed when we're not interested in collecting events anymore. + closing chan struct{} + // closingOnce closes [closing] once. + closingOnce sync.Once + // done is closed at the end of the background goroutine. + done chan struct{} + // clusterName is the anonymized cluster name. clusterName []byte // reporterHostID is the anonymized host ID of the reporter (this agent). reporterHostID []byte - // wg tracks all long-lived or I/O bound goroutines, to be waited on during - // GracefulStop. - wg sync.WaitGroup - // baseCtx is the parent context for all operations (backend operations - // being the most important). - baseCtx context.Context - // baseCancel cancels baseCtx. + // baseCancel cancels the context used by the background goroutine. baseCancel context.CancelFunc - // periodicCancel cancels the context used by the periodic finalizer; child - // of baseCtx. - periodicCancel context.CancelFunc - - // mu protects stopped, startTime and userActivity. - mu sync.Mutex - // stopped indicates that startTime and userActivity should not be touched - // anymore, that all events should be dropped, and that no goroutines - // tracked by wg should be started. - stopped bool - - // startTime is the start time of the currently active data collection. - startTime time.Time - // userActivity is a map of username (non-anonymized) to UserActivityRecord. - userActivity map[string]*prehogv1.UserActivityRecord } var _ usagereporter.GracefulStopper = (*Reporter)(nil) -// GracefulStop implements [usagereporter.GracefulStopper]. Can only be called -// at most once. +// GracefulStop implements [usagereporter.GracefulStopper]. func (r *Reporter) GracefulStop(ctx context.Context) error { - r.periodicCancel() - - r.mu.Lock() - - r.stopped = true + r.closingOnce.Do(func() { close(r.closing) }) - startTime := r.startTime - userActivity := r.userActivity - // release memory - r.userActivity = nil - - r.mu.Unlock() - - if len(userActivity) > 0 { - // we're allowed to do this because we know that we'll only Wait on r.wg - // later on in this function's body; anywhere else we're not allowed to - // Add while r.stopped is true, as Wait can't be called concurrently - // with an Add from 0, and if we began shutting down we can't assume - // that the count is above 0 - r.wg.Add(1) - go r.persistUserActivity(startTime, userActivity) + select { + case <-r.done: + r.baseCancel() + return nil + case <-ctx.Done(): } - go func() { - select { - case <-ctx.Done(): - r.baseCancel() - case <-r.baseCtx.Done(): - } - }() - - r.wg.Wait() r.baseCancel() + <-r.done + return ctx.Err() } @@ -195,25 +160,63 @@ func (r *Reporter) AnonymizeAndSubmit(events ...usagereporter.Anonymizable) { } func (r *Reporter) anonymizeAndSubmit(events []usagereporter.Anonymizable) { - r.mu.Lock() - defer r.mu.Unlock() - - if r.stopped { - return + for _, e := range events { + select { + case r.ingest <- e: + case <-r.closing: + return + } } +} + +func (r *Reporter) run(ctx context.Context) { + defer close(r.done) + + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() - r.maybeFinalizeReportsLocked() + startTime := time.Now().UTC().Truncate(reportGranularity) + userActivity := make(map[string]*prehogv1.UserActivityRecord) userRecord := func(userName string) *prehogv1.UserActivityRecord { - record := r.userActivity[userName] + record := userActivity[userName] if record == nil { record = &prehogv1.UserActivityRecord{} - r.userActivity[userName] = record + userActivity[userName] = record } return record } - for _, ae := range events { + var wg sync.WaitGroup + +Ingest: + for { + var ae usagereporter.Anonymizable + select { + case <-ticker.C: + case ae = <-r.ingest: + + case <-ctx.Done(): + r.closingOnce.Do(func() { close(r.closing) }) + break Ingest + case <-r.closing: + break Ingest + } + + now := time.Now().UTC() + if !now.Before(startTime.Add(reportGranularity)) || now.Before(startTime.Add(-rollbackGrace)) { + if len(userActivity) > 0 { + wg.Add(1) + go func() { + defer wg.Done() + r.persistUserActivity(ctx, startTime, userActivity) + }() + } + + startTime = now.Truncate(reportGranularity) + userActivity = make(map[string]*prehogv1.UserActivityRecord, len(userActivity)) + } + switch te := ae.(type) { case *usagereporter.UserLoginEvent: userRecord(te.UserName).Logins++ @@ -240,31 +243,15 @@ func (r *Reporter) anonymizeAndSubmit(events []usagereporter.Anonymizable) { userRecord(te.UserName).SftpEvents++ } } -} - -func (r *Reporter) maybeFinalizeReportsLocked() { - now := time.Now().UTC() - - if !r.startTime.Add(reportGranularity).Before(now) { - return - } - - startTime := r.startTime - userActivity := r.userActivity - r.startTime = now.Truncate(reportGranularity) - // we expect the amount of users to not vary a lot between one time window - // and the next - r.userActivity = make(map[string]*prehogv1.UserActivityRecord, len(userActivity)) if len(userActivity) > 0 { - r.wg.Add(1) - go r.persistUserActivity(startTime, userActivity) + r.persistUserActivity(ctx, startTime, userActivity) } -} -func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[string]*prehogv1.UserActivityRecord) { - defer r.wg.Done() + wg.Wait() +} +func (r *Reporter) persistUserActivity(ctx context.Context, startTime time.Time, userActivity map[string]*prehogv1.UserActivityRecord) { records := make([]*prehogv1.UserActivityRecord, 0, len(userActivity)) for userName, record := range userActivity { record.UserName = r.anonymizer.AnonymizeNonEmpty(userName) @@ -282,7 +269,7 @@ func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[str } records = records[len(report.Records):] - if err := r.svc.upsertUserActivityReport(r.baseCtx, report, reportTTL); err != nil { + if err := r.svc.upsertUserActivityReport(ctx, report, reportTTL); err != nil { r.log.WithError(err).WithFields(logrus.Fields{ "start_time": startTime, "lost_records": len(report.Records), @@ -290,27 +277,3 @@ func (r *Reporter) persistUserActivity(startTime time.Time, userActivity map[str } } } - -func (r *Reporter) runPeriodicFinalize(ctx context.Context) { - defer r.wg.Done() - - ticker := time.NewTicker(time.Minute) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - } - - // if r.mu is locked then something has just called - // maybeFinalizeReportLocked anyway - if r.mu.TryLock() { - if !r.stopped { - r.maybeFinalizeReportsLocked() - } - r.mu.Unlock() - } - } -} From 6cc7dc34b479d985c837362d69e15a2abdd09237 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 17:17:06 +0200 Subject: [PATCH 16/32] Close the base context at the end of run --- lib/usagereporter/teleport/aggregating/reporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index c6b198db8f2f6..890c8863db1d9 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -127,7 +127,6 @@ func (r *Reporter) GracefulStop(ctx context.Context) error { select { case <-r.done: - r.baseCancel() return nil case <-ctx.Done(): } @@ -170,6 +169,7 @@ func (r *Reporter) anonymizeAndSubmit(events []usagereporter.Anonymizable) { } func (r *Reporter) run(ctx context.Context) { + defer r.baseCancel() defer close(r.done) ticker := time.NewTicker(time.Minute) From 9a8d056808ab9ba555f9bdfb22797dac055cffdf Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 17:48:03 +0200 Subject: [PATCH 17/32] Use the count parameter in listUserActivityReports --- lib/usagereporter/teleport/aggregating/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go index 63899e5c3dfc8..387c7ec97be57 100644 --- a/lib/usagereporter/teleport/aggregating/service.go +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -122,7 +122,7 @@ func (r reportService) deleteUserActivityReport(ctx context.Context, report *pre func (r reportService) listUserActivityReports(ctx context.Context, count int) ([]*prehogv1.UserActivityReport, error) { rangeStart := backend.ExactKey(userActivityReportsPrefix) - result, err := r.b.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), submitBatchSize) + result, err := r.b.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), count) if err != nil { return nil, trace.Wrap(err) } From 846c8241d1999ddecd9e051cb1595a76ddd9d012 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 17:53:11 +0200 Subject: [PATCH 18/32] Use ttl parameter in createUserActivityReportsLock --- lib/usagereporter/teleport/aggregating/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go index 387c7ec97be57..159f5b255cd08 100644 --- a/lib/usagereporter/teleport/aggregating/service.go +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -143,7 +143,7 @@ func (r reportService) createUserActivityReportsLock(ctx context.Context, ttl ti if _, err := r.b.Create(ctx, backend.Item{ Key: backend.Key(userActivityReportsLock), Value: []byte("null"), - Expires: r.b.Clock().Now().UTC().Add(submitLockDuration), + Expires: r.b.Clock().Now().UTC().Add(ttl), }); err != nil { return trace.Wrap(err) } From bd3ec8fa282dd5bc33ba376670418505f21a47b6 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 17:58:36 +0200 Subject: [PATCH 19/32] service tests --- .../teleport/aggregating/service_test.go | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 lib/usagereporter/teleport/aggregating/service_test.go diff --git a/lib/usagereporter/teleport/aggregating/service_test.go b/lib/usagereporter/teleport/aggregating/service_test.go new file mode 100644 index 0000000000000..33c824d8c1eb6 --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/service_test.go @@ -0,0 +1,93 @@ +package aggregating + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + "github.com/gravitational/teleport/lib/backend/memory" +) + +func newReport(startTime time.Time) *prehogv1.UserActivityReport { + u := uuid.New() + r := &prehogv1.UserActivityReport{ + ReportUuid: u[:], + StartTime: timestamppb.New(startTime), + } + return r +} + +func TestCRUD(t *testing.T) { + ctx := context.Background() + clk := clockwork.NewFakeClock() + bk, err := memory.New(memory.Config{ + Clock: clk, + EventsOff: true, + }) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, bk.Close()) }) + + svc := reportService{bk} + + r0 := newReport(clk.Now().Add(time.Minute)) + r1 := newReport(clk.Now().Add(time.Minute)) + r2 := newReport(clk.Now().Add(2 * time.Minute)) + + require.NoError(t, svc.upsertUserActivityReport(ctx, r0, time.Hour)) + require.NoError(t, svc.upsertUserActivityReport(ctx, r1, time.Hour)) + require.NoError(t, svc.upsertUserActivityReport(ctx, r2, time.Hour)) + + // we expect r0 and r1 in unspecified order + reports, err := svc.listUserActivityReports(ctx, 2) + require.NoError(t, err) + require.Len(t, reports, 2) + if proto.Equal(r0, reports[0]) { + require.True(t, proto.Equal(r1, reports[1])) + } else { + require.True(t, proto.Equal(r0, reports[1])) + require.True(t, proto.Equal(r1, reports[0])) + } + + require.NoError(t, svc.deleteUserActivityReport(ctx, r1)) + reports, err = svc.listUserActivityReports(ctx, 5) + require.NoError(t, err) + require.Len(t, reports, 2) + // r2 has a later start time, so it must appear later + require.True(t, proto.Equal(r0, reports[0])) + require.True(t, proto.Equal(r2, reports[1])) + + clk.Advance(time.Minute + time.Hour) + reports, err = svc.listUserActivityReports(ctx, 5) + require.NoError(t, err) + require.Len(t, reports, 1) + require.True(t, proto.Equal(r2, reports[0])) +} + +func TestLock(t *testing.T) { + ctx := context.Background() + clk := clockwork.NewFakeClock() + bk, err := memory.New(memory.Config{ + Clock: clk, + EventsOff: true, + }) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, bk.Close()) }) + + svc := reportService{bk} + + require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute)) + clk.Advance(time.Minute) + err = svc.createUserActivityReportsLock(ctx, 2*time.Minute) + require.Error(t, err) + require.True(t, trace.IsAlreadyExists(err)) + clk.Advance(time.Minute) + require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute)) +} From 3586e7b01fe4340312b0e5c0ff660d1c3491296c Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Mon, 3 Apr 2023 18:45:03 +0200 Subject: [PATCH 20/32] submitter test --- .../teleport/aggregating/submitter_test.go | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 lib/usagereporter/teleport/aggregating/submitter_test.go diff --git a/lib/usagereporter/teleport/aggregating/submitter_test.go b/lib/usagereporter/teleport/aggregating/submitter_test.go new file mode 100644 index 0000000000000..39f657f17c531 --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/submitter_test.go @@ -0,0 +1,118 @@ +package aggregating + +import ( + "context" + "errors" + "testing" + + "github.com/google/uuid" + "github.com/jonboulle/clockwork" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/gravitational/teleport/api/types" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services/local" +) + +// TestSubmitOnce tests the behavior of [submitOnce]; the [RunSubmitter] +// function is just a jittered periodic call to submitOnce, so testing it has +// very little use. +func TestSubmitOnce(t *testing.T) { + ctx := context.Background() + clk := clockwork.NewFakeClock() + bk, err := memory.New(memory.Config{ + Clock: clk, + EventsOff: true, + }) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, bk.Close()) }) + + svc := reportService{bk} + + var submitted []*prehogv1.UserActivityReport + submitOk := func(ctx context.Context, req *prehogv1.SubmitUsageReportsRequest) (uuid.UUID, error) { + submitted = append(submitted, req.UserActivity...) + return uuid.New(), nil + } + submitErr := func(ctx context.Context, req *prehogv1.SubmitUsageReportsRequest) (uuid.UUID, error) { + submitted = append(submitted, req.UserActivity...) + return uuid.Nil, errors.New("") + } + + scfg := SubmitterConfig{ + Backend: bk, + Log: logrus.StandardLogger(), + Status: local.NewStatusService(bk), + Submitter: submitOk, + } + require.NoError(t, scfg.CheckAndSetDefaults()) + + r0 := newReport(clk.Now()) + require.NoError(t, svc.upsertUserActivityReport(ctx, r0, reportTTL)) + + // successful submit, no alerts, no leftover reports + submitOnce(ctx, scfg) + require.Len(t, submitted, 1) + require.True(t, proto.Equal(r0, submitted[0])) + reports, err := svc.listUserActivityReports(ctx, 10) + require.NoError(t, err) + require.Empty(t, reports) + submitted = nil + + alerts, err := scfg.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ + AlertID: alertName, + }) + require.NoError(t, err) + require.Empty(t, alerts) + + require.NoError(t, svc.upsertUserActivityReport(ctx, r0, reportTTL)) + + // failed submit past the grace time, we get the alert and the report is still there + clk.Advance(alertGraceDuration) + scfg.Submitter = submitErr + submitOnce(ctx, scfg) + require.Len(t, submitted, 1) + require.True(t, proto.Equal(r0, submitted[0])) + reports, err = svc.listUserActivityReports(ctx, 10) + require.NoError(t, err) + require.Len(t, reports, 1) + require.True(t, proto.Equal(r0, reports[0])) + submitted = nil + + alerts, err = scfg.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ + AlertID: alertName, + }) + require.NoError(t, err) + require.Len(t, alerts, 1) + require.Equal(t, alertName, alerts[0].GetName()) + + scfg.Submitter = submitOk + // the lock is still held, nothing happens + submitOnce(ctx, scfg) + require.Empty(t, submitted) + + clk.Advance(submitLockDuration) + // successful submission, no remaining events but the alert stays for one more cycle + submitOnce(ctx, scfg) + require.Len(t, submitted, 1) + submitted = nil + + alerts, err = scfg.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ + AlertID: alertName, + }) + require.NoError(t, err) + require.Len(t, alerts, 1) + require.Equal(t, alertName, alerts[0].GetName()) + + // nothing to submit, alert gone + submitOnce(ctx, scfg) + require.Empty(t, submitted) + alerts, err = scfg.Status.GetClusterAlerts(ctx, types.GetClusterAlertsRequest{ + AlertID: alertName, + }) + require.NoError(t, err) + require.Empty(t, alerts) +} From 766011c13292be35a75620e37fa33b59f387daaf Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 10:53:33 +0200 Subject: [PATCH 21/32] reporter test --- .../teleport/aggregating/reporter.go | 35 ++++- .../teleport/aggregating/reporter_test.go | 134 ++++++++++++++++++ 2 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 lib/usagereporter/teleport/aggregating/reporter_test.go diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 890c8863db1d9..6b6f29b3763e8 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -20,6 +20,7 @@ import ( "time" "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" @@ -33,11 +34,14 @@ const ( reportGranularity = 15 * time.Minute rollbackGrace = time.Minute reportTTL = 60 * 24 * time.Hour + + checkInterval = time.Minute ) type ReporterConfig struct { Backend backend.Backend Log logrus.FieldLogger + Clock clockwork.Clock ClusterName types.ClusterName ReporterHostID string @@ -50,6 +54,9 @@ func (cfg *ReporterConfig) CheckAndSetDefaults() error { if cfg.Log == nil { return trace.BadParameter("missing Log") } + if cfg.Clock == nil { + cfg.Clock = clockwork.NewRealClock() + } if cfg.ClusterName == nil { return trace.BadParameter("missing ClusterName") } @@ -78,6 +85,7 @@ func NewReporter(ctx context.Context, cfg ReporterConfig) (*Reporter, error) { anonymizer: anonymizer, svc: reportService{cfg.Backend}, log: cfg.Log, + clock: cfg.Clock, ingest: make(chan usagereporter.Anonymizable), closing: make(chan struct{}), @@ -99,6 +107,7 @@ type Reporter struct { anonymizer utils.Anonymizer svc reportService log logrus.FieldLogger + clock clockwork.Clock // ingest collects events from calls to [AnonymizeAndSubmit] to the // background goroutine. @@ -117,6 +126,10 @@ type Reporter struct { // baseCancel cancels the context used by the background goroutine. baseCancel context.CancelFunc + + // ingested, if non-nil, received events after being added to the aggregated + // data. Used in tests. + ingested chan usagereporter.Anonymizable } var _ usagereporter.GracefulStopper = (*Reporter)(nil) @@ -172,10 +185,13 @@ func (r *Reporter) run(ctx context.Context) { defer r.baseCancel() defer close(r.done) - ticker := time.NewTicker(time.Minute) + ticker := r.clock.NewTicker(checkInterval) defer ticker.Stop() - startTime := time.Now().UTC().Truncate(reportGranularity) + startTime := r.clock.Now().UTC().Truncate(reportGranularity) + windowStart := startTime.Add(-rollbackGrace) + windowEnd := startTime.Add(reportGranularity) + userActivity := make(map[string]*prehogv1.UserActivityRecord) userRecord := func(userName string) *prehogv1.UserActivityRecord { @@ -193,7 +209,7 @@ Ingest: for { var ae usagereporter.Anonymizable select { - case <-ticker.C: + case <-ticker.Chan(): case ae = <-r.ingest: case <-ctx.Done(): @@ -203,17 +219,18 @@ Ingest: break Ingest } - now := time.Now().UTC() - if !now.Before(startTime.Add(reportGranularity)) || now.Before(startTime.Add(-rollbackGrace)) { + if now := r.clock.Now().UTC(); now.Before(windowStart) || !now.Before(windowEnd) { if len(userActivity) > 0 { wg.Add(1) - go func() { + go func(ctx context.Context, startTime time.Time, userActivity map[string]*prehogv1.UserActivityRecord) { defer wg.Done() r.persistUserActivity(ctx, startTime, userActivity) - }() + }(ctx, startTime, userActivity) } startTime = now.Truncate(reportGranularity) + windowStart = startTime.Add(-rollbackGrace) + windowEnd = startTime.Add(reportGranularity) userActivity = make(map[string]*prehogv1.UserActivityRecord, len(userActivity)) } @@ -242,6 +259,10 @@ Ingest: case *usagereporter.SFTPEvent: userRecord(te.UserName).SftpEvents++ } + + if r.ingested != nil { + r.ingested <- ae + } } if len(userActivity) > 0 { diff --git a/lib/usagereporter/teleport/aggregating/reporter_test.go b/lib/usagereporter/teleport/aggregating/reporter_test.go new file mode 100644 index 0000000000000..2d0c6c24e747b --- /dev/null +++ b/lib/usagereporter/teleport/aggregating/reporter_test.go @@ -0,0 +1,134 @@ +package aggregating + +import ( + "bytes" + "context" + "testing" + "time" + + "github.com/google/uuid" + "github.com/jonboulle/clockwork" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services" + usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" +) + +func TestReporter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + clk := clockwork.NewFakeClock() + bk, err := memory.New(memory.Config{ + Clock: clk, + }) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, bk.Close()) }) + // we set up a watcher to not have to poll the backend for newly added items + // we expect + w, err := bk.NewWatcher(ctx, backend.Watch{}) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, w.Close()) }) + recvBackendEvent := func() backend.Event { + select { + case e := <-w.Events(): + return e + case <-time.After(time.Second): + t.Fatal("failed to get backend event") + return backend.Event{} + } + } + require.Equal(t, types.OpInit, recvBackendEvent().Type) + + clusterName, err := services.NewClusterNameWithRandomID(types.ClusterNameSpecV2{ + ClusterName: "clustername", + }) + require.NoError(t, err) + + r, err := NewReporter(ctx, ReporterConfig{ + Backend: bk, + Log: logrus.StandardLogger(), + Clock: clk, + ClusterName: clusterName, + ReporterHostID: uuid.NewString(), + }) + require.NoError(t, err) + + svc := reportService{bk} + + r.ingested = make(chan usagereporter.Anonymizable, 3) + recvIngested := func() { + select { + case <-r.ingested: + case <-time.After(time.Second): + t.Fatal("failed to receive ingested event") + } + } + r.AnonymizeAndSubmit(&usagereporter.UserLoginEvent{ + UserName: "alice", + }) + r.AnonymizeAndSubmit(&usagereporter.SessionStartEvent{ + UserName: "alice", + SessionType: "ssh", + }) + r.AnonymizeAndSubmit(&usagereporter.SessionStartEvent{ + UserName: "alice", + SessionType: "ssh", + }) + recvIngested() + recvIngested() + recvIngested() + r.ingested = nil + + clk.BlockUntil(1) + clk.Advance(reportGranularity) + + require.Equal(t, types.OpPut, recvBackendEvent().Type) + + reports, err := svc.listUserActivityReports(ctx, 10) + require.NoError(t, err) + require.Len(t, reports, 1) + require.Len(t, reports[0].Records, 1) + record := reports[0].Records[0] + require.Equal(t, uint64(1), record.Logins) + require.Equal(t, uint64(2), record.SshSessions) + + require.NoError(t, svc.deleteUserActivityReport(ctx, reports[0])) + require.Equal(t, types.OpDelete, recvBackendEvent().Type) + + // on a GracefulStop there's no need to advance the clock, all processed + // data is emitted immediately + r.ingested = make(chan usagereporter.Anonymizable, 3) + r.AnonymizeAndSubmit(&usagereporter.UserLoginEvent{ + UserName: "alice", + }) + r.AnonymizeAndSubmit(&usagereporter.UserLoginEvent{ + UserName: "bob", + }) + r.AnonymizeAndSubmit(&usagereporter.SessionStartEvent{ + UserName: "bob", + SessionType: "k8s", + }) + recvIngested() + recvIngested() + recvIngested() + r.ingested = nil + + require.NoError(t, r.GracefulStop(ctx)) + reports, err = svc.listUserActivityReports(ctx, 10) + require.NoError(t, err) + require.Len(t, reports, 1) + require.Len(t, reports[0].Records, 2) + rec1, rec2 := reports[0].Records[0], reports[0].Records[1] + // record.UserName is alice + if !bytes.Equal(record.UserName, rec1.UserName) { + rec1, rec2 = rec2, rec1 + } + require.Equal(t, uint64(1), rec1.Logins) + require.Equal(t, uint64(1), rec2.Logins) + require.Equal(t, uint64(0), rec1.KubeSessions) + require.Equal(t, uint64(1), rec2.KubeSessions) +} From 91b75dc06280c0406ce34c81da44722698b5c93c Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 10:58:15 +0200 Subject: [PATCH 22/32] Document GracefulStopper.GracefulStop --- lib/usagereporter/teleport/usagereporter.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/usagereporter/teleport/usagereporter.go b/lib/usagereporter/teleport/usagereporter.go index fe6cc633121b2..9feac1c62a53e 100644 --- a/lib/usagereporter/teleport/usagereporter.go +++ b/lib/usagereporter/teleport/usagereporter.go @@ -81,6 +81,10 @@ type UsageReporter interface { type GracefulStopper interface { UsageReporter + // GracefulStop gracefully closes and runs any finalization needed by the + // UsageReporter; operations can run as long as the context is alive, but + // must terminate quickly (even losing data) if the context is closed. + // Returns nil if operations have completed cleanly. GracefulStop(context.Context) error } From 634b59c393ad66998f5144fdaa84a5e998b7b58e Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 11:21:25 +0200 Subject: [PATCH 23/32] Fix errorf/warnf that don't need formatting --- lib/usagereporter/teleport/aggregating/submitter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index b0fc16a25610c..47af86a195c05 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -110,7 +110,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { reports, err := svc.listUserActivityReports(ctx, submitBatchSize) if err != nil { - c.Log.WithError(err).Errorf("Failed to load usage reports for submission.") + c.Log.WithError(err).Error("Failed to load usage reports for submission.") return } @@ -152,7 +152,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { "reports": len(reports), "oldest_report": reports[0].GetStartTime().AsTime(), "newest_report": reports[len(reports)-1].GetStartTime().AsTime(), - }).Errorf("Failed to send usage reports.") + }).Error("Failed to send usage reports.") if time.Since(reports[0].StartTime.AsTime()) <= alertGraceDuration { return From 4f844534534a89d17ceae0df9255a806ce0aea14 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 11:24:12 +0200 Subject: [PATCH 24/32] Add debug line on a successful report --- lib/usagereporter/teleport/aggregating/reporter.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 6b6f29b3763e8..1274a98c3e2d6 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -295,6 +295,13 @@ func (r *Reporter) persistUserActivity(ctx context.Context, startTime time.Time, "start_time": startTime, "lost_records": len(report.Records), }).Error("Failed to persist user activity report, dropping data.") + continue } + + r.log.WithFields(logrus.Fields{ + "report_uuid": report.ReportUuid, + "start_time": startTime, + "records": len(report.Records), + }).Debug("Persisted user activity report.") } } From ac51c249ebccc9133e389503deb39be26baebcf4 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 11:24:55 +0200 Subject: [PATCH 25/32] fix license --- .../teleport/aggregating/reporter_test.go | 14 ++++++++++++++ .../teleport/aggregating/service_test.go | 14 ++++++++++++++ .../teleport/aggregating/submitter_test.go | 14 ++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/lib/usagereporter/teleport/aggregating/reporter_test.go b/lib/usagereporter/teleport/aggregating/reporter_test.go index 2d0c6c24e747b..f27581c2ae1fb 100644 --- a/lib/usagereporter/teleport/aggregating/reporter_test.go +++ b/lib/usagereporter/teleport/aggregating/reporter_test.go @@ -1,3 +1,17 @@ +// 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 aggregating import ( diff --git a/lib/usagereporter/teleport/aggregating/service_test.go b/lib/usagereporter/teleport/aggregating/service_test.go index 33c824d8c1eb6..536ec19499b85 100644 --- a/lib/usagereporter/teleport/aggregating/service_test.go +++ b/lib/usagereporter/teleport/aggregating/service_test.go @@ -1,3 +1,17 @@ +// 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 aggregating import ( diff --git a/lib/usagereporter/teleport/aggregating/submitter_test.go b/lib/usagereporter/teleport/aggregating/submitter_test.go index 39f657f17c531..535ac3aa69188 100644 --- a/lib/usagereporter/teleport/aggregating/submitter_test.go +++ b/lib/usagereporter/teleport/aggregating/submitter_test.go @@ -1,3 +1,17 @@ +// 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 aggregating import ( From da545be2dd54ab8a9a044fbd0fcd10a58bb4673b Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 11:29:45 +0200 Subject: [PATCH 26/32] sync prehog proto from cloud --- gen/proto/go/prehog/v1alpha/teleport.pb.go | 275 +++++++++++++------ gen/proto/js/prehog/v1alpha/teleport_pb.d.ts | 19 ++ gen/proto/js/prehog/v1alpha/teleport_pb.js | 21 +- proto/prehog/v1alpha/teleport.proto | 22 ++ 4 files changed, 257 insertions(+), 80 deletions(-) diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index 485ca92dda9c0..e2d50440ba029 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -102,24 +102,43 @@ func (ResourceKind) EnumDescriptor() ([]byte, []int) { type DiscoverResource int32 const ( - DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED DiscoverResource = 0 - DiscoverResource_DISCOVER_RESOURCE_SERVER DiscoverResource = 1 - DiscoverResource_DISCOVER_RESOURCE_KUBERNETES DiscoverResource = 2 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED DiscoverResource = 3 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED DiscoverResource = 4 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED DiscoverResource = 5 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS DiscoverResource = 6 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_RDS DiscoverResource = 7 - DiscoverResource_DISCOVER_RESOURCE_APPLICATION_HTTP DiscoverResource = 8 - DiscoverResource_DISCOVER_RESOURCE_APPLICATION_TCP DiscoverResource = 9 - DiscoverResource_DISCOVER_RESOURCE_WINDOWS_DESKTOP DiscoverResource = 10 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS DiscoverResource = 11 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT DiscoverResource = 12 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED DiscoverResource = 13 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED DiscoverResource = 14 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP DiscoverResource = 15 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_GCP DiscoverResource = 16 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP DiscoverResource = 17 + DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED DiscoverResource = 0 + DiscoverResource_DISCOVER_RESOURCE_SERVER DiscoverResource = 1 + DiscoverResource_DISCOVER_RESOURCE_KUBERNETES DiscoverResource = 2 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED DiscoverResource = 3 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED DiscoverResource = 4 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED DiscoverResource = 5 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS DiscoverResource = 6 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_RDS DiscoverResource = 7 + DiscoverResource_DISCOVER_RESOURCE_APPLICATION_HTTP DiscoverResource = 8 + DiscoverResource_DISCOVER_RESOURCE_APPLICATION_TCP DiscoverResource = 9 + DiscoverResource_DISCOVER_RESOURCE_WINDOWS_DESKTOP DiscoverResource = 10 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS DiscoverResource = 11 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT DiscoverResource = 12 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED DiscoverResource = 13 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED DiscoverResource = 14 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP DiscoverResource = 15 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_GCP DiscoverResource = 16 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP DiscoverResource = 17 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS DiscoverResource = 18 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE DiscoverResource = 19 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_DYNAMODB DiscoverResource = 20 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES DiscoverResource = 21 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED DiscoverResource = 22 // Cassandra & ScyllaDb + DiscoverResource_DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED DiscoverResource = 23 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE DiscoverResource = 24 // Elasticache & MemoryDb + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB DiscoverResource = 25 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE DiscoverResource = 26 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED DiscoverResource = 27 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE DiscoverResource = 28 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE DiscoverResource = 29 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT DiscoverResource = 30 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED DiscoverResource = 31 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS DiscoverResource = 32 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SNOWFLAKE DiscoverResource = 33 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY DiscoverResource = 34 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY DiscoverResource = 35 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION DiscoverResource = 36 ) // Enum value maps for DiscoverResource. @@ -143,26 +162,64 @@ var ( 15: "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP", 16: "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP", 17: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP", + 18: "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS", + 19: "DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE", + 20: "DISCOVER_RESOURCE_DATABASE_DYNAMODB", + 21: "DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES", + 22: "DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED", + 23: "DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED", + 24: "DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE", + 25: "DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB", + 26: "DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE", + 27: "DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED", + 28: "DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE", + 29: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE", + 30: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT", + 31: "DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED", + 32: "DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS", + 33: "DISCOVER_RESOURCE_DATABASE_SNOWFLAKE", + 34: "DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY", + 35: "DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY", + 36: "DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION", } DiscoverResource_value = map[string]int32{ - "DISCOVER_RESOURCE_UNSPECIFIED": 0, - "DISCOVER_RESOURCE_SERVER": 1, - "DISCOVER_RESOURCE_KUBERNETES": 2, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED": 3, - "DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED": 4, - "DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED": 5, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS": 6, - "DISCOVER_RESOURCE_DATABASE_MYSQL_RDS": 7, - "DISCOVER_RESOURCE_APPLICATION_HTTP": 8, - "DISCOVER_RESOURCE_APPLICATION_TCP": 9, - "DISCOVER_RESOURCE_WINDOWS_DESKTOP": 10, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS": 11, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT": 12, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED": 13, - "DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED": 14, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP": 15, - "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP": 16, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP": 17, + "DISCOVER_RESOURCE_UNSPECIFIED": 0, + "DISCOVER_RESOURCE_SERVER": 1, + "DISCOVER_RESOURCE_KUBERNETES": 2, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED": 3, + "DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED": 4, + "DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED": 5, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS": 6, + "DISCOVER_RESOURCE_DATABASE_MYSQL_RDS": 7, + "DISCOVER_RESOURCE_APPLICATION_HTTP": 8, + "DISCOVER_RESOURCE_APPLICATION_TCP": 9, + "DISCOVER_RESOURCE_WINDOWS_DESKTOP": 10, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS": 11, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT": 12, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED": 13, + "DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED": 14, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP": 15, + "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP": 16, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP": 17, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS": 18, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE": 19, + "DISCOVER_RESOURCE_DATABASE_DYNAMODB": 20, + "DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES": 21, + "DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED": 22, + "DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED": 23, + "DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE": 24, + "DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB": 25, + "DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE": 26, + "DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED": 27, + "DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE": 28, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE": 29, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT": 30, + "DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED": 31, + "DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS": 32, + "DISCOVER_RESOURCE_DATABASE_SNOWFLAKE": 33, + "DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY": 34, + "DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY": 35, + "DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION": 36, } ) @@ -4324,7 +4381,7 @@ var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, 0x45, 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4f, 0x50, - 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xaa, 0x06, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x63, + 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xed, 0x0d, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, @@ -4375,49 +4432,109 @@ var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ 0x5f, 0x47, 0x43, 0x50, 0x10, 0x10, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x47, - 0x43, 0x50, 0x10, 0x11, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x43, 0x4f, - 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, - 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, - 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, 0x44, - 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, - 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xa3, 0x03, 0x0a, 0x18, 0x54, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x65, - 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, + 0x43, 0x50, 0x10, 0x11, 0x12, 0x3b, 0x0a, 0x37, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, + 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, + 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x44, 0x53, + 0x48, 0x49, 0x46, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x4c, 0x45, 0x53, 0x53, 0x10, + 0x12, 0x12, 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, + 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x13, + 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x44, + 0x59, 0x4e, 0x41, 0x4d, 0x4f, 0x44, 0x42, 0x10, 0x14, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, + 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, + 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, 0x53, 0x41, 0x4e, 0x44, 0x52, + 0x41, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x50, 0x41, 0x43, 0x45, 0x53, 0x10, 0x15, 0x12, 0x34, 0x0a, + 0x30, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, 0x53, + 0x41, 0x4e, 0x44, 0x52, 0x41, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, + 0x44, 0x10, 0x16, 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, + 0x45, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x5f, + 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x17, 0x12, 0x30, 0x0a, + 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, 0x49, + 0x53, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x18, 0x12, + 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, + 0x44, 0x49, 0x53, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0x59, 0x44, 0x42, 0x10, 0x19, 0x12, 0x30, + 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, + 0x49, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x1a, + 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, + 0x45, 0x44, 0x49, 0x53, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x4c, + 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x2a, 0x0a, 0x26, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x41, + 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1c, 0x12, 0x2e, 0x0a, 0x2a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x41, + 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1d, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4d, + 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x1e, 0x12, 0x36, 0x0a, 0x32, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x43, 0x4b, 0x52, 0x4f, 0x41, + 0x43, 0x48, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, + 0x10, 0x1f, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, + 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x41, 0x54, 0x4c, 0x41, 0x53, 0x10, 0x20, + 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, + 0x4e, 0x4f, 0x57, 0x46, 0x4c, 0x41, 0x4b, 0x45, 0x10, 0x21, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x44, 0x53, + 0x5f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x10, 0x22, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x4f, + 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, + 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x23, 0x12, 0x37, + 0x0a, 0x33, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, + 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x24, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, + 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, 0x50, + 0x50, 0x45, 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, + 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, + 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xa3, 0x03, + 0x0a, 0x18, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, - 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, - 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, + 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, + 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, + 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, + 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts index 0d6b914d68b78..ffa9ac2682855 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts @@ -1769,6 +1769,25 @@ export enum DiscoverResource { DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP = 15, DISCOVER_RESOURCE_DATABASE_MYSQL_GCP = 16, DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP = 17, + DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS = 18, + DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE = 19, + DISCOVER_RESOURCE_DATABASE_DYNAMODB = 20, + DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES = 21, + DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED = 22, + DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED = 23, + DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE = 24, + DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB = 25, + DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE = 26, + DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED = 27, + DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE = 28, + DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE = 29, + DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT = 30, + DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED = 31, + DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS = 32, + DISCOVER_RESOURCE_DATABASE_SNOWFLAKE = 33, + DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY = 34, + DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY = 35, + DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION = 36, } export enum DiscoverStatus { diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.js b/gen/proto/js/prehog/v1alpha/teleport_pb.js index 74cc68dad2c94..cf2377cc952be 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.js +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.js @@ -12250,7 +12250,26 @@ proto.prehog.v1alpha.DiscoverResource = { DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED: 14, DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP: 15, DISCOVER_RESOURCE_DATABASE_MYSQL_GCP: 16, - DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP: 17 + DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP: 17, + DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS: 18, + DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE: 19, + DISCOVER_RESOURCE_DATABASE_DYNAMODB: 20, + DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES: 21, + DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED: 22, + DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED: 23, + DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE: 24, + DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB: 25, + DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE: 26, + DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED: 27, + DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE: 28, + DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE: 29, + DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT: 30, + DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED: 31, + DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS: 32, + DISCOVER_RESOURCE_DATABASE_SNOWFLAKE: 33, + DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY: 34, + DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY: 35, + DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION: 36 }; /** diff --git a/proto/prehog/v1alpha/teleport.proto b/proto/prehog/v1alpha/teleport.proto index eab9c44fe58a1..898ae8c5d90ac 100644 --- a/proto/prehog/v1alpha/teleport.proto +++ b/proto/prehog/v1alpha/teleport.proto @@ -171,6 +171,28 @@ enum DiscoverResource { DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP = 15; DISCOVER_RESOURCE_DATABASE_MYSQL_GCP = 16; DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP = 17; + + DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS = 18; + DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE = 19; + DISCOVER_RESOURCE_DATABASE_DYNAMODB = 20; + DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES = 21; + DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED = 22; // Cassandra & ScyllaDb + DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED = 23; + DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE = 24; // Elasticache & MemoryDb + DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB = 25; + DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE = 26; + DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED = 27; + + DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE = 28; + DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE = 29; + DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT = 30; + DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED = 31; + DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS = 32; + DISCOVER_RESOURCE_DATABASE_SNOWFLAKE = 33; + + DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY = 34; + DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY = 35; + DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION = 36; } // DiscoverResourceMetadata contains common metadata identifying resource type being added. From 0a33633db13b222cc0ce32fdf9a67479cf7ef2fb Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Tue, 4 Apr 2023 11:37:20 +0200 Subject: [PATCH 27/32] Only send non-nil events through test channel This fixes a hang/data race, as we effectively "send" a nil event whenever the ticker hits --- lib/usagereporter/teleport/aggregating/reporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 1274a98c3e2d6..01c31a377f8b4 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -260,7 +260,7 @@ Ingest: userRecord(te.UserName).SftpEvents++ } - if r.ingested != nil { + if ae != nil && r.ingested != nil { r.ingested <- ae } } From 7a153578690f7a43093e4ec4f1a76da5e3e29fe8 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 5 Apr 2023 11:18:20 +0200 Subject: [PATCH 28/32] Docs and variable names --- .../teleport/aggregating/reporter.go | 36 ++++++++++++------- .../teleport/aggregating/reporter_test.go | 10 +++--- .../teleport/aggregating/service.go | 3 ++ .../teleport/aggregating/submitter.go | 14 ++++++-- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 01c31a377f8b4..22e4f362d1bf6 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -38,15 +38,27 @@ const ( checkInterval = time.Minute ) +// ReporterConfig contains the configuration for a [Reporter]. type ReporterConfig struct { + // Backend is the backend used to store reports. Required Backend backend.Backend - Log logrus.FieldLogger - Clock clockwork.Clock - - ClusterName types.ClusterName - ReporterHostID string + // Log is the logger used for logging. Required. + Log logrus.FieldLogger + // Clock is the clock used for timestamping reports and deciding when to + // persist them to the backend. Optional, defaults to the real clock. + Clock clockwork.Clock + + // ClusterName is the ClusterName resource for the current cluster, used for + // anonymization and to report the cluster name itself. Required. + ClusterName types.ClusterName + // HostID is the host ID of the current Teleport instance, added to reports + // for auditing purposes. Required. + HostID string } +// CheckAndSetDefaults checks the [ReporterConfig] for validity, returning nil +// if there's no error. Idempotent but not concurrent safe, as it might need to +// write to the config to apply defaults. func (cfg *ReporterConfig) CheckAndSetDefaults() error { if cfg.Backend == nil { return trace.BadParameter("missing Backend") @@ -60,8 +72,8 @@ func (cfg *ReporterConfig) CheckAndSetDefaults() error { if cfg.ClusterName == nil { return trace.BadParameter("missing ClusterName") } - if cfg.ReporterHostID == "" { - return trace.BadParameter("missing ReporterHostID") + if cfg.HostID == "" { + return trace.BadParameter("missing HostID") } return nil } @@ -91,8 +103,8 @@ func NewReporter(ctx context.Context, cfg ReporterConfig) (*Reporter, error) { closing: make(chan struct{}), done: make(chan struct{}), - clusterName: anonymizer.AnonymizeNonEmpty(cfg.ClusterName.GetClusterName()), - reporterHostID: anonymizer.AnonymizeNonEmpty(cfg.ReporterHostID), + clusterName: anonymizer.AnonymizeNonEmpty(cfg.ClusterName.GetClusterName()), + hostID: anonymizer.AnonymizeNonEmpty(cfg.HostID), baseCancel: baseCancel, } @@ -121,8 +133,8 @@ type Reporter struct { // clusterName is the anonymized cluster name. clusterName []byte - // reporterHostID is the anonymized host ID of the reporter (this agent). - reporterHostID []byte + // hostID is the anonymized host ID of the reporter (this instance). + hostID []byte // baseCancel cancels the context used by the background goroutine. baseCancel context.CancelFunc @@ -280,7 +292,7 @@ func (r *Reporter) persistUserActivity(ctx context.Context, startTime time.Time, } for len(records) > 0 { - report, err := prepareUserActivityReport(r.clusterName, r.reporterHostID, startTime, records) + report, err := prepareUserActivityReport(r.clusterName, r.hostID, startTime, records) if err != nil { r.log.WithError(err).WithFields(logrus.Fields{ "start_time": startTime, diff --git a/lib/usagereporter/teleport/aggregating/reporter_test.go b/lib/usagereporter/teleport/aggregating/reporter_test.go index f27581c2ae1fb..f15529a224878 100644 --- a/lib/usagereporter/teleport/aggregating/reporter_test.go +++ b/lib/usagereporter/teleport/aggregating/reporter_test.go @@ -63,11 +63,11 @@ func TestReporter(t *testing.T) { require.NoError(t, err) r, err := NewReporter(ctx, ReporterConfig{ - Backend: bk, - Log: logrus.StandardLogger(), - Clock: clk, - ClusterName: clusterName, - ReporterHostID: uuid.NewString(), + Backend: bk, + Log: logrus.StandardLogger(), + Clock: clk, + ClusterName: clusterName, + HostID: uuid.NewString(), }) require.NoError(t, err) diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go index 159f5b255cd08..7210ef583b8eb 100644 --- a/lib/usagereporter/teleport/aggregating/service.go +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -120,6 +120,9 @@ func (r reportService) deleteUserActivityReport(ctx context.Context, report *pre return nil } +// listUserActivityReports returns the first `count` user activity reports +// according to the key order; as we store them with time and uuid in the key, +// this results in returning earlier reports first. func (r reportService) listUserActivityReports(ctx context.Context, count int) ([]*prehogv1.UserActivityReport, error) { rangeStart := backend.ExactKey(userActivityReportsPrefix) result, err := r.b.GetRange(ctx, rangeStart, backend.RangeEnd(rangeStart), count) diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index 47af86a195c05..f50b2ee244afd 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -59,13 +59,21 @@ var alertMessage = fmt.Sprintf("Teleport has failed to contact the usage reporti "Otherwise, contact Teleport Support at (%v).", alertGraceHours, defaultEndpointHostname, alertLink) +// SubmitterConfig contains the configuration for a [Submitter]. type SubmitterConfig struct { - Backend backend.Backend - Log logrus.FieldLogger - Status services.StatusInternal + // Backend is the backend to use to read reports and apply locks. Required. + Backend backend.Backend + // Log is the [logrus.FieldLogger] used for logging. Required. + Log logrus.FieldLogger + // Status is used to create or clear cluster alerts on a failure. Required. + Status services.StatusInternal + // Submitter is used to submit usage reports. Required. Submitter UsageReportsSubmitter } +// CheckAndSetDefaults checks the [SubmitterConfig] for validity, returning nil +// if there's no error. Idempotent but not concurrent safe, as it might need to +// write to the config to apply defaults. func (cfg *SubmitterConfig) CheckAndSetDefaults() error { if cfg.Backend == nil { return trace.BadParameter("missing Backend") From e206aa4020919986a3684fea6f9503a11cadd496 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 5 Apr 2023 11:22:14 +0200 Subject: [PATCH 29/32] Add some debug info to the lock and the alert --- lib/usagereporter/teleport/aggregating/service.go | 7 +++++-- lib/usagereporter/teleport/aggregating/service_test.go | 6 +++--- lib/usagereporter/teleport/aggregating/submitter.go | 8 +++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go index 7210ef583b8eb..e3ea629152a75 100644 --- a/lib/usagereporter/teleport/aggregating/service.go +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -142,10 +142,13 @@ func (r reportService) listUserActivityReports(ctx context.Context, count int) ( return reports, nil } -func (r reportService) createUserActivityReportsLock(ctx context.Context, ttl time.Duration) error { +func (r reportService) createUserActivityReportsLock(ctx context.Context, ttl time.Duration, payload []byte) error { + if len(payload) == 0 { + payload = []byte("null") + } if _, err := r.b.Create(ctx, backend.Item{ Key: backend.Key(userActivityReportsLock), - Value: []byte("null"), + Value: payload, Expires: r.b.Clock().Now().UTC().Add(ttl), }); err != nil { return trace.Wrap(err) diff --git a/lib/usagereporter/teleport/aggregating/service_test.go b/lib/usagereporter/teleport/aggregating/service_test.go index 536ec19499b85..ad9a6749511d7 100644 --- a/lib/usagereporter/teleport/aggregating/service_test.go +++ b/lib/usagereporter/teleport/aggregating/service_test.go @@ -97,11 +97,11 @@ func TestLock(t *testing.T) { svc := reportService{bk} - require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute)) + require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute, nil)) clk.Advance(time.Minute) - err = svc.createUserActivityReportsLock(ctx, 2*time.Minute) + err = svc.createUserActivityReportsLock(ctx, 2*time.Minute, nil) require.Error(t, err) require.True(t, trace.IsAlreadyExists(err)) clk.Advance(time.Minute) - require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute)) + require.NoError(t, svc.createUserActivityReportsLock(ctx, 2*time.Minute, nil)) } diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index f50b2ee244afd..0e088f0f85b6b 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -69,6 +69,10 @@ type SubmitterConfig struct { Status services.StatusInternal // Submitter is used to submit usage reports. Required. Submitter UsageReportsSubmitter + + // HostID is the host ID of the current Teleport instance, used in the lock + // payload and cluster alert description to help debugging. Optional. + HostID string } // CheckAndSetDefaults checks the [SubmitterConfig] for validity, returning nil @@ -140,7 +144,8 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { return } - if err := svc.createUserActivityReportsLock(ctx, submitLockDuration); err != nil { + debugPayload := fmt.Sprintf("%v %q", time.Now().Round(0), c.HostID) + if err := svc.createUserActivityReportsLock(ctx, submitLockDuration, []byte(debugPayload)); err != nil { if trace.IsAlreadyExists(err) { c.Log.Debugf("Failed to acquire lock %v, already held.", userActivityReportsLock) } else { @@ -176,6 +181,7 @@ func submitOnce(ctx context.Context, c SubmitterConfig) { c.Log.WithError(err).Errorf("Failed to create cluster alert %v.", alertName) return } + alert.Metadata.Description = debugPayload if err := c.Status.UpsertClusterAlert(ctx, alert); err != nil { c.Log.WithError(err).Errorf("Failed to upsert cluster alert %v.", alertName) } From 43c77f99963b5a327fc470496250b5c739e5a43b Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 5 Apr 2023 16:16:37 +0200 Subject: [PATCH 30/32] Sync prehog protos from cloud This includes a move to prehog.v1 for the new rpc and messages --- .../v1/prehogv1connect/teleport.connect.go | 87 ++ gen/proto/go/prehog/v1/teleport.pb.go | 536 +++++++ .../prehogv1alphaconnect/teleport.connect.go | 28 +- gen/proto/go/prehog/v1alpha/teleport.pb.go | 769 +++------- gen/proto/js/prehog/v1/teleport_grpc_pb.d.ts | 42 + gen/proto/js/prehog/v1/teleport_grpc_pb.js | 60 + gen/proto/js/prehog/v1/teleport_pb.d.ts | 165 +++ gen/proto/js/prehog/v1/teleport_pb.js | 1249 +++++++++++++++++ .../js/prehog/v1alpha/teleport_grpc_pb.d.ts | 17 - .../js/prehog/v1alpha/teleport_grpc_pb.js | 33 - gen/proto/js/prehog/v1alpha/teleport_pb.d.ts | 157 --- gen/proto/js/prehog/v1alpha/teleport_pb.js | 1229 ---------------- .../teleport/aggregating/reporter.go | 2 +- .../teleport/aggregating/service.go | 2 +- .../teleport/aggregating/service_test.go | 2 +- .../teleport/aggregating/submitter.go | 2 +- .../teleport/aggregating/submitter_test.go | 2 +- proto/prehog/v1/teleport.proto | 76 + proto/prehog/v1alpha/teleport.proto | 52 - 19 files changed, 2391 insertions(+), 2119 deletions(-) create mode 100644 gen/proto/go/prehog/v1/prehogv1connect/teleport.connect.go create mode 100644 gen/proto/go/prehog/v1/teleport.pb.go create mode 100644 gen/proto/js/prehog/v1/teleport_grpc_pb.d.ts create mode 100644 gen/proto/js/prehog/v1/teleport_grpc_pb.js create mode 100644 gen/proto/js/prehog/v1/teleport_pb.d.ts create mode 100644 gen/proto/js/prehog/v1/teleport_pb.js create mode 100644 proto/prehog/v1/teleport.proto diff --git a/gen/proto/go/prehog/v1/prehogv1connect/teleport.connect.go b/gen/proto/go/prehog/v1/prehogv1connect/teleport.connect.go new file mode 100644 index 0000000000000..775547de1d47c --- /dev/null +++ b/gen/proto/go/prehog/v1/prehogv1connect/teleport.connect.go @@ -0,0 +1,87 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: prehog/v1/teleport.proto + +package prehogv1connect + +import ( + context "context" + errors "errors" + connect_go "github.com/bufbuild/connect-go" + v1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect_go.IsAtLeastVersion0_1_0 + +const ( + // TeleportReportingServiceName is the fully-qualified name of the TeleportReportingService service. + TeleportReportingServiceName = "prehog.v1.TeleportReportingService" +) + +// TeleportReportingServiceClient is a client for the prehog.v1.TeleportReportingService service. +type TeleportReportingServiceClient interface { + SubmitUsageReports(context.Context, *connect_go.Request[v1.SubmitUsageReportsRequest]) (*connect_go.Response[v1.SubmitUsageReportsResponse], error) +} + +// NewTeleportReportingServiceClient constructs a client for the prehog.v1.TeleportReportingService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewTeleportReportingServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) TeleportReportingServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + return &teleportReportingServiceClient{ + submitUsageReports: connect_go.NewClient[v1.SubmitUsageReportsRequest, v1.SubmitUsageReportsResponse]( + httpClient, + baseURL+"/prehog.v1.TeleportReportingService/SubmitUsageReports", + opts..., + ), + } +} + +// teleportReportingServiceClient implements TeleportReportingServiceClient. +type teleportReportingServiceClient struct { + submitUsageReports *connect_go.Client[v1.SubmitUsageReportsRequest, v1.SubmitUsageReportsResponse] +} + +// SubmitUsageReports calls prehog.v1.TeleportReportingService.SubmitUsageReports. +func (c *teleportReportingServiceClient) SubmitUsageReports(ctx context.Context, req *connect_go.Request[v1.SubmitUsageReportsRequest]) (*connect_go.Response[v1.SubmitUsageReportsResponse], error) { + return c.submitUsageReports.CallUnary(ctx, req) +} + +// TeleportReportingServiceHandler is an implementation of the prehog.v1.TeleportReportingService +// service. +type TeleportReportingServiceHandler interface { + SubmitUsageReports(context.Context, *connect_go.Request[v1.SubmitUsageReportsRequest]) (*connect_go.Response[v1.SubmitUsageReportsResponse], error) +} + +// NewTeleportReportingServiceHandler builds an HTTP handler from the service implementation. It +// returns the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewTeleportReportingServiceHandler(svc TeleportReportingServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { + mux := http.NewServeMux() + mux.Handle("/prehog.v1.TeleportReportingService/SubmitUsageReports", connect_go.NewUnaryHandler( + "/prehog.v1.TeleportReportingService/SubmitUsageReports", + svc.SubmitUsageReports, + opts..., + )) + return "/prehog.v1.TeleportReportingService/", mux +} + +// UnimplementedTeleportReportingServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedTeleportReportingServiceHandler struct{} + +func (UnimplementedTeleportReportingServiceHandler) SubmitUsageReports(context.Context, *connect_go.Request[v1.SubmitUsageReportsRequest]) (*connect_go.Response[v1.SubmitUsageReportsResponse], error) { + return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("prehog.v1.TeleportReportingService.SubmitUsageReports is not implemented")) +} diff --git a/gen/proto/go/prehog/v1/teleport.pb.go b/gen/proto/go/prehog/v1/teleport.pb.go new file mode 100644 index 0000000000000..8d5dc217f75c8 --- /dev/null +++ b/gen/proto/go/prehog/v1/teleport.pb.go @@ -0,0 +1,536 @@ +// 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: prehog/v1/teleport.proto + +package prehogv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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) +) + +type UserActivityReport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // randomly generated UUID for this specific report, 16 bytes (in string order) + ReportUuid []byte `protobuf:"bytes,1,opt,name=report_uuid,json=reportUuid,proto3" json:"report_uuid,omitempty"` + // anonymized, 32 bytes (HMAC-SHA-256) + ClusterName []byte `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` + // anonymized, 32 bytes (HMAC-SHA-256) + ReporterHostid []byte `protobuf:"bytes,3,opt,name=reporter_hostid,json=reporterHostid,proto3" json:"reporter_hostid,omitempty"` + // beginning of the time window for this data; ending is not specified but is + // intended to be at most 15 minutes + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + Records []*UserActivityRecord `protobuf:"bytes,5,rep,name=records,proto3" json:"records,omitempty"` +} + +func (x *UserActivityReport) Reset() { + *x = UserActivityReport{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1_teleport_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserActivityReport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserActivityReport) ProtoMessage() {} + +func (x *UserActivityReport) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1_teleport_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 UserActivityReport.ProtoReflect.Descriptor instead. +func (*UserActivityReport) Descriptor() ([]byte, []int) { + return file_prehog_v1_teleport_proto_rawDescGZIP(), []int{0} +} + +func (x *UserActivityReport) GetReportUuid() []byte { + if x != nil { + return x.ReportUuid + } + return nil +} + +func (x *UserActivityReport) GetClusterName() []byte { + if x != nil { + return x.ClusterName + } + return nil +} + +func (x *UserActivityReport) GetReporterHostid() []byte { + if x != nil { + return x.ReporterHostid + } + return nil +} + +func (x *UserActivityReport) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *UserActivityReport) GetRecords() []*UserActivityRecord { + if x != nil { + return x.Records + } + return nil +} + +type UserActivityRecord struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // anonymized, 32 bytes (HMAC-SHA-256) + UserName []byte `protobuf:"bytes,1,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` + // counter of user.login events + Logins uint64 `protobuf:"varint,2,opt,name=logins,proto3" json:"logins,omitempty"` + // counter of session.start events (non-Kube) + SshSessions uint64 `protobuf:"varint,3,opt,name=ssh_sessions,json=sshSessions,proto3" json:"ssh_sessions,omitempty"` + // counter of app.session.start events (non-TCP) + AppSessions uint64 `protobuf:"varint,4,opt,name=app_sessions,json=appSessions,proto3" json:"app_sessions,omitempty"` + // counter of session.start events (only Kube) + KubeSessions uint64 `protobuf:"varint,5,opt,name=kube_sessions,json=kubeSessions,proto3" json:"kube_sessions,omitempty"` + // counter of db.session.start events + DbSessions uint64 `protobuf:"varint,6,opt,name=db_sessions,json=dbSessions,proto3" json:"db_sessions,omitempty"` + // counter of windows.desktop.session.start events + DesktopSessions uint64 `protobuf:"varint,7,opt,name=desktop_sessions,json=desktopSessions,proto3" json:"desktop_sessions,omitempty"` + // counter of app.session.start events (only TCP) + AppTcpSessions uint64 `protobuf:"varint,8,opt,name=app_tcp_sessions,json=appTcpSessions,proto3" json:"app_tcp_sessions,omitempty"` + // counter of port events + SshPortSessions uint64 `protobuf:"varint,9,opt,name=ssh_port_sessions,json=sshPortSessions,proto3" json:"ssh_port_sessions,omitempty"` + // counter of kube.request events + KubeRequests uint64 `protobuf:"varint,10,opt,name=kube_requests,json=kubeRequests,proto3" json:"kube_requests,omitempty"` + // counter of sftp events + SftpEvents uint64 `protobuf:"varint,11,opt,name=sftp_events,json=sftpEvents,proto3" json:"sftp_events,omitempty"` +} + +func (x *UserActivityRecord) Reset() { + *x = UserActivityRecord{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1_teleport_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserActivityRecord) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserActivityRecord) ProtoMessage() {} + +func (x *UserActivityRecord) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1_teleport_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 UserActivityRecord.ProtoReflect.Descriptor instead. +func (*UserActivityRecord) Descriptor() ([]byte, []int) { + return file_prehog_v1_teleport_proto_rawDescGZIP(), []int{1} +} + +func (x *UserActivityRecord) GetUserName() []byte { + if x != nil { + return x.UserName + } + return nil +} + +func (x *UserActivityRecord) GetLogins() uint64 { + if x != nil { + return x.Logins + } + return 0 +} + +func (x *UserActivityRecord) GetSshSessions() uint64 { + if x != nil { + return x.SshSessions + } + return 0 +} + +func (x *UserActivityRecord) GetAppSessions() uint64 { + if x != nil { + return x.AppSessions + } + return 0 +} + +func (x *UserActivityRecord) GetKubeSessions() uint64 { + if x != nil { + return x.KubeSessions + } + return 0 +} + +func (x *UserActivityRecord) GetDbSessions() uint64 { + if x != nil { + return x.DbSessions + } + return 0 +} + +func (x *UserActivityRecord) GetDesktopSessions() uint64 { + if x != nil { + return x.DesktopSessions + } + return 0 +} + +func (x *UserActivityRecord) GetAppTcpSessions() uint64 { + if x != nil { + return x.AppTcpSessions + } + return 0 +} + +func (x *UserActivityRecord) GetSshPortSessions() uint64 { + if x != nil { + return x.SshPortSessions + } + return 0 +} + +func (x *UserActivityRecord) GetKubeRequests() uint64 { + if x != nil { + return x.KubeRequests + } + return 0 +} + +func (x *UserActivityRecord) GetSftpEvents() uint64 { + if x != nil { + return x.SftpEvents + } + return 0 +} + +type SubmitUsageReportsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // at most 10 in a single RPC, each shouldn't exceed 128KiB or so + UserActivity []*UserActivityReport `protobuf:"bytes,1,rep,name=user_activity,json=userActivity,proto3" json:"user_activity,omitempty"` +} + +func (x *SubmitUsageReportsRequest) Reset() { + *x = SubmitUsageReportsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1_teleport_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitUsageReportsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitUsageReportsRequest) ProtoMessage() {} + +func (x *SubmitUsageReportsRequest) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1_teleport_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 SubmitUsageReportsRequest.ProtoReflect.Descriptor instead. +func (*SubmitUsageReportsRequest) Descriptor() ([]byte, []int) { + return file_prehog_v1_teleport_proto_rawDescGZIP(), []int{2} +} + +func (x *SubmitUsageReportsRequest) GetUserActivity() []*UserActivityReport { + if x != nil { + return x.UserActivity + } + return nil +} + +type SubmitUsageReportsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // randomly generated UUID for this specific batch, 16 bytes (in string order) + BatchUuid []byte `protobuf:"bytes,1,opt,name=batch_uuid,json=batchUuid,proto3" json:"batch_uuid,omitempty"` +} + +func (x *SubmitUsageReportsResponse) Reset() { + *x = SubmitUsageReportsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_prehog_v1_teleport_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitUsageReportsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitUsageReportsResponse) ProtoMessage() {} + +func (x *SubmitUsageReportsResponse) ProtoReflect() protoreflect.Message { + mi := &file_prehog_v1_teleport_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 SubmitUsageReportsResponse.ProtoReflect.Descriptor instead. +func (*SubmitUsageReportsResponse) Descriptor() ([]byte, []int) { + return file_prehog_v1_teleport_proto_rawDescGZIP(), []int{3} +} + +func (x *SubmitUsageReportsResponse) GetBatchUuid() []byte { + if x != nil { + return x.BatchUuid + } + return nil +} + +var File_prehog_v1_teleport_proto protoreflect.FileDescriptor + +var file_prehog_v1_teleport_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x72, 0x65, 0x68, + 0x6f, 0x67, 0x2e, 0x76, 0x31, 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, 0xf5, 0x01, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x5f, 0x68, 0x6f, + 0x73, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x9c, + 0x03, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, + 0x68, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x73, 0x73, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x62, 0x5f, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x64, 0x62, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, + 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0f, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x70, 0x70, + 0x54, 0x63, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x73, + 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, 0x65, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x6b, 0x75, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x66, 0x74, 0x70, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x73, 0x66, 0x74, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x5f, 0x0a, + 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0d, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x3b, + 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x55, 0x75, 0x69, 0x64, 0x32, 0x7f, 0x0a, 0x18, 0x54, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x24, 0x2e, + 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x43, 0x5a, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, + 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_prehog_v1_teleport_proto_rawDescOnce sync.Once + file_prehog_v1_teleport_proto_rawDescData = file_prehog_v1_teleport_proto_rawDesc +) + +func file_prehog_v1_teleport_proto_rawDescGZIP() []byte { + file_prehog_v1_teleport_proto_rawDescOnce.Do(func() { + file_prehog_v1_teleport_proto_rawDescData = protoimpl.X.CompressGZIP(file_prehog_v1_teleport_proto_rawDescData) + }) + return file_prehog_v1_teleport_proto_rawDescData +} + +var file_prehog_v1_teleport_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_prehog_v1_teleport_proto_goTypes = []interface{}{ + (*UserActivityReport)(nil), // 0: prehog.v1.UserActivityReport + (*UserActivityRecord)(nil), // 1: prehog.v1.UserActivityRecord + (*SubmitUsageReportsRequest)(nil), // 2: prehog.v1.SubmitUsageReportsRequest + (*SubmitUsageReportsResponse)(nil), // 3: prehog.v1.SubmitUsageReportsResponse + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp +} +var file_prehog_v1_teleport_proto_depIdxs = []int32{ + 4, // 0: prehog.v1.UserActivityReport.start_time:type_name -> google.protobuf.Timestamp + 1, // 1: prehog.v1.UserActivityReport.records:type_name -> prehog.v1.UserActivityRecord + 0, // 2: prehog.v1.SubmitUsageReportsRequest.user_activity:type_name -> prehog.v1.UserActivityReport + 2, // 3: prehog.v1.TeleportReportingService.SubmitUsageReports:input_type -> prehog.v1.SubmitUsageReportsRequest + 3, // 4: prehog.v1.TeleportReportingService.SubmitUsageReports:output_type -> prehog.v1.SubmitUsageReportsResponse + 4, // [4:5] is the sub-list for method output_type + 3, // [3:4] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_prehog_v1_teleport_proto_init() } +func file_prehog_v1_teleport_proto_init() { + if File_prehog_v1_teleport_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_prehog_v1_teleport_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserActivityReport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1_teleport_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserActivityRecord); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1_teleport_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitUsageReportsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prehog_v1_teleport_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitUsageReportsResponse); 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_prehog_v1_teleport_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_prehog_v1_teleport_proto_goTypes, + DependencyIndexes: file_prehog_v1_teleport_proto_depIdxs, + MessageInfos: file_prehog_v1_teleport_proto_msgTypes, + }.Build() + File_prehog_v1_teleport_proto = out.File + file_prehog_v1_teleport_proto_rawDesc = nil + file_prehog_v1_teleport_proto_goTypes = nil + file_prehog_v1_teleport_proto_depIdxs = nil +} diff --git a/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go index 753252fea4721..8ced4f3e66918 100644 --- a/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go +++ b/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect/teleport.connect.go @@ -32,7 +32,6 @@ type TeleportReportingServiceClient interface { SubmitEvent(context.Context, *connect_go.Request[v1alpha.SubmitEventRequest]) (*connect_go.Response[v1alpha.SubmitEventResponse], error) SubmitEvents(context.Context, *connect_go.Request[v1alpha.SubmitEventsRequest]) (*connect_go.Response[v1alpha.SubmitEventsResponse], error) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) - SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) } // NewTeleportReportingServiceClient constructs a client for the @@ -60,20 +59,14 @@ func NewTeleportReportingServiceClient(httpClient connect_go.HTTPClient, baseURL baseURL+"/prehog.v1alpha.TeleportReportingService/HelloTeleport", opts..., ), - submitUsageReports: connect_go.NewClient[v1alpha.SubmitUsageReportsRequest, v1alpha.SubmitUsageReportsResponse]( - httpClient, - baseURL+"/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", - opts..., - ), } } // teleportReportingServiceClient implements TeleportReportingServiceClient. type teleportReportingServiceClient struct { - submitEvent *connect_go.Client[v1alpha.SubmitEventRequest, v1alpha.SubmitEventResponse] - submitEvents *connect_go.Client[v1alpha.SubmitEventsRequest, v1alpha.SubmitEventsResponse] - helloTeleport *connect_go.Client[v1alpha.HelloTeleportRequest, v1alpha.HelloTeleportResponse] - submitUsageReports *connect_go.Client[v1alpha.SubmitUsageReportsRequest, v1alpha.SubmitUsageReportsResponse] + submitEvent *connect_go.Client[v1alpha.SubmitEventRequest, v1alpha.SubmitEventResponse] + submitEvents *connect_go.Client[v1alpha.SubmitEventsRequest, v1alpha.SubmitEventsResponse] + helloTeleport *connect_go.Client[v1alpha.HelloTeleportRequest, v1alpha.HelloTeleportResponse] } // SubmitEvent calls prehog.v1alpha.TeleportReportingService.SubmitEvent. @@ -93,11 +86,6 @@ func (c *teleportReportingServiceClient) HelloTeleport(ctx context.Context, req return c.helloTeleport.CallUnary(ctx, req) } -// SubmitUsageReports calls prehog.v1alpha.TeleportReportingService.SubmitUsageReports. -func (c *teleportReportingServiceClient) SubmitUsageReports(ctx context.Context, req *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) { - return c.submitUsageReports.CallUnary(ctx, req) -} - // TeleportReportingServiceHandler is an implementation of the // prehog.v1alpha.TeleportReportingService service. type TeleportReportingServiceHandler interface { @@ -105,7 +93,6 @@ type TeleportReportingServiceHandler interface { SubmitEvent(context.Context, *connect_go.Request[v1alpha.SubmitEventRequest]) (*connect_go.Response[v1alpha.SubmitEventResponse], error) SubmitEvents(context.Context, *connect_go.Request[v1alpha.SubmitEventsRequest]) (*connect_go.Response[v1alpha.SubmitEventsResponse], error) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) - SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) } // NewTeleportReportingServiceHandler builds an HTTP handler from the service implementation. It @@ -130,11 +117,6 @@ func NewTeleportReportingServiceHandler(svc TeleportReportingServiceHandler, opt svc.HelloTeleport, opts..., )) - mux.Handle("/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", connect_go.NewUnaryHandler( - "/prehog.v1alpha.TeleportReportingService/SubmitUsageReports", - svc.SubmitUsageReports, - opts..., - )) return "/prehog.v1alpha.TeleportReportingService/", mux } @@ -152,7 +134,3 @@ func (UnimplementedTeleportReportingServiceHandler) SubmitEvents(context.Context func (UnimplementedTeleportReportingServiceHandler) HelloTeleport(context.Context, *connect_go.Request[v1alpha.HelloTeleportRequest]) (*connect_go.Response[v1alpha.HelloTeleportResponse], error) { return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("prehog.v1alpha.TeleportReportingService.HelloTeleport is not implemented")) } - -func (UnimplementedTeleportReportingServiceHandler) SubmitUsageReports(context.Context, *connect_go.Request[v1alpha.SubmitUsageReportsRequest]) (*connect_go.Response[v1alpha.SubmitUsageReportsResponse], error) { - return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("prehog.v1alpha.TeleportReportingService.SubmitUsageReports is not implemented")) -} diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index e2d50440ba029..50ffd9d6042b4 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -3377,323 +3377,6 @@ func (*HelloTeleportResponse) Descriptor() ([]byte, []int) { return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{43} } -type UserActivityReport struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // randomly generated UUID for this specific report, 16 bytes (in string order) - ReportUuid []byte `protobuf:"bytes,1,opt,name=report_uuid,json=reportUuid,proto3" json:"report_uuid,omitempty"` - // anonymized, 32 bytes (HMAC-SHA-256) - ClusterName []byte `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` - // anonymized, 32 bytes (HMAC-SHA-256) - ReporterHostid []byte `protobuf:"bytes,3,opt,name=reporter_hostid,json=reporterHostid,proto3" json:"reporter_hostid,omitempty"` - // beginning of the time window for this data; ending is not specified but is - // intended to be at most 15 minutes - StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - Records []*UserActivityRecord `protobuf:"bytes,5,rep,name=records,proto3" json:"records,omitempty"` -} - -func (x *UserActivityReport) Reset() { - *x = UserActivityReport{} - if protoimpl.UnsafeEnabled { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[44] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UserActivityReport) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UserActivityReport) ProtoMessage() {} - -func (x *UserActivityReport) ProtoReflect() protoreflect.Message { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[44] - 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 UserActivityReport.ProtoReflect.Descriptor instead. -func (*UserActivityReport) Descriptor() ([]byte, []int) { - return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{44} -} - -func (x *UserActivityReport) GetReportUuid() []byte { - if x != nil { - return x.ReportUuid - } - return nil -} - -func (x *UserActivityReport) GetClusterName() []byte { - if x != nil { - return x.ClusterName - } - return nil -} - -func (x *UserActivityReport) GetReporterHostid() []byte { - if x != nil { - return x.ReporterHostid - } - return nil -} - -func (x *UserActivityReport) GetStartTime() *timestamppb.Timestamp { - if x != nil { - return x.StartTime - } - return nil -} - -func (x *UserActivityReport) GetRecords() []*UserActivityRecord { - if x != nil { - return x.Records - } - return nil -} - -type UserActivityRecord struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // anonymized, 32 bytes (HMAC-SHA-256) - UserName []byte `protobuf:"bytes,1,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` - // counter of user.login events - Logins uint64 `protobuf:"varint,2,opt,name=logins,proto3" json:"logins,omitempty"` - // counter of session.start events (non-Kube) - SshSessions uint64 `protobuf:"varint,3,opt,name=ssh_sessions,json=sshSessions,proto3" json:"ssh_sessions,omitempty"` - // counter of app.session.start events (non-TCP) - AppSessions uint64 `protobuf:"varint,4,opt,name=app_sessions,json=appSessions,proto3" json:"app_sessions,omitempty"` - // counter of session.start events (only Kube) - KubeSessions uint64 `protobuf:"varint,5,opt,name=kube_sessions,json=kubeSessions,proto3" json:"kube_sessions,omitempty"` - // counter of db.session.start events - DbSessions uint64 `protobuf:"varint,6,opt,name=db_sessions,json=dbSessions,proto3" json:"db_sessions,omitempty"` - // counter of windows.desktop.session.start events - DesktopSessions uint64 `protobuf:"varint,7,opt,name=desktop_sessions,json=desktopSessions,proto3" json:"desktop_sessions,omitempty"` - // counter of app.session.start events (only TCP) - AppTcpSessions uint64 `protobuf:"varint,8,opt,name=app_tcp_sessions,json=appTcpSessions,proto3" json:"app_tcp_sessions,omitempty"` - // counter of port events - SshPortSessions uint64 `protobuf:"varint,9,opt,name=ssh_port_sessions,json=sshPortSessions,proto3" json:"ssh_port_sessions,omitempty"` - // counter of kube.request events - KubeRequests uint64 `protobuf:"varint,10,opt,name=kube_requests,json=kubeRequests,proto3" json:"kube_requests,omitempty"` - // counter of sftp events - SftpEvents uint64 `protobuf:"varint,11,opt,name=sftp_events,json=sftpEvents,proto3" json:"sftp_events,omitempty"` -} - -func (x *UserActivityRecord) Reset() { - *x = UserActivityRecord{} - if protoimpl.UnsafeEnabled { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UserActivityRecord) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UserActivityRecord) ProtoMessage() {} - -func (x *UserActivityRecord) ProtoReflect() protoreflect.Message { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[45] - 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 UserActivityRecord.ProtoReflect.Descriptor instead. -func (*UserActivityRecord) Descriptor() ([]byte, []int) { - return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{45} -} - -func (x *UserActivityRecord) GetUserName() []byte { - if x != nil { - return x.UserName - } - return nil -} - -func (x *UserActivityRecord) GetLogins() uint64 { - if x != nil { - return x.Logins - } - return 0 -} - -func (x *UserActivityRecord) GetSshSessions() uint64 { - if x != nil { - return x.SshSessions - } - return 0 -} - -func (x *UserActivityRecord) GetAppSessions() uint64 { - if x != nil { - return x.AppSessions - } - return 0 -} - -func (x *UserActivityRecord) GetKubeSessions() uint64 { - if x != nil { - return x.KubeSessions - } - return 0 -} - -func (x *UserActivityRecord) GetDbSessions() uint64 { - if x != nil { - return x.DbSessions - } - return 0 -} - -func (x *UserActivityRecord) GetDesktopSessions() uint64 { - if x != nil { - return x.DesktopSessions - } - return 0 -} - -func (x *UserActivityRecord) GetAppTcpSessions() uint64 { - if x != nil { - return x.AppTcpSessions - } - return 0 -} - -func (x *UserActivityRecord) GetSshPortSessions() uint64 { - if x != nil { - return x.SshPortSessions - } - return 0 -} - -func (x *UserActivityRecord) GetKubeRequests() uint64 { - if x != nil { - return x.KubeRequests - } - return 0 -} - -func (x *UserActivityRecord) GetSftpEvents() uint64 { - if x != nil { - return x.SftpEvents - } - return 0 -} - -type SubmitUsageReportsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserActivity []*UserActivityReport `protobuf:"bytes,1,rep,name=user_activity,json=userActivity,proto3" json:"user_activity,omitempty"` -} - -func (x *SubmitUsageReportsRequest) Reset() { - *x = SubmitUsageReportsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubmitUsageReportsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitUsageReportsRequest) ProtoMessage() {} - -func (x *SubmitUsageReportsRequest) ProtoReflect() protoreflect.Message { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[46] - 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 SubmitUsageReportsRequest.ProtoReflect.Descriptor instead. -func (*SubmitUsageReportsRequest) Descriptor() ([]byte, []int) { - return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{46} -} - -func (x *SubmitUsageReportsRequest) GetUserActivity() []*UserActivityReport { - if x != nil { - return x.UserActivity - } - return nil -} - -type SubmitUsageReportsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // randomly generated UUID for this specific batch, 16 bytes (in string order) - BatchUuid []byte `protobuf:"bytes,1,opt,name=batch_uuid,json=batchUuid,proto3" json:"batch_uuid,omitempty"` -} - -func (x *SubmitUsageReportsResponse) Reset() { - *x = SubmitUsageReportsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubmitUsageReportsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitUsageReportsResponse) ProtoMessage() {} - -func (x *SubmitUsageReportsResponse) ProtoReflect() protoreflect.Message { - mi := &file_prehog_v1alpha_teleport_proto_msgTypes[47] - 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 SubmitUsageReportsResponse.ProtoReflect.Descriptor instead. -func (*SubmitUsageReportsResponse) Descriptor() ([]byte, []int) { - return file_prehog_v1alpha_teleport_proto_rawDescGZIP(), []int{47} -} - -func (x *SubmitUsageReportsResponse) GetBatchUuid() []byte { - if x != nil { - return x.BatchUuid - } - return nil -} - var File_prehog_v1alpha_teleport_proto protoreflect.FileDescriptor var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ @@ -4315,226 +3998,167 @@ var file_prehog_v1alpha_teleport_proto_rawDesc = []byte{ 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xfa, 0x01, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x72, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, - 0x22, 0x9c, 0x03, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, - 0x73, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x62, 0x5f, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x64, 0x62, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x6b, - 0x74, 0x6f, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x70, 0x70, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, - 0x70, 0x70, 0x54, 0x63, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, - 0x11, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x50, 0x6f, 0x72, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x75, 0x62, - 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x1f, - 0x0a, 0x0b, 0x73, 0x66, 0x74, 0x70, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x66, 0x74, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x64, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0d, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, - 0x69, 0x76, 0x69, 0x74, 0x79, 0x22, 0x3b, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x75, 0x75, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x55, 0x75, - 0x69, 0x64, 0x2a, 0xe2, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, - 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, - 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, 0x5f, - 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x52, 0x56, - 0x45, 0x52, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, 0x45, - 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4f, 0x50, - 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xed, 0x0d, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x1d, - 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x1c, 0x0a, 0x18, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x20, 0x0a, - 0x1c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x52, 0x4e, 0x45, 0x54, 0x45, 0x53, 0x10, 0x02, 0x12, - 0x33, 0x0a, 0x2f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, - 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, - 0x45, 0x44, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, - 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, - 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, - 0x53, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, - 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x4c, - 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, - 0x53, 0x5f, 0x52, 0x44, 0x53, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x6e, 0x73, 0x65, 0x2a, 0xe2, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, + 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x5f, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x52, + 0x56, 0x45, 0x52, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, + 0x45, 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4f, + 0x50, 0x45, 0x4e, 0x53, 0x53, 0x48, 0x10, 0x06, 0x2a, 0xed, 0x0d, 0x0a, 0x10, 0x44, 0x69, 0x73, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x0a, + 0x1d, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x1c, 0x0a, 0x18, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x20, + 0x0a, 0x1c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x4b, 0x55, 0x42, 0x45, 0x52, 0x4e, 0x45, 0x54, 0x45, 0x53, 0x10, 0x02, + 0x12, 0x33, 0x0a, 0x2f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, + 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, + 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, + 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, + 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, + 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, - 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x52, 0x44, 0x53, 0x10, - 0x07, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x08, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, - 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, - 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x43, 0x50, 0x10, 0x09, - 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, 0x45, - 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x0a, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x53, 0x45, + 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x2b, 0x0a, 0x27, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, + 0x45, 0x53, 0x5f, 0x52, 0x44, 0x53, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, + 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x52, 0x44, 0x53, + 0x10, 0x07, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x10, 0x08, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x43, 0x50, 0x10, + 0x09, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x44, + 0x45, 0x53, 0x4b, 0x54, 0x4f, 0x50, 0x10, 0x0a, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, + 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, + 0x5f, 0x52, 0x44, 0x53, 0x10, 0x0b, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, + 0x44, 0x53, 0x48, 0x49, 0x46, 0x54, 0x10, 0x0c, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, + 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, + 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0d, 0x12, 0x30, + 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, + 0x49, 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0e, + 0x12, 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, + 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x0f, 0x12, 0x28, 0x0a, + 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, + 0x4c, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x10, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, - 0x52, 0x44, 0x53, 0x10, 0x0b, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, + 0x47, 0x43, 0x50, 0x10, 0x11, 0x12, 0x3b, 0x0a, 0x37, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x44, - 0x53, 0x48, 0x49, 0x46, 0x54, 0x10, 0x0c, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, 0x4f, - 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, - 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, - 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0d, 0x12, 0x30, 0x0a, - 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, 0x49, - 0x53, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0e, 0x12, - 0x2b, 0x0a, 0x27, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, - 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x50, 0x10, 0x0f, 0x12, 0x28, 0x0a, 0x24, - 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, - 0x5f, 0x47, 0x43, 0x50, 0x10, 0x10, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, - 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x47, - 0x43, 0x50, 0x10, 0x11, 0x12, 0x3b, 0x0a, 0x37, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, - 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, - 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x44, 0x53, - 0x48, 0x49, 0x46, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x4c, 0x45, 0x53, 0x53, 0x10, - 0x12, 0x12, 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x48, 0x49, 0x46, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x4c, 0x45, 0x53, 0x53, + 0x10, 0x12, 0x12, 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, + 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, + 0x13, 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, - 0x50, 0x4f, 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x13, - 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x44, - 0x59, 0x4e, 0x41, 0x4d, 0x4f, 0x44, 0x42, 0x10, 0x14, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, - 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, - 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, 0x53, 0x41, 0x4e, 0x44, 0x52, - 0x41, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x50, 0x41, 0x43, 0x45, 0x53, 0x10, 0x15, 0x12, 0x34, 0x0a, - 0x30, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, 0x53, - 0x41, 0x4e, 0x44, 0x52, 0x41, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, - 0x44, 0x10, 0x16, 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, - 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, - 0x45, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x5f, - 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x17, 0x12, 0x30, 0x0a, - 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, 0x49, - 0x53, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x18, 0x12, - 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, - 0x44, 0x49, 0x53, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0x59, 0x44, 0x42, 0x10, 0x19, 0x12, 0x30, + 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x4f, 0x44, 0x42, 0x10, 0x14, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, + 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, + 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, 0x53, 0x41, 0x4e, 0x44, + 0x52, 0x41, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x50, 0x41, 0x43, 0x45, 0x53, 0x10, 0x15, 0x12, 0x34, + 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, + 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x53, + 0x53, 0x41, 0x4e, 0x44, 0x52, 0x41, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, + 0x45, 0x44, 0x10, 0x16, 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, + 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, + 0x53, 0x45, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, + 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x17, 0x12, 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x44, - 0x49, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x1a, - 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, + 0x49, 0x53, 0x5f, 0x45, 0x4c, 0x41, 0x53, 0x54, 0x49, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x18, + 0x12, 0x2d, 0x0a, 0x29, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, - 0x45, 0x44, 0x49, 0x53, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x4c, - 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x2a, 0x0a, 0x26, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, 0x41, - 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1c, 0x12, 0x2e, 0x0a, 0x2a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, - 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x41, - 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1d, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, - 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4d, - 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x1e, 0x12, 0x36, 0x0a, 0x32, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x43, 0x4b, 0x52, 0x4f, 0x41, - 0x43, 0x48, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, - 0x10, 0x1f, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, - 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x41, 0x54, 0x4c, 0x41, 0x53, 0x10, 0x20, - 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, - 0x4e, 0x4f, 0x57, 0x46, 0x4c, 0x41, 0x4b, 0x45, 0x10, 0x21, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, - 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x44, 0x53, - 0x5f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x10, 0x22, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x4f, - 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, - 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x23, 0x12, 0x37, - 0x0a, 0x33, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x5f, 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, - 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x24, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, - 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, - 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, - 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, - 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, 0x50, - 0x50, 0x45, 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, - 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, - 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xa3, 0x03, - 0x0a, 0x18, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, - 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, - 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, - 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, - 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, + 0x45, 0x44, 0x49, 0x53, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0x59, 0x44, 0x42, 0x10, 0x19, 0x12, + 0x30, 0x0a, 0x2c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, + 0x44, 0x49, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, + 0x1a, 0x12, 0x38, 0x0a, 0x34, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, + 0x52, 0x45, 0x44, 0x49, 0x53, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x45, + 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x2a, 0x0a, 0x26, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x59, 0x53, 0x51, 0x4c, 0x5f, + 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1c, 0x12, 0x2e, 0x0a, 0x2a, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, + 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, + 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x1d, 0x12, 0x32, 0x0a, 0x2e, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, + 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x51, 0x4c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, + 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x1e, 0x12, 0x36, 0x0a, 0x32, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x43, 0x4b, 0x52, 0x4f, + 0x41, 0x43, 0x48, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x48, 0x4f, 0x53, 0x54, 0x45, + 0x44, 0x10, 0x1f, 0x12, 0x2c, 0x0a, 0x28, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, + 0x45, 0x5f, 0x4d, 0x4f, 0x4e, 0x47, 0x4f, 0x44, 0x42, 0x5f, 0x41, 0x54, 0x4c, 0x41, 0x53, 0x10, + 0x20, 0x12, 0x28, 0x0a, 0x24, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, + 0x53, 0x4e, 0x4f, 0x57, 0x46, 0x4c, 0x41, 0x4b, 0x45, 0x10, 0x21, 0x12, 0x2c, 0x0a, 0x28, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, + 0x5f, 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x44, + 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x10, 0x22, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x49, 0x53, + 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, + 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, + 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x23, 0x12, + 0x37, 0x0a, 0x33, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x4f, + 0x55, 0x52, 0x43, 0x45, 0x5f, 0x44, 0x4f, 0x43, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, + 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, + 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x24, 0x2a, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x69, 0x73, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x44, + 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, + 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, + 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x4b, 0x49, + 0x50, 0x50, 0x45, 0x44, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, + 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x32, 0xb4, + 0x02, 0x0a, 0x18, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0b, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x5b, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, + 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, + 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4550,7 +4174,7 @@ func file_prehog_v1alpha_teleport_proto_rawDescGZIP() []byte { } var file_prehog_v1alpha_teleport_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_prehog_v1alpha_teleport_proto_msgTypes = make([]protoimpl.MessageInfo, 48) +var file_prehog_v1alpha_teleport_proto_msgTypes = make([]protoimpl.MessageInfo, 44) var file_prehog_v1alpha_teleport_proto_goTypes = []interface{}{ (ResourceKind)(0), // 0: prehog.v1alpha.ResourceKind (DiscoverResource)(0), // 1: prehog.v1alpha.DiscoverResource @@ -4599,16 +4223,12 @@ var file_prehog_v1alpha_teleport_proto_goTypes = []interface{}{ (*SubmitEventsResponse)(nil), // 44: prehog.v1alpha.SubmitEventsResponse (*HelloTeleportRequest)(nil), // 45: prehog.v1alpha.HelloTeleportRequest (*HelloTeleportResponse)(nil), // 46: prehog.v1alpha.HelloTeleportResponse - (*UserActivityReport)(nil), // 47: prehog.v1alpha.UserActivityReport - (*UserActivityRecord)(nil), // 48: prehog.v1alpha.UserActivityRecord - (*SubmitUsageReportsRequest)(nil), // 49: prehog.v1alpha.SubmitUsageReportsRequest - (*SubmitUsageReportsResponse)(nil), // 50: prehog.v1alpha.SubmitUsageReportsResponse - (*durationpb.Duration)(nil), // 51: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 52: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 47: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 48: google.protobuf.Timestamp } var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 0, // 0: prehog.v1alpha.ResourceHeartbeatEvent.resource_kind:type_name -> prehog.v1alpha.ResourceKind - 51, // 1: prehog.v1alpha.UserCertificateIssuedEvent.ttl:type_name -> google.protobuf.Duration + 47, // 1: prehog.v1alpha.UserCertificateIssuedEvent.ttl:type_name -> google.protobuf.Duration 1, // 2: prehog.v1alpha.DiscoverResourceMetadata.resource:type_name -> prehog.v1alpha.DiscoverResource 2, // 3: prehog.v1alpha.DiscoverStepStatus.status:type_name -> prehog.v1alpha.DiscoverStatus 18, // 4: prehog.v1alpha.UIDiscoverStartedEvent.metadata:type_name -> prehog.v1alpha.DiscoverMetadata @@ -4646,7 +4266,7 @@ var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 18, // 36: prehog.v1alpha.UIDiscoverCompletedEvent.metadata:type_name -> prehog.v1alpha.DiscoverMetadata 19, // 37: prehog.v1alpha.UIDiscoverCompletedEvent.resource:type_name -> prehog.v1alpha.DiscoverResourceMetadata 20, // 38: prehog.v1alpha.UIDiscoverCompletedEvent.status:type_name -> prehog.v1alpha.DiscoverStepStatus - 52, // 39: prehog.v1alpha.SubmitEventRequest.timestamp:type_name -> google.protobuf.Timestamp + 48, // 39: prehog.v1alpha.SubmitEventRequest.timestamp:type_name -> google.protobuf.Timestamp 3, // 40: prehog.v1alpha.SubmitEventRequest.user_login:type_name -> prehog.v1alpha.UserLoginEvent 4, // 41: prehog.v1alpha.SubmitEventRequest.sso_create:type_name -> prehog.v1alpha.SSOCreateEvent 5, // 42: prehog.v1alpha.SubmitEventRequest.resource_create:type_name -> prehog.v1alpha.ResourceCreateEvent @@ -4684,22 +4304,17 @@ var file_prehog_v1alpha_teleport_proto_depIdxs = []int32{ 40, // 74: prehog.v1alpha.SubmitEventRequest.agent_metadata_event:type_name -> prehog.v1alpha.AgentMetadataEvent 6, // 75: prehog.v1alpha.SubmitEventRequest.resource_heartbeat:type_name -> prehog.v1alpha.ResourceHeartbeatEvent 41, // 76: prehog.v1alpha.SubmitEventsRequest.events:type_name -> prehog.v1alpha.SubmitEventRequest - 52, // 77: prehog.v1alpha.UserActivityReport.start_time:type_name -> google.protobuf.Timestamp - 48, // 78: prehog.v1alpha.UserActivityReport.records:type_name -> prehog.v1alpha.UserActivityRecord - 47, // 79: prehog.v1alpha.SubmitUsageReportsRequest.user_activity:type_name -> prehog.v1alpha.UserActivityReport - 41, // 80: prehog.v1alpha.TeleportReportingService.SubmitEvent:input_type -> prehog.v1alpha.SubmitEventRequest - 43, // 81: prehog.v1alpha.TeleportReportingService.SubmitEvents:input_type -> prehog.v1alpha.SubmitEventsRequest - 45, // 82: prehog.v1alpha.TeleportReportingService.HelloTeleport:input_type -> prehog.v1alpha.HelloTeleportRequest - 49, // 83: prehog.v1alpha.TeleportReportingService.SubmitUsageReports:input_type -> prehog.v1alpha.SubmitUsageReportsRequest - 42, // 84: prehog.v1alpha.TeleportReportingService.SubmitEvent:output_type -> prehog.v1alpha.SubmitEventResponse - 44, // 85: prehog.v1alpha.TeleportReportingService.SubmitEvents:output_type -> prehog.v1alpha.SubmitEventsResponse - 46, // 86: prehog.v1alpha.TeleportReportingService.HelloTeleport:output_type -> prehog.v1alpha.HelloTeleportResponse - 50, // 87: prehog.v1alpha.TeleportReportingService.SubmitUsageReports:output_type -> prehog.v1alpha.SubmitUsageReportsResponse - 84, // [84:88] is the sub-list for method output_type - 80, // [80:84] is the sub-list for method input_type - 80, // [80:80] is the sub-list for extension type_name - 80, // [80:80] is the sub-list for extension extendee - 0, // [0:80] is the sub-list for field type_name + 41, // 77: prehog.v1alpha.TeleportReportingService.SubmitEvent:input_type -> prehog.v1alpha.SubmitEventRequest + 43, // 78: prehog.v1alpha.TeleportReportingService.SubmitEvents:input_type -> prehog.v1alpha.SubmitEventsRequest + 45, // 79: prehog.v1alpha.TeleportReportingService.HelloTeleport:input_type -> prehog.v1alpha.HelloTeleportRequest + 42, // 80: prehog.v1alpha.TeleportReportingService.SubmitEvent:output_type -> prehog.v1alpha.SubmitEventResponse + 44, // 81: prehog.v1alpha.TeleportReportingService.SubmitEvents:output_type -> prehog.v1alpha.SubmitEventsResponse + 46, // 82: prehog.v1alpha.TeleportReportingService.HelloTeleport:output_type -> prehog.v1alpha.HelloTeleportResponse + 80, // [80:83] is the sub-list for method output_type + 77, // [77:80] is the sub-list for method input_type + 77, // [77:77] is the sub-list for extension type_name + 77, // [77:77] is the sub-list for extension extendee + 0, // [0:77] is the sub-list for field type_name } func init() { file_prehog_v1alpha_teleport_proto_init() } @@ -5236,54 +4851,6 @@ func file_prehog_v1alpha_teleport_proto_init() { return nil } } - file_prehog_v1alpha_teleport_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserActivityReport); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_prehog_v1alpha_teleport_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserActivityRecord); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_prehog_v1alpha_teleport_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubmitUsageReportsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_prehog_v1alpha_teleport_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubmitUsageReportsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } file_prehog_v1alpha_teleport_proto_msgTypes[38].OneofWrappers = []interface{}{ (*SubmitEventRequest_UserLogin)(nil), @@ -5329,7 +4896,7 @@ func file_prehog_v1alpha_teleport_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_prehog_v1alpha_teleport_proto_rawDesc, NumEnums: 3, - NumMessages: 48, + NumMessages: 44, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/js/prehog/v1/teleport_grpc_pb.d.ts b/gen/proto/js/prehog/v1/teleport_grpc_pb.d.ts new file mode 100644 index 0000000000000..1cc55c98a2328 --- /dev/null +++ b/gen/proto/js/prehog/v1/teleport_grpc_pb.d.ts @@ -0,0 +1,42 @@ +// package: prehog.v1 +// file: prehog/v1/teleport.proto + +/* tslint:disable */ +/* eslint-disable */ + +import * as grpc from "grpc"; +import * as prehog_v1_teleport_pb from "../../prehog/v1/teleport_pb"; +import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/timestamp_pb"; + +interface ITeleportReportingServiceService extends grpc.ServiceDefinition { + submitUsageReports: ITeleportReportingServiceService_ISubmitUsageReports; +} + +interface ITeleportReportingServiceService_ISubmitUsageReports extends grpc.MethodDefinition { + path: "/prehog.v1.TeleportReportingService/SubmitUsageReports"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} + +export const TeleportReportingServiceService: ITeleportReportingServiceService; + +export interface ITeleportReportingServiceServer { + submitUsageReports: grpc.handleUnaryCall; +} + +export interface ITeleportReportingServiceClient { + submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; +} + +export class TeleportReportingServiceClient extends grpc.Client implements ITeleportReportingServiceClient { + constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); + public submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + public submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; + public submitUsageReports(request: prehog_v1_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; +} diff --git a/gen/proto/js/prehog/v1/teleport_grpc_pb.js b/gen/proto/js/prehog/v1/teleport_grpc_pb.js new file mode 100644 index 0000000000000..db0f294b7001c --- /dev/null +++ b/gen/proto/js/prehog/v1/teleport_grpc_pb.js @@ -0,0 +1,60 @@ +// GENERATED CODE -- DO NOT EDIT! + +// Original file comments: +// 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. +// +'use strict'; +var grpc = require('@grpc/grpc-js'); +var prehog_v1_teleport_pb = require('../../prehog/v1/teleport_pb.js'); +var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); + +function serialize_prehog_v1_SubmitUsageReportsRequest(arg) { + if (!(arg instanceof prehog_v1_teleport_pb.SubmitUsageReportsRequest)) { + throw new Error('Expected argument of type prehog.v1.SubmitUsageReportsRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_prehog_v1_SubmitUsageReportsRequest(buffer_arg) { + return prehog_v1_teleport_pb.SubmitUsageReportsRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_prehog_v1_SubmitUsageReportsResponse(arg) { + if (!(arg instanceof prehog_v1_teleport_pb.SubmitUsageReportsResponse)) { + throw new Error('Expected argument of type prehog.v1.SubmitUsageReportsResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_prehog_v1_SubmitUsageReportsResponse(buffer_arg) { + return prehog_v1_teleport_pb.SubmitUsageReportsResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +var TeleportReportingServiceService = exports.TeleportReportingServiceService = { + submitUsageReports: { + path: '/prehog.v1.TeleportReportingService/SubmitUsageReports', + requestStream: false, + responseStream: false, + requestType: prehog_v1_teleport_pb.SubmitUsageReportsRequest, + responseType: prehog_v1_teleport_pb.SubmitUsageReportsResponse, + requestSerialize: serialize_prehog_v1_SubmitUsageReportsRequest, + requestDeserialize: deserialize_prehog_v1_SubmitUsageReportsRequest, + responseSerialize: serialize_prehog_v1_SubmitUsageReportsResponse, + responseDeserialize: deserialize_prehog_v1_SubmitUsageReportsResponse, + }, +}; + +exports.TeleportReportingServiceClient = grpc.makeGenericClientConstructor(TeleportReportingServiceService); diff --git a/gen/proto/js/prehog/v1/teleport_pb.d.ts b/gen/proto/js/prehog/v1/teleport_pb.d.ts new file mode 100644 index 0000000000000..d003066d5b8c1 --- /dev/null +++ b/gen/proto/js/prehog/v1/teleport_pb.d.ts @@ -0,0 +1,165 @@ +// package: prehog.v1 +// file: prehog/v1/teleport.proto + +/* tslint:disable */ +/* eslint-disable */ + +import * as jspb from "google-protobuf"; +import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/timestamp_pb"; + +export class UserActivityReport extends jspb.Message { + getReportUuid(): Uint8Array | string; + getReportUuid_asU8(): Uint8Array; + getReportUuid_asB64(): string; + setReportUuid(value: Uint8Array | string): UserActivityReport; + + getClusterName(): Uint8Array | string; + getClusterName_asU8(): Uint8Array; + getClusterName_asB64(): string; + setClusterName(value: Uint8Array | string): UserActivityReport; + + getReporterHostid(): Uint8Array | string; + getReporterHostid_asU8(): Uint8Array; + getReporterHostid_asB64(): string; + setReporterHostid(value: Uint8Array | string): UserActivityReport; + + + hasStartTime(): boolean; + clearStartTime(): void; + getStartTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setStartTime(value?: google_protobuf_timestamp_pb.Timestamp): UserActivityReport; + + clearRecordsList(): void; + getRecordsList(): Array; + setRecordsList(value: Array): UserActivityReport; + addRecords(value?: UserActivityRecord, index?: number): UserActivityRecord; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UserActivityReport.AsObject; + static toObject(includeInstance: boolean, msg: UserActivityReport): UserActivityReport.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UserActivityReport, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UserActivityReport; + static deserializeBinaryFromReader(message: UserActivityReport, reader: jspb.BinaryReader): UserActivityReport; +} + +export namespace UserActivityReport { + export type AsObject = { + reportUuid: Uint8Array | string, + clusterName: Uint8Array | string, + reporterHostid: Uint8Array | string, + startTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + recordsList: Array, + } +} + +export class UserActivityRecord extends jspb.Message { + getUserName(): Uint8Array | string; + getUserName_asU8(): Uint8Array; + getUserName_asB64(): string; + setUserName(value: Uint8Array | string): UserActivityRecord; + + getLogins(): number; + setLogins(value: number): UserActivityRecord; + + getSshSessions(): number; + setSshSessions(value: number): UserActivityRecord; + + getAppSessions(): number; + setAppSessions(value: number): UserActivityRecord; + + getKubeSessions(): number; + setKubeSessions(value: number): UserActivityRecord; + + getDbSessions(): number; + setDbSessions(value: number): UserActivityRecord; + + getDesktopSessions(): number; + setDesktopSessions(value: number): UserActivityRecord; + + getAppTcpSessions(): number; + setAppTcpSessions(value: number): UserActivityRecord; + + getSshPortSessions(): number; + setSshPortSessions(value: number): UserActivityRecord; + + getKubeRequests(): number; + setKubeRequests(value: number): UserActivityRecord; + + getSftpEvents(): number; + setSftpEvents(value: number): UserActivityRecord; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UserActivityRecord.AsObject; + static toObject(includeInstance: boolean, msg: UserActivityRecord): UserActivityRecord.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UserActivityRecord, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UserActivityRecord; + static deserializeBinaryFromReader(message: UserActivityRecord, reader: jspb.BinaryReader): UserActivityRecord; +} + +export namespace UserActivityRecord { + export type AsObject = { + userName: Uint8Array | string, + logins: number, + sshSessions: number, + appSessions: number, + kubeSessions: number, + dbSessions: number, + desktopSessions: number, + appTcpSessions: number, + sshPortSessions: number, + kubeRequests: number, + sftpEvents: number, + } +} + +export class SubmitUsageReportsRequest extends jspb.Message { + clearUserActivityList(): void; + getUserActivityList(): Array; + setUserActivityList(value: Array): SubmitUsageReportsRequest; + addUserActivity(value?: UserActivityReport, index?: number): UserActivityReport; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SubmitUsageReportsRequest.AsObject; + static toObject(includeInstance: boolean, msg: SubmitUsageReportsRequest): SubmitUsageReportsRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: SubmitUsageReportsRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsRequest; + static deserializeBinaryFromReader(message: SubmitUsageReportsRequest, reader: jspb.BinaryReader): SubmitUsageReportsRequest; +} + +export namespace SubmitUsageReportsRequest { + export type AsObject = { + userActivityList: Array, + } +} + +export class SubmitUsageReportsResponse extends jspb.Message { + getBatchUuid(): Uint8Array | string; + getBatchUuid_asU8(): Uint8Array; + getBatchUuid_asB64(): string; + setBatchUuid(value: Uint8Array | string): SubmitUsageReportsResponse; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SubmitUsageReportsResponse.AsObject; + static toObject(includeInstance: boolean, msg: SubmitUsageReportsResponse): SubmitUsageReportsResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: SubmitUsageReportsResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsResponse; + static deserializeBinaryFromReader(message: SubmitUsageReportsResponse, reader: jspb.BinaryReader): SubmitUsageReportsResponse; +} + +export namespace SubmitUsageReportsResponse { + export type AsObject = { + batchUuid: Uint8Array | string, + } +} diff --git a/gen/proto/js/prehog/v1/teleport_pb.js b/gen/proto/js/prehog/v1/teleport_pb.js new file mode 100644 index 0000000000000..b2d63f9e17d56 --- /dev/null +++ b/gen/proto/js/prehog/v1/teleport_pb.js @@ -0,0 +1,1249 @@ +// source: prehog/v1/teleport.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { return this || window || global || self || Function('return this')(); }).call(null); + +var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); +goog.object.extend(proto, google_protobuf_timestamp_pb); +goog.exportSymbol('proto.prehog.v1.SubmitUsageReportsRequest', null, global); +goog.exportSymbol('proto.prehog.v1.SubmitUsageReportsResponse', null, global); +goog.exportSymbol('proto.prehog.v1.UserActivityRecord', null, global); +goog.exportSymbol('proto.prehog.v1.UserActivityReport', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1.UserActivityReport = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1.UserActivityReport.repeatedFields_, null); +}; +goog.inherits(proto.prehog.v1.UserActivityReport, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1.UserActivityReport.displayName = 'proto.prehog.v1.UserActivityReport'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1.UserActivityRecord = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.prehog.v1.UserActivityRecord, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1.UserActivityRecord.displayName = 'proto.prehog.v1.UserActivityRecord'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1.SubmitUsageReportsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1.SubmitUsageReportsRequest.repeatedFields_, null); +}; +goog.inherits(proto.prehog.v1.SubmitUsageReportsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1.SubmitUsageReportsRequest.displayName = 'proto.prehog.v1.SubmitUsageReportsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.prehog.v1.SubmitUsageReportsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.prehog.v1.SubmitUsageReportsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.prehog.v1.SubmitUsageReportsResponse.displayName = 'proto.prehog.v1.SubmitUsageReportsResponse'; +} + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.prehog.v1.UserActivityReport.repeatedFields_ = [5]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1.UserActivityReport.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1.UserActivityReport.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1.UserActivityReport} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.UserActivityReport.toObject = function(includeInstance, msg) { + var f, obj = { + reportUuid: msg.getReportUuid_asB64(), + clusterName: msg.getClusterName_asB64(), + reporterHostid: msg.getReporterHostid_asB64(), + startTime: (f = msg.getStartTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), + recordsList: jspb.Message.toObjectList(msg.getRecordsList(), + proto.prehog.v1.UserActivityRecord.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1.UserActivityReport} + */ +proto.prehog.v1.UserActivityReport.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1.UserActivityReport; + return proto.prehog.v1.UserActivityReport.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1.UserActivityReport} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1.UserActivityReport} + */ +proto.prehog.v1.UserActivityReport.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setReportUuid(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setClusterName(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setReporterHostid(value); + break; + case 4: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setStartTime(value); + break; + case 5: + var value = new proto.prehog.v1.UserActivityRecord; + reader.readMessage(value,proto.prehog.v1.UserActivityRecord.deserializeBinaryFromReader); + msg.addRecords(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityReport.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1.UserActivityReport.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1.UserActivityReport} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.UserActivityReport.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getReportUuid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getClusterName_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getReporterHostid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getStartTime(); + if (f != null) { + writer.writeMessage( + 4, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } + f = message.getRecordsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 5, + f, + proto.prehog.v1.UserActivityRecord.serializeBinaryToWriter + ); + } +}; + + +/** + * optional bytes report_uuid = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1.UserActivityReport.prototype.getReportUuid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes report_uuid = 1; + * This is a type-conversion wrapper around `getReportUuid()` + * @return {string} + */ +proto.prehog.v1.UserActivityReport.prototype.getReportUuid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getReportUuid())); +}; + + +/** + * optional bytes report_uuid = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getReportUuid()` + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityReport.prototype.getReportUuid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getReportUuid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1.UserActivityReport} returns this + */ +proto.prehog.v1.UserActivityReport.prototype.setReportUuid = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes cluster_name = 2; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1.UserActivityReport.prototype.getClusterName = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes cluster_name = 2; + * This is a type-conversion wrapper around `getClusterName()` + * @return {string} + */ +proto.prehog.v1.UserActivityReport.prototype.getClusterName_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getClusterName())); +}; + + +/** + * optional bytes cluster_name = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getClusterName()` + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityReport.prototype.getClusterName_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getClusterName())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1.UserActivityReport} returns this + */ +proto.prehog.v1.UserActivityReport.prototype.setClusterName = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes reporter_hostid = 3; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1.UserActivityReport.prototype.getReporterHostid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes reporter_hostid = 3; + * This is a type-conversion wrapper around `getReporterHostid()` + * @return {string} + */ +proto.prehog.v1.UserActivityReport.prototype.getReporterHostid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getReporterHostid())); +}; + + +/** + * optional bytes reporter_hostid = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getReporterHostid()` + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityReport.prototype.getReporterHostid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getReporterHostid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1.UserActivityReport} returns this + */ +proto.prehog.v1.UserActivityReport.prototype.setReporterHostid = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional google.protobuf.Timestamp start_time = 4; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.prehog.v1.UserActivityReport.prototype.getStartTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 4)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.prehog.v1.UserActivityReport} returns this +*/ +proto.prehog.v1.UserActivityReport.prototype.setStartTime = function(value) { + return jspb.Message.setWrapperField(this, 4, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.prehog.v1.UserActivityReport} returns this + */ +proto.prehog.v1.UserActivityReport.prototype.clearStartTime = function() { + return this.setStartTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.prehog.v1.UserActivityReport.prototype.hasStartTime = function() { + return jspb.Message.getField(this, 4) != null; +}; + + +/** + * repeated UserActivityRecord records = 5; + * @return {!Array} + */ +proto.prehog.v1.UserActivityReport.prototype.getRecordsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1.UserActivityRecord, 5)); +}; + + +/** + * @param {!Array} value + * @return {!proto.prehog.v1.UserActivityReport} returns this +*/ +proto.prehog.v1.UserActivityReport.prototype.setRecordsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 5, value); +}; + + +/** + * @param {!proto.prehog.v1.UserActivityRecord=} opt_value + * @param {number=} opt_index + * @return {!proto.prehog.v1.UserActivityRecord} + */ +proto.prehog.v1.UserActivityReport.prototype.addRecords = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.prehog.v1.UserActivityRecord, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.prehog.v1.UserActivityReport} returns this + */ +proto.prehog.v1.UserActivityReport.prototype.clearRecordsList = function() { + return this.setRecordsList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1.UserActivityRecord.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1.UserActivityRecord.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1.UserActivityRecord} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.UserActivityRecord.toObject = function(includeInstance, msg) { + var f, obj = { + userName: msg.getUserName_asB64(), + logins: jspb.Message.getFieldWithDefault(msg, 2, 0), + sshSessions: jspb.Message.getFieldWithDefault(msg, 3, 0), + appSessions: jspb.Message.getFieldWithDefault(msg, 4, 0), + kubeSessions: jspb.Message.getFieldWithDefault(msg, 5, 0), + dbSessions: jspb.Message.getFieldWithDefault(msg, 6, 0), + desktopSessions: jspb.Message.getFieldWithDefault(msg, 7, 0), + appTcpSessions: jspb.Message.getFieldWithDefault(msg, 8, 0), + sshPortSessions: jspb.Message.getFieldWithDefault(msg, 9, 0), + kubeRequests: jspb.Message.getFieldWithDefault(msg, 10, 0), + sftpEvents: jspb.Message.getFieldWithDefault(msg, 11, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1.UserActivityRecord} + */ +proto.prehog.v1.UserActivityRecord.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1.UserActivityRecord; + return proto.prehog.v1.UserActivityRecord.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1.UserActivityRecord} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1.UserActivityRecord} + */ +proto.prehog.v1.UserActivityRecord.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setUserName(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setLogins(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSshSessions(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint64()); + msg.setAppSessions(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setKubeSessions(value); + break; + case 6: + var value = /** @type {number} */ (reader.readUint64()); + msg.setDbSessions(value); + break; + case 7: + var value = /** @type {number} */ (reader.readUint64()); + msg.setDesktopSessions(value); + break; + case 8: + var value = /** @type {number} */ (reader.readUint64()); + msg.setAppTcpSessions(value); + break; + case 9: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSshPortSessions(value); + break; + case 10: + var value = /** @type {number} */ (reader.readUint64()); + msg.setKubeRequests(value); + break; + case 11: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSftpEvents(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityRecord.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1.UserActivityRecord.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1.UserActivityRecord} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.UserActivityRecord.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserName_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getLogins(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getSshSessions(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getAppSessions(); + if (f !== 0) { + writer.writeUint64( + 4, + f + ); + } + f = message.getKubeSessions(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } + f = message.getDbSessions(); + if (f !== 0) { + writer.writeUint64( + 6, + f + ); + } + f = message.getDesktopSessions(); + if (f !== 0) { + writer.writeUint64( + 7, + f + ); + } + f = message.getAppTcpSessions(); + if (f !== 0) { + writer.writeUint64( + 8, + f + ); + } + f = message.getSshPortSessions(); + if (f !== 0) { + writer.writeUint64( + 9, + f + ); + } + f = message.getKubeRequests(); + if (f !== 0) { + writer.writeUint64( + 10, + f + ); + } + f = message.getSftpEvents(); + if (f !== 0) { + writer.writeUint64( + 11, + f + ); + } +}; + + +/** + * optional bytes user_name = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1.UserActivityRecord.prototype.getUserName = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes user_name = 1; + * This is a type-conversion wrapper around `getUserName()` + * @return {string} + */ +proto.prehog.v1.UserActivityRecord.prototype.getUserName_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getUserName())); +}; + + +/** + * optional bytes user_name = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getUserName()` + * @return {!Uint8Array} + */ +proto.prehog.v1.UserActivityRecord.prototype.getUserName_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getUserName())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setUserName = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional uint64 logins = 2; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getLogins = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setLogins = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 ssh_sessions = 3; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getSshSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setSshSessions = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint64 app_sessions = 4; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getAppSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setAppSessions = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional uint64 kube_sessions = 5; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getKubeSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setKubeSessions = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional uint64 db_sessions = 6; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getDbSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setDbSessions = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional uint64 desktop_sessions = 7; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getDesktopSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setDesktopSessions = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + +/** + * optional uint64 app_tcp_sessions = 8; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getAppTcpSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setAppTcpSessions = function(value) { + return jspb.Message.setProto3IntField(this, 8, value); +}; + + +/** + * optional uint64 ssh_port_sessions = 9; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getSshPortSessions = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setSshPortSessions = function(value) { + return jspb.Message.setProto3IntField(this, 9, value); +}; + + +/** + * optional uint64 kube_requests = 10; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getKubeRequests = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setKubeRequests = function(value) { + return jspb.Message.setProto3IntField(this, 10, value); +}; + + +/** + * optional uint64 sftp_events = 11; + * @return {number} + */ +proto.prehog.v1.UserActivityRecord.prototype.getSftpEvents = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.prehog.v1.UserActivityRecord} returns this + */ +proto.prehog.v1.UserActivityRecord.prototype.setSftpEvents = function(value) { + return jspb.Message.setProto3IntField(this, 11, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.prehog.v1.SubmitUsageReportsRequest.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1.SubmitUsageReportsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1.SubmitUsageReportsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.SubmitUsageReportsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + userActivityList: jspb.Message.toObjectList(msg.getUserActivityList(), + proto.prehog.v1.UserActivityReport.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1.SubmitUsageReportsRequest} + */ +proto.prehog.v1.SubmitUsageReportsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1.SubmitUsageReportsRequest; + return proto.prehog.v1.SubmitUsageReportsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1.SubmitUsageReportsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1.SubmitUsageReportsRequest} + */ +proto.prehog.v1.SubmitUsageReportsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.prehog.v1.UserActivityReport; + reader.readMessage(value,proto.prehog.v1.UserActivityReport.deserializeBinaryFromReader); + msg.addUserActivity(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1.SubmitUsageReportsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1.SubmitUsageReportsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.SubmitUsageReportsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserActivityList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.prehog.v1.UserActivityReport.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated UserActivityReport user_activity = 1; + * @return {!Array} + */ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.getUserActivityList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1.UserActivityReport, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.prehog.v1.SubmitUsageReportsRequest} returns this +*/ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.setUserActivityList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.prehog.v1.UserActivityReport=} opt_value + * @param {number=} opt_index + * @return {!proto.prehog.v1.UserActivityReport} + */ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.addUserActivity = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.prehog.v1.UserActivityReport, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.prehog.v1.SubmitUsageReportsRequest} returns this + */ +proto.prehog.v1.SubmitUsageReportsRequest.prototype.clearUserActivityList = function() { + return this.setUserActivityList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.prehog.v1.SubmitUsageReportsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.prehog.v1.SubmitUsageReportsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.SubmitUsageReportsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + batchUuid: msg.getBatchUuid_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.prehog.v1.SubmitUsageReportsResponse} + */ +proto.prehog.v1.SubmitUsageReportsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.prehog.v1.SubmitUsageReportsResponse; + return proto.prehog.v1.SubmitUsageReportsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.prehog.v1.SubmitUsageReportsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.prehog.v1.SubmitUsageReportsResponse} + */ +proto.prehog.v1.SubmitUsageReportsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setBatchUuid(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.prehog.v1.SubmitUsageReportsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.prehog.v1.SubmitUsageReportsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.prehog.v1.SubmitUsageReportsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getBatchUuid_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } +}; + + +/** + * optional bytes batch_uuid = 1; + * @return {!(string|Uint8Array)} + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.getBatchUuid = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes batch_uuid = 1; + * This is a type-conversion wrapper around `getBatchUuid()` + * @return {string} + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.getBatchUuid_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getBatchUuid())); +}; + + +/** + * optional bytes batch_uuid = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getBatchUuid()` + * @return {!Uint8Array} + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.getBatchUuid_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getBatchUuid())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.prehog.v1.SubmitUsageReportsResponse} returns this + */ +proto.prehog.v1.SubmitUsageReportsResponse.prototype.setBatchUuid = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +goog.object.extend(exports, proto.prehog.v1); diff --git a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts index c806029b03eff..b058fb8710cef 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.d.ts @@ -13,7 +13,6 @@ interface ITeleportReportingServiceService extends grpc.ServiceDefinition { @@ -43,15 +42,6 @@ interface ITeleportReportingServiceService_IHelloTeleport extends grpc.MethodDef responseSerialize: grpc.serialize; responseDeserialize: grpc.deserialize; } -interface ITeleportReportingServiceService_ISubmitUsageReports extends grpc.MethodDefinition { - path: "/prehog.v1alpha.TeleportReportingService/SubmitUsageReports"; - requestStream: false; - responseStream: false; - requestSerialize: grpc.serialize; - requestDeserialize: grpc.deserialize; - responseSerialize: grpc.serialize; - responseDeserialize: grpc.deserialize; -} export const TeleportReportingServiceService: ITeleportReportingServiceService; @@ -59,7 +49,6 @@ export interface ITeleportReportingServiceServer { submitEvent: grpc.handleUnaryCall; submitEvents: grpc.handleUnaryCall; helloTeleport: grpc.handleUnaryCall; - submitUsageReports: grpc.handleUnaryCall; } export interface ITeleportReportingServiceClient { @@ -72,9 +61,6 @@ export interface ITeleportReportingServiceClient { helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; - submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; - submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; - submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; } export class TeleportReportingServiceClient extends grpc.Client implements ITeleportReportingServiceClient { @@ -88,7 +74,4 @@ export class TeleportReportingServiceClient extends grpc.Client implements ITele public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; public helloTeleport(request: prehog_v1alpha_teleport_pb.HelloTeleportRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.HelloTeleportResponse) => void): grpc.ClientUnaryCall; - public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; - public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; - public submitUsageReports(request: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse) => void): grpc.ClientUnaryCall; } diff --git a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js index ae8470c38a463..b075793a9573b 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js +++ b/gen/proto/js/prehog/v1alpha/teleport_grpc_pb.js @@ -87,28 +87,6 @@ function deserialize_prehog_v1alpha_SubmitEventsResponse(buffer_arg) { return prehog_v1alpha_teleport_pb.SubmitEventsResponse.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_prehog_v1alpha_SubmitUsageReportsRequest(arg) { - if (!(arg instanceof prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest)) { - throw new Error('Expected argument of type prehog.v1alpha.SubmitUsageReportsRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_prehog_v1alpha_SubmitUsageReportsRequest(buffer_arg) { - return prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_prehog_v1alpha_SubmitUsageReportsResponse(arg) { - if (!(arg instanceof prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse)) { - throw new Error('Expected argument of type prehog.v1alpha.SubmitUsageReportsResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_prehog_v1alpha_SubmitUsageReportsResponse(buffer_arg) { - return prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - var TeleportReportingServiceService = exports.TeleportReportingServiceService = { submitEvent: { @@ -144,17 +122,6 @@ var TeleportReportingServiceService = exports.TeleportReportingServiceService = responseSerialize: serialize_prehog_v1alpha_HelloTeleportResponse, responseDeserialize: deserialize_prehog_v1alpha_HelloTeleportResponse, }, - submitUsageReports: { - path: '/prehog.v1alpha.TeleportReportingService/SubmitUsageReports', - requestStream: false, - responseStream: false, - requestType: prehog_v1alpha_teleport_pb.SubmitUsageReportsRequest, - responseType: prehog_v1alpha_teleport_pb.SubmitUsageReportsResponse, - requestSerialize: serialize_prehog_v1alpha_SubmitUsageReportsRequest, - requestDeserialize: deserialize_prehog_v1alpha_SubmitUsageReportsRequest, - responseSerialize: serialize_prehog_v1alpha_SubmitUsageReportsResponse, - responseDeserialize: deserialize_prehog_v1alpha_SubmitUsageReportsResponse, - }, }; exports.TeleportReportingServiceClient = grpc.makeGenericClientConstructor(TeleportReportingServiceService); diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts index ffa9ac2682855..870e01ea2411f 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.d.ts @@ -1583,163 +1583,6 @@ export namespace HelloTeleportResponse { } } -export class UserActivityReport extends jspb.Message { - getReportUuid(): Uint8Array | string; - getReportUuid_asU8(): Uint8Array; - getReportUuid_asB64(): string; - setReportUuid(value: Uint8Array | string): UserActivityReport; - - getClusterName(): Uint8Array | string; - getClusterName_asU8(): Uint8Array; - getClusterName_asB64(): string; - setClusterName(value: Uint8Array | string): UserActivityReport; - - getReporterHostid(): Uint8Array | string; - getReporterHostid_asU8(): Uint8Array; - getReporterHostid_asB64(): string; - setReporterHostid(value: Uint8Array | string): UserActivityReport; - - - hasStartTime(): boolean; - clearStartTime(): void; - getStartTime(): google_protobuf_timestamp_pb.Timestamp | undefined; - setStartTime(value?: google_protobuf_timestamp_pb.Timestamp): UserActivityReport; - - clearRecordsList(): void; - getRecordsList(): Array; - setRecordsList(value: Array): UserActivityReport; - addRecords(value?: UserActivityRecord, index?: number): UserActivityRecord; - - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): UserActivityReport.AsObject; - static toObject(includeInstance: boolean, msg: UserActivityReport): UserActivityReport.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: UserActivityReport, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): UserActivityReport; - static deserializeBinaryFromReader(message: UserActivityReport, reader: jspb.BinaryReader): UserActivityReport; -} - -export namespace UserActivityReport { - export type AsObject = { - reportUuid: Uint8Array | string, - clusterName: Uint8Array | string, - reporterHostid: Uint8Array | string, - startTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, - recordsList: Array, - } -} - -export class UserActivityRecord extends jspb.Message { - getUserName(): Uint8Array | string; - getUserName_asU8(): Uint8Array; - getUserName_asB64(): string; - setUserName(value: Uint8Array | string): UserActivityRecord; - - getLogins(): number; - setLogins(value: number): UserActivityRecord; - - getSshSessions(): number; - setSshSessions(value: number): UserActivityRecord; - - getAppSessions(): number; - setAppSessions(value: number): UserActivityRecord; - - getKubeSessions(): number; - setKubeSessions(value: number): UserActivityRecord; - - getDbSessions(): number; - setDbSessions(value: number): UserActivityRecord; - - getDesktopSessions(): number; - setDesktopSessions(value: number): UserActivityRecord; - - getAppTcpSessions(): number; - setAppTcpSessions(value: number): UserActivityRecord; - - getSshPortSessions(): number; - setSshPortSessions(value: number): UserActivityRecord; - - getKubeRequests(): number; - setKubeRequests(value: number): UserActivityRecord; - - getSftpEvents(): number; - setSftpEvents(value: number): UserActivityRecord; - - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): UserActivityRecord.AsObject; - static toObject(includeInstance: boolean, msg: UserActivityRecord): UserActivityRecord.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: UserActivityRecord, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): UserActivityRecord; - static deserializeBinaryFromReader(message: UserActivityRecord, reader: jspb.BinaryReader): UserActivityRecord; -} - -export namespace UserActivityRecord { - export type AsObject = { - userName: Uint8Array | string, - logins: number, - sshSessions: number, - appSessions: number, - kubeSessions: number, - dbSessions: number, - desktopSessions: number, - appTcpSessions: number, - sshPortSessions: number, - kubeRequests: number, - sftpEvents: number, - } -} - -export class SubmitUsageReportsRequest extends jspb.Message { - clearUserActivityList(): void; - getUserActivityList(): Array; - setUserActivityList(value: Array): SubmitUsageReportsRequest; - addUserActivity(value?: UserActivityReport, index?: number): UserActivityReport; - - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): SubmitUsageReportsRequest.AsObject; - static toObject(includeInstance: boolean, msg: SubmitUsageReportsRequest): SubmitUsageReportsRequest.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: SubmitUsageReportsRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsRequest; - static deserializeBinaryFromReader(message: SubmitUsageReportsRequest, reader: jspb.BinaryReader): SubmitUsageReportsRequest; -} - -export namespace SubmitUsageReportsRequest { - export type AsObject = { - userActivityList: Array, - } -} - -export class SubmitUsageReportsResponse extends jspb.Message { - getBatchUuid(): Uint8Array | string; - getBatchUuid_asU8(): Uint8Array; - getBatchUuid_asB64(): string; - setBatchUuid(value: Uint8Array | string): SubmitUsageReportsResponse; - - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): SubmitUsageReportsResponse.AsObject; - static toObject(includeInstance: boolean, msg: SubmitUsageReportsResponse): SubmitUsageReportsResponse.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: SubmitUsageReportsResponse, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): SubmitUsageReportsResponse; - static deserializeBinaryFromReader(message: SubmitUsageReportsResponse, reader: jspb.BinaryReader): SubmitUsageReportsResponse; -} - -export namespace SubmitUsageReportsResponse { - export type AsObject = { - batchUuid: Uint8Array | string, - } -} - export enum ResourceKind { RESOURCE_KIND_UNSPECIFIED = 0, RESOURCE_KIND_NODE = 1, diff --git a/gen/proto/js/prehog/v1alpha/teleport_pb.js b/gen/proto/js/prehog/v1alpha/teleport_pb.js index cf2377cc952be..2c6c36ead56fa 100644 --- a/gen/proto/js/prehog/v1alpha/teleport_pb.js +++ b/gen/proto/js/prehog/v1alpha/teleport_pb.js @@ -40,8 +40,6 @@ goog.exportSymbol('proto.prehog.v1alpha.SubmitEventRequest.EventCase', null, glo goog.exportSymbol('proto.prehog.v1alpha.SubmitEventResponse', null, global); goog.exportSymbol('proto.prehog.v1alpha.SubmitEventsRequest', null, global); goog.exportSymbol('proto.prehog.v1alpha.SubmitEventsResponse', null, global); -goog.exportSymbol('proto.prehog.v1alpha.SubmitUsageReportsRequest', null, global); -goog.exportSymbol('proto.prehog.v1alpha.SubmitUsageReportsResponse', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIBannerClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UICreateNewRoleCancelClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UICreateNewRoleClickEvent', null, global); @@ -67,8 +65,6 @@ goog.exportSymbol('proto.prehog.v1alpha.UIOnboardSetCredentialSubmitEvent', null goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesContinueClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesCopyClickEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UIRecoveryCodesPrintClickEvent', null, global); -goog.exportSymbol('proto.prehog.v1alpha.UserActivityRecord', null, global); -goog.exportSymbol('proto.prehog.v1alpha.UserActivityReport', null, global); goog.exportSymbol('proto.prehog.v1alpha.UserCertificateIssuedEvent', null, global); goog.exportSymbol('proto.prehog.v1alpha.UserLoginEvent', null, global); /** @@ -995,90 +991,6 @@ if (goog.DEBUG && !COMPILED) { */ proto.prehog.v1alpha.HelloTeleportResponse.displayName = 'proto.prehog.v1alpha.HelloTeleportResponse'; } -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.prehog.v1alpha.UserActivityReport = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1alpha.UserActivityReport.repeatedFields_, null); -}; -goog.inherits(proto.prehog.v1alpha.UserActivityReport, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.prehog.v1alpha.UserActivityReport.displayName = 'proto.prehog.v1alpha.UserActivityReport'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.prehog.v1alpha.UserActivityRecord = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.prehog.v1alpha.UserActivityRecord, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.prehog.v1alpha.UserActivityRecord.displayName = 'proto.prehog.v1alpha.UserActivityRecord'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.prehog.v1alpha.SubmitUsageReportsRequest.repeatedFields_, null); -}; -goog.inherits(proto.prehog.v1alpha.SubmitUsageReportsRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.prehog.v1alpha.SubmitUsageReportsRequest.displayName = 'proto.prehog.v1alpha.SubmitUsageReportsRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.prehog.v1alpha.SubmitUsageReportsResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.prehog.v1alpha.SubmitUsageReportsResponse.displayName = 'proto.prehog.v1alpha.SubmitUsageReportsResponse'; -} @@ -11075,1147 +10987,6 @@ proto.prehog.v1alpha.HelloTeleportResponse.serializeBinaryToWriter = function(me }; - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.prehog.v1alpha.UserActivityReport.repeatedFields_ = [5]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.toObject = function(opt_includeInstance) { - return proto.prehog.v1alpha.UserActivityReport.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.prehog.v1alpha.UserActivityReport} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.UserActivityReport.toObject = function(includeInstance, msg) { - var f, obj = { - reportUuid: msg.getReportUuid_asB64(), - clusterName: msg.getClusterName_asB64(), - reporterHostid: msg.getReporterHostid_asB64(), - startTime: (f = msg.getStartTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), - recordsList: jspb.Message.toObjectList(msg.getRecordsList(), - proto.prehog.v1alpha.UserActivityRecord.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.prehog.v1alpha.UserActivityReport} - */ -proto.prehog.v1alpha.UserActivityReport.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.prehog.v1alpha.UserActivityReport; - return proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.prehog.v1alpha.UserActivityReport} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.prehog.v1alpha.UserActivityReport} - */ -proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setReportUuid(value); - break; - case 2: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setClusterName(value); - break; - case 3: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setReporterHostid(value); - break; - case 4: - var value = new google_protobuf_timestamp_pb.Timestamp; - reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); - msg.setStartTime(value); - break; - case 5: - var value = new proto.prehog.v1alpha.UserActivityRecord; - reader.readMessage(value,proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader); - msg.addRecords(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.prehog.v1alpha.UserActivityReport} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getReportUuid_asU8(); - if (f.length > 0) { - writer.writeBytes( - 1, - f - ); - } - f = message.getClusterName_asU8(); - if (f.length > 0) { - writer.writeBytes( - 2, - f - ); - } - f = message.getReporterHostid_asU8(); - if (f.length > 0) { - writer.writeBytes( - 3, - f - ); - } - f = message.getStartTime(); - if (f != null) { - writer.writeMessage( - 4, - f, - google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter - ); - } - f = message.getRecordsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 5, - f, - proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter - ); - } -}; - - -/** - * optional bytes report_uuid = 1; - * @return {!(string|Uint8Array)} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * optional bytes report_uuid = 1; - * This is a type-conversion wrapper around `getReportUuid()` - * @return {string} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getReportUuid())); -}; - - -/** - * optional bytes report_uuid = 1; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getReportUuid()` - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReportUuid_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getReportUuid())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this - */ -proto.prehog.v1alpha.UserActivityReport.prototype.setReportUuid = function(value) { - return jspb.Message.setProto3BytesField(this, 1, value); -}; - - -/** - * optional bytes cluster_name = 2; - * @return {!(string|Uint8Array)} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * optional bytes cluster_name = 2; - * This is a type-conversion wrapper around `getClusterName()` - * @return {string} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getClusterName())); -}; - - -/** - * optional bytes cluster_name = 2; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getClusterName()` - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getClusterName_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getClusterName())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this - */ -proto.prehog.v1alpha.UserActivityReport.prototype.setClusterName = function(value) { - return jspb.Message.setProto3BytesField(this, 2, value); -}; - - -/** - * optional bytes reporter_hostid = 3; - * @return {!(string|Uint8Array)} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); -}; - - -/** - * optional bytes reporter_hostid = 3; - * This is a type-conversion wrapper around `getReporterHostid()` - * @return {string} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getReporterHostid())); -}; - - -/** - * optional bytes reporter_hostid = 3; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getReporterHostid()` - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getReporterHostid_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getReporterHostid())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this - */ -proto.prehog.v1alpha.UserActivityReport.prototype.setReporterHostid = function(value) { - return jspb.Message.setProto3BytesField(this, 3, value); -}; - - -/** - * optional google.protobuf.Timestamp start_time = 4; - * @return {?proto.google.protobuf.Timestamp} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getStartTime = function() { - return /** @type{?proto.google.protobuf.Timestamp} */ ( - jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 4)); -}; - - -/** - * @param {?proto.google.protobuf.Timestamp|undefined} value - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this -*/ -proto.prehog.v1alpha.UserActivityReport.prototype.setStartTime = function(value) { - return jspb.Message.setWrapperField(this, 4, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this - */ -proto.prehog.v1alpha.UserActivityReport.prototype.clearStartTime = function() { - return this.setStartTime(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.hasStartTime = function() { - return jspb.Message.getField(this, 4) != null; -}; - - -/** - * repeated UserActivityRecord records = 5; - * @return {!Array} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.getRecordsList = function() { - return /** @type{!Array} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1alpha.UserActivityRecord, 5)); -}; - - -/** - * @param {!Array} value - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this -*/ -proto.prehog.v1alpha.UserActivityReport.prototype.setRecordsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 5, value); -}; - - -/** - * @param {!proto.prehog.v1alpha.UserActivityRecord=} opt_value - * @param {number=} opt_index - * @return {!proto.prehog.v1alpha.UserActivityRecord} - */ -proto.prehog.v1alpha.UserActivityReport.prototype.addRecords = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.prehog.v1alpha.UserActivityRecord, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.prehog.v1alpha.UserActivityReport} returns this - */ -proto.prehog.v1alpha.UserActivityReport.prototype.clearRecordsList = function() { - return this.setRecordsList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.toObject = function(opt_includeInstance) { - return proto.prehog.v1alpha.UserActivityRecord.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.prehog.v1alpha.UserActivityRecord} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.UserActivityRecord.toObject = function(includeInstance, msg) { - var f, obj = { - userName: msg.getUserName_asB64(), - logins: jspb.Message.getFieldWithDefault(msg, 2, 0), - sshSessions: jspb.Message.getFieldWithDefault(msg, 3, 0), - appSessions: jspb.Message.getFieldWithDefault(msg, 4, 0), - kubeSessions: jspb.Message.getFieldWithDefault(msg, 5, 0), - dbSessions: jspb.Message.getFieldWithDefault(msg, 6, 0), - desktopSessions: jspb.Message.getFieldWithDefault(msg, 7, 0), - appTcpSessions: jspb.Message.getFieldWithDefault(msg, 8, 0), - sshPortSessions: jspb.Message.getFieldWithDefault(msg, 9, 0), - kubeRequests: jspb.Message.getFieldWithDefault(msg, 10, 0), - sftpEvents: jspb.Message.getFieldWithDefault(msg, 11, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.prehog.v1alpha.UserActivityRecord} - */ -proto.prehog.v1alpha.UserActivityRecord.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.prehog.v1alpha.UserActivityRecord; - return proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.prehog.v1alpha.UserActivityRecord} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.prehog.v1alpha.UserActivityRecord} - */ -proto.prehog.v1alpha.UserActivityRecord.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setUserName(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setLogins(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSshSessions(value); - break; - case 4: - var value = /** @type {number} */ (reader.readUint64()); - msg.setAppSessions(value); - break; - case 5: - var value = /** @type {number} */ (reader.readUint64()); - msg.setKubeSessions(value); - break; - case 6: - var value = /** @type {number} */ (reader.readUint64()); - msg.setDbSessions(value); - break; - case 7: - var value = /** @type {number} */ (reader.readUint64()); - msg.setDesktopSessions(value); - break; - case 8: - var value = /** @type {number} */ (reader.readUint64()); - msg.setAppTcpSessions(value); - break; - case 9: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSshPortSessions(value); - break; - case 10: - var value = /** @type {number} */ (reader.readUint64()); - msg.setKubeRequests(value); - break; - case 11: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSftpEvents(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.prehog.v1alpha.UserActivityRecord} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.UserActivityRecord.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getUserName_asU8(); - if (f.length > 0) { - writer.writeBytes( - 1, - f - ); - } - f = message.getLogins(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getSshSessions(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getAppSessions(); - if (f !== 0) { - writer.writeUint64( - 4, - f - ); - } - f = message.getKubeSessions(); - if (f !== 0) { - writer.writeUint64( - 5, - f - ); - } - f = message.getDbSessions(); - if (f !== 0) { - writer.writeUint64( - 6, - f - ); - } - f = message.getDesktopSessions(); - if (f !== 0) { - writer.writeUint64( - 7, - f - ); - } - f = message.getAppTcpSessions(); - if (f !== 0) { - writer.writeUint64( - 8, - f - ); - } - f = message.getSshPortSessions(); - if (f !== 0) { - writer.writeUint64( - 9, - f - ); - } - f = message.getKubeRequests(); - if (f !== 0) { - writer.writeUint64( - 10, - f - ); - } - f = message.getSftpEvents(); - if (f !== 0) { - writer.writeUint64( - 11, - f - ); - } -}; - - -/** - * optional bytes user_name = 1; - * @return {!(string|Uint8Array)} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * optional bytes user_name = 1; - * This is a type-conversion wrapper around `getUserName()` - * @return {string} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getUserName())); -}; - - -/** - * optional bytes user_name = 1; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getUserName()` - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getUserName_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getUserName())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setUserName = function(value) { - return jspb.Message.setProto3BytesField(this, 1, value); -}; - - -/** - * optional uint64 logins = 2; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getLogins = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setLogins = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 ssh_sessions = 3; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getSshSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setSshSessions = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional uint64 app_sessions = 4; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getAppSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setAppSessions = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional uint64 kube_sessions = 5; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getKubeSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setKubeSessions = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional uint64 db_sessions = 6; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getDbSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setDbSessions = function(value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - - -/** - * optional uint64 desktop_sessions = 7; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getDesktopSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setDesktopSessions = function(value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - - -/** - * optional uint64 app_tcp_sessions = 8; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getAppTcpSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setAppTcpSessions = function(value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - - -/** - * optional uint64 ssh_port_sessions = 9; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getSshPortSessions = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setSshPortSessions = function(value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - - -/** - * optional uint64 kube_requests = 10; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getKubeRequests = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setKubeRequests = function(value) { - return jspb.Message.setProto3IntField(this, 10, value); -}; - - -/** - * optional uint64 sftp_events = 11; - * @return {number} - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.getSftpEvents = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.prehog.v1alpha.UserActivityRecord} returns this - */ -proto.prehog.v1alpha.UserActivityRecord.prototype.setSftpEvents = function(value) { - return jspb.Message.setProto3IntField(this, 11, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.toObject = function(opt_includeInstance) { - return proto.prehog.v1alpha.SubmitUsageReportsRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.toObject = function(includeInstance, msg) { - var f, obj = { - userActivityList: jspb.Message.toObjectList(msg.getUserActivityList(), - proto.prehog.v1alpha.UserActivityReport.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.prehog.v1alpha.SubmitUsageReportsRequest; - return proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.prehog.v1alpha.UserActivityReport; - reader.readMessage(value,proto.prehog.v1alpha.UserActivityReport.deserializeBinaryFromReader); - msg.addUserActivity(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.prehog.v1alpha.SubmitUsageReportsRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.prehog.v1alpha.SubmitUsageReportsRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getUserActivityList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.prehog.v1alpha.UserActivityReport.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated UserActivityReport user_activity = 1; - * @return {!Array} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.getUserActivityList = function() { - return /** @type{!Array} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.prehog.v1alpha.UserActivityReport, 1)); -}; - - -/** - * @param {!Array} value - * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} returns this -*/ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.setUserActivityList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.prehog.v1alpha.UserActivityReport=} opt_value - * @param {number=} opt_index - * @return {!proto.prehog.v1alpha.UserActivityReport} - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.addUserActivity = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.prehog.v1alpha.UserActivityReport, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.prehog.v1alpha.SubmitUsageReportsRequest} returns this - */ -proto.prehog.v1alpha.SubmitUsageReportsRequest.prototype.clearUserActivityList = function() { - return this.setUserActivityList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.toObject = function(opt_includeInstance) { - return proto.prehog.v1alpha.SubmitUsageReportsResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.toObject = function(includeInstance, msg) { - var f, obj = { - batchUuid: msg.getBatchUuid_asB64() - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.prehog.v1alpha.SubmitUsageReportsResponse; - return proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setBatchUuid(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.prehog.v1alpha.SubmitUsageReportsResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.prehog.v1alpha.SubmitUsageReportsResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getBatchUuid_asU8(); - if (f.length > 0) { - writer.writeBytes( - 1, - f - ); - } -}; - - -/** - * optional bytes batch_uuid = 1; - * @return {!(string|Uint8Array)} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * optional bytes batch_uuid = 1; - * This is a type-conversion wrapper around `getBatchUuid()` - * @return {string} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getBatchUuid())); -}; - - -/** - * optional bytes batch_uuid = 1; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getBatchUuid()` - * @return {!Uint8Array} - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.getBatchUuid_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getBatchUuid())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.prehog.v1alpha.SubmitUsageReportsResponse} returns this - */ -proto.prehog.v1alpha.SubmitUsageReportsResponse.prototype.setBatchUuid = function(value) { - return jspb.Message.setProto3BytesField(this, 1, value); -}; - - /** * @enum {number} */ diff --git a/lib/usagereporter/teleport/aggregating/reporter.go b/lib/usagereporter/teleport/aggregating/reporter.go index 22e4f362d1bf6..527d2f79b1ece 100644 --- a/lib/usagereporter/teleport/aggregating/reporter.go +++ b/lib/usagereporter/teleport/aggregating/reporter.go @@ -24,7 +24,7 @@ import ( "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" "github.com/gravitational/teleport/lib/backend" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" diff --git a/lib/usagereporter/teleport/aggregating/service.go b/lib/usagereporter/teleport/aggregating/service.go index e3ea629152a75..e8cafb6dae08e 100644 --- a/lib/usagereporter/teleport/aggregating/service.go +++ b/lib/usagereporter/teleport/aggregating/service.go @@ -23,7 +23,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" "github.com/gravitational/teleport/lib/backend" ) diff --git a/lib/usagereporter/teleport/aggregating/service_test.go b/lib/usagereporter/teleport/aggregating/service_test.go index ad9a6749511d7..d60d57c1d2289 100644 --- a/lib/usagereporter/teleport/aggregating/service_test.go +++ b/lib/usagereporter/teleport/aggregating/service_test.go @@ -26,7 +26,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" "github.com/gravitational/teleport/lib/backend/memory" ) diff --git a/lib/usagereporter/teleport/aggregating/submitter.go b/lib/usagereporter/teleport/aggregating/submitter.go index 0e088f0f85b6b..76d0864bfb5d9 100644 --- a/lib/usagereporter/teleport/aggregating/submitter.go +++ b/lib/usagereporter/teleport/aggregating/submitter.go @@ -25,7 +25,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/retryutils" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" diff --git a/lib/usagereporter/teleport/aggregating/submitter_test.go b/lib/usagereporter/teleport/aggregating/submitter_test.go index 535ac3aa69188..f7b5ffb37d906 100644 --- a/lib/usagereporter/teleport/aggregating/submitter_test.go +++ b/lib/usagereporter/teleport/aggregating/submitter_test.go @@ -26,7 +26,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1" "github.com/gravitational/teleport/lib/backend/memory" "github.com/gravitational/teleport/lib/services/local" ) diff --git a/proto/prehog/v1/teleport.proto b/proto/prehog/v1/teleport.proto new file mode 100644 index 0000000000000..b868f4cb693cd --- /dev/null +++ b/proto/prehog/v1/teleport.proto @@ -0,0 +1,76 @@ +// 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 prehog.v1; + +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/gravitational/teleport/gen/proto/go/prehog/v1;prehogv1"; + +message UserActivityReport { + // randomly generated UUID for this specific report, 16 bytes (in string order) + bytes report_uuid = 1; + // anonymized, 32 bytes (HMAC-SHA-256) + bytes cluster_name = 2; + // anonymized, 32 bytes (HMAC-SHA-256) + bytes reporter_hostid = 3; + // beginning of the time window for this data; ending is not specified but is + // intended to be at most 15 minutes + google.protobuf.Timestamp start_time = 4; + + repeated UserActivityRecord records = 5; +} + +message UserActivityRecord { + // anonymized, 32 bytes (HMAC-SHA-256) + bytes user_name = 1; + + // counter of user.login events + uint64 logins = 2; + + // counter of session.start events (non-Kube) + uint64 ssh_sessions = 3; + // counter of app.session.start events (non-TCP) + uint64 app_sessions = 4; + // counter of session.start events (only Kube) + uint64 kube_sessions = 5; + // counter of db.session.start events + uint64 db_sessions = 6; + // counter of windows.desktop.session.start events + uint64 desktop_sessions = 7; + // counter of app.session.start events (only TCP) + uint64 app_tcp_sessions = 8; + // counter of port events + uint64 ssh_port_sessions = 9; + + // counter of kube.request events + uint64 kube_requests = 10; + // counter of sftp events + uint64 sftp_events = 11; +} + +message SubmitUsageReportsRequest { + // at most 10 in a single RPC, each shouldn't exceed 128KiB or so + repeated UserActivityReport user_activity = 1; +} +message SubmitUsageReportsResponse { + // randomly generated UUID for this specific batch, 16 bytes (in string order) + bytes batch_uuid = 1; +} + +service TeleportReportingService { + rpc SubmitUsageReports(SubmitUsageReportsRequest) returns (SubmitUsageReportsResponse) {} +} diff --git a/proto/prehog/v1alpha/teleport.proto b/proto/prehog/v1alpha/teleport.proto index 898ae8c5d90ac..de191b3a425e0 100644 --- a/proto/prehog/v1alpha/teleport.proto +++ b/proto/prehog/v1alpha/teleport.proto @@ -444,62 +444,10 @@ message SubmitEventsResponse {} message HelloTeleportRequest {} message HelloTeleportResponse {} -message UserActivityReport { - // randomly generated UUID for this specific report, 16 bytes (in string order) - bytes report_uuid = 1; - // anonymized, 32 bytes (HMAC-SHA-256) - bytes cluster_name = 2; - // anonymized, 32 bytes (HMAC-SHA-256) - bytes reporter_hostid = 3; - // beginning of the time window for this data; ending is not specified but is - // intended to be at most 15 minutes - google.protobuf.Timestamp start_time = 4; - - repeated UserActivityRecord records = 5; -} - -message UserActivityRecord { - // anonymized, 32 bytes (HMAC-SHA-256) - bytes user_name = 1; - - // counter of user.login events - uint64 logins = 2; - - // counter of session.start events (non-Kube) - uint64 ssh_sessions = 3; - // counter of app.session.start events (non-TCP) - uint64 app_sessions = 4; - // counter of session.start events (only Kube) - uint64 kube_sessions = 5; - // counter of db.session.start events - uint64 db_sessions = 6; - // counter of windows.desktop.session.start events - uint64 desktop_sessions = 7; - // counter of app.session.start events (only TCP) - uint64 app_tcp_sessions = 8; - // counter of port events - uint64 ssh_port_sessions = 9; - - // counter of kube.request events - uint64 kube_requests = 10; - // counter of sftp events - uint64 sftp_events = 11; -} - -message SubmitUsageReportsRequest { - repeated UserActivityReport user_activity = 1; -} -message SubmitUsageReportsResponse { - // randomly generated UUID for this specific batch, 16 bytes (in string order) - bytes batch_uuid = 1; -} - service TeleportReportingService { rpc SubmitEvent(SubmitEventRequest) returns (SubmitEventResponse) { option deprecated = true; } rpc SubmitEvents(SubmitEventsRequest) returns (SubmitEventsResponse) {} rpc HelloTeleport(HelloTeleportRequest) returns (HelloTeleportResponse) {} - - rpc SubmitUsageReports(SubmitUsageReportsRequest) returns (SubmitUsageReportsResponse) {} } From 6588cbb783fdceb25020c07cb7fd19702fcdfd01 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 5 Apr 2023 16:18:50 +0200 Subject: [PATCH 31/32] mechanical: rename imports of prehog/v1alpha This renames imports of ".../prehog/v1alpha" from prehogv1a to prehogv1, and imports of ".../prehog/v1alpha/prehogv1alphaconnect" from prehogv1c to prehogv1ac, to avoid confusion with imports of the newly added ".../prehog/v1" and ".../prehog/v1/prehogv1connect". --- lib/usagereporter/daemon/usagereporter.go | 30 +-- lib/usagereporter/teleport/types.go | 248 +++++++++--------- lib/usagereporter/teleport/types_discover.go | 176 ++++++------- lib/usagereporter/teleport/usagereporter.go | 18 +- .../teleport/usagereporter_test.go | 53 ++-- tool/tbot/anonymous_telemetry.go | 20 +- tool/tbot/anonymous_telemetry_test.go | 16 +- 7 files changed, 281 insertions(+), 280 deletions(-) diff --git a/lib/usagereporter/daemon/usagereporter.go b/lib/usagereporter/daemon/usagereporter.go index 5a86a31ce7d40..5944200d45a15 100644 --- a/lib/usagereporter/daemon/usagereporter.go +++ b/lib/usagereporter/daemon/usagereporter.go @@ -23,8 +23,8 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1ac "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" teletermv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/usagereporter" @@ -61,9 +61,9 @@ const ( ) type ( - UsageReporter = usagereporter.UsageReporter[prehogv1.SubmitConnectEventRequest] - SubmitFunc = usagereporter.SubmitFunc[prehogv1.SubmitConnectEventRequest] - SubmittedEvent = usagereporter.SubmittedEvent[prehogv1.SubmitConnectEventRequest] + UsageReporter = usagereporter.UsageReporter[prehogv1a.SubmitConnectEventRequest] + SubmitFunc = usagereporter.SubmitFunc[prehogv1a.SubmitConnectEventRequest] + SubmittedEvent = usagereporter.SubmittedEvent[prehogv1a.SubmitConnectEventRequest] ) func NewConnectUsageReporter(ctx context.Context, prehogAddr string) (*UsageReporter, error) { @@ -71,7 +71,7 @@ func NewConnectUsageReporter(ctx context.Context, prehogAddr string) (*UsageRepo if err != nil { return nil, trace.Wrap(err) } - return usagereporter.NewUsageReporter(&usagereporter.Options[prehogv1.SubmitConnectEventRequest]{ + return usagereporter.NewUsageReporter(&usagereporter.Options[prehogv1a.SubmitConnectEventRequest]{ Submit: submitter, MinBatchSize: minBatchSize, MaxBatchSize: maxBatchSize, @@ -97,7 +97,7 @@ func newPrehogSubmitter(ctx context.Context, prehogEndpoint string) (SubmitFunc, Timeout: 5 * time.Second, } - client := prehogv1c.NewConnectReportingServiceClient(httpClient, prehogEndpoint) + client := prehogv1ac.NewConnectReportingServiceClient(httpClient, prehogEndpoint) return func(reporter *UsageReporter, events []*SubmittedEvent) ([]*SubmittedEvent, error) { var failed []*SubmittedEvent @@ -118,12 +118,12 @@ func newPrehogSubmitter(ctx context.Context, prehogEndpoint string) (SubmitFunc, }, nil } -func GetAnonymizedPrehogEvent(req *teletermv1.ReportUsageEventRequest) (*prehogv1.SubmitConnectEventRequest, error) { +func GetAnonymizedPrehogEvent(req *teletermv1.ReportUsageEventRequest) (*prehogv1a.SubmitConnectEventRequest, error) { prehogEvent := req.PrehogReq // non-anonymized switch prehogEvent.GetEvent().(type) { - case *prehogv1.SubmitConnectEventRequest_UserJobRoleUpdate: + case *prehogv1a.SubmitConnectEventRequest_UserJobRoleUpdate: return prehogEvent, nil } @@ -133,27 +133,27 @@ func GetAnonymizedPrehogEvent(req *teletermv1.ReportUsageEventRequest) (*prehogv return nil, trace.Wrap(err) } switch e := prehogEvent.GetEvent().(type) { - case *prehogv1.SubmitConnectEventRequest_ClusterLogin: + case *prehogv1a.SubmitConnectEventRequest_ClusterLogin: e.ClusterLogin.ClusterName = anonymizer.AnonymizeString(e.ClusterLogin.ClusterName) e.ClusterLogin.UserName = anonymizer.AnonymizeString(e.ClusterLogin.UserName) return prehogEvent, nil - case *prehogv1.SubmitConnectEventRequest_ProtocolUse: + case *prehogv1a.SubmitConnectEventRequest_ProtocolUse: e.ProtocolUse.ClusterName = anonymizer.AnonymizeString(e.ProtocolUse.ClusterName) e.ProtocolUse.UserName = anonymizer.AnonymizeString(e.ProtocolUse.UserName) return prehogEvent, nil - case *prehogv1.SubmitConnectEventRequest_AccessRequestCreate: + case *prehogv1a.SubmitConnectEventRequest_AccessRequestCreate: e.AccessRequestCreate.ClusterName = anonymizer.AnonymizeString(e.AccessRequestCreate.ClusterName) e.AccessRequestCreate.UserName = anonymizer.AnonymizeString(e.AccessRequestCreate.UserName) return prehogEvent, nil - case *prehogv1.SubmitConnectEventRequest_AccessRequestReview: + case *prehogv1a.SubmitConnectEventRequest_AccessRequestReview: e.AccessRequestReview.ClusterName = anonymizer.AnonymizeString(e.AccessRequestReview.ClusterName) e.AccessRequestReview.UserName = anonymizer.AnonymizeString(e.AccessRequestReview.UserName) return prehogEvent, nil - case *prehogv1.SubmitConnectEventRequest_AccessRequestAssumeRole: + case *prehogv1a.SubmitConnectEventRequest_AccessRequestAssumeRole: e.AccessRequestAssumeRole.ClusterName = anonymizer.AnonymizeString(e.AccessRequestAssumeRole.ClusterName) e.AccessRequestAssumeRole.UserName = anonymizer.AnonymizeString(e.AccessRequestAssumeRole.UserName) return prehogEvent, nil - case *prehogv1.SubmitConnectEventRequest_FileTransferRun: + case *prehogv1a.SubmitConnectEventRequest_FileTransferRun: e.FileTransferRun.ClusterName = anonymizer.AnonymizeString(e.FileTransferRun.ClusterName) e.FileTransferRun.UserName = anonymizer.AnonymizeString(e.FileTransferRun.UserName) return prehogEvent, nil diff --git a/lib/usagereporter/teleport/types.go b/lib/usagereporter/teleport/types.go index b9c8cbfe7b2ae..69b4badd571d1 100644 --- a/lib/usagereporter/teleport/types.go +++ b/lib/usagereporter/teleport/types.go @@ -23,7 +23,7 @@ import ( "github.com/gravitational/teleport" usageeventsv1 "github.com/gravitational/teleport/api/gen/proto/go/usageevents/v1" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" "github.com/gravitational/teleport/lib/utils" ) @@ -31,21 +31,21 @@ import ( type Anonymizable interface { // Anonymize uses the given anonymizer to anonymize the event and converts // it into a partially filled SubmitEventRequest. - Anonymize(utils.Anonymizer) prehogv1.SubmitEventRequest + Anonymize(utils.Anonymizer) prehogv1a.SubmitEventRequest } // UserLoginEvent is an event emitted when a user logs into Teleport, // potentially via SSO. -type UserLoginEvent prehogv1.UserLoginEvent +type UserLoginEvent prehogv1a.UserLoginEvent -func (u *UserLoginEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { +func (u *UserLoginEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { var deviceID string if u.DeviceId != "" { deviceID = a.AnonymizeString(u.DeviceId) } - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UserLogin{ - UserLogin: &prehogv1.UserLoginEvent{ + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UserLogin{ + UserLogin: &prehogv1a.UserLoginEvent{ UserName: a.AnonymizeString(u.UserName), ConnectorType: u.ConnectorType, DeviceId: deviceID, @@ -55,12 +55,12 @@ func (u *UserLoginEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventReque } // SSOCreateEvent is emitted when an SSO connector has been created. -type SSOCreateEvent prehogv1.SSOCreateEvent +type SSOCreateEvent prehogv1a.SSOCreateEvent -func (u *SSOCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_SsoCreate{ - SsoCreate: &prehogv1.SSOCreateEvent{ +func (u *SSOCreateEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_SsoCreate{ + SsoCreate: &prehogv1a.SSOCreateEvent{ ConnectorType: u.ConnectorType, }, }, @@ -69,12 +69,12 @@ func (u *SSOCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventReque // SessionStartEvent is an event emitted when some Teleport session has started // (ssh, etc). -type SessionStartEvent prehogv1.SessionStartEvent +type SessionStartEvent prehogv1a.SessionStartEvent -func (u *SessionStartEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_SessionStartV2{ - SessionStartV2: &prehogv1.SessionStartEvent{ +func (u *SessionStartEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_SessionStartV2{ + SessionStartV2: &prehogv1a.SessionStartEvent{ UserName: a.AnonymizeString(u.UserName), SessionType: u.SessionType, }, @@ -84,12 +84,12 @@ func (u *SessionStartEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRe // ResourceCreateEvent is an event emitted when various resource types have been // created. -type ResourceCreateEvent prehogv1.ResourceCreateEvent +type ResourceCreateEvent prehogv1a.ResourceCreateEvent -func (u *ResourceCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_ResourceCreate{ - ResourceCreate: &prehogv1.ResourceCreateEvent{ +func (u *ResourceCreateEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_ResourceCreate{ + ResourceCreate: &prehogv1a.ResourceCreateEvent{ ResourceType: u.ResourceType, }, }, @@ -97,12 +97,12 @@ func (u *ResourceCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEvent } // UIBannerClickEvent is a UI event sent when a banner is clicked. -type UIBannerClickEvent prehogv1.UIBannerClickEvent +type UIBannerClickEvent prehogv1a.UIBannerClickEvent -func (u *UIBannerClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiBannerClick{ - UiBannerClick: &prehogv1.UIBannerClickEvent{ +func (u *UIBannerClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiBannerClick{ + UiBannerClick: &prehogv1a.UIBannerClickEvent{ UserName: a.AnonymizeString(u.UserName), Alert: u.Alert, }, @@ -112,12 +112,12 @@ func (u *UIBannerClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventR // UIOnboardCompleteGoToDashboardClickEvent is a UI event sent when // onboarding is complete. -type UIOnboardCompleteGoToDashboardClickEvent prehogv1.UIOnboardCompleteGoToDashboardClickEvent +type UIOnboardCompleteGoToDashboardClickEvent prehogv1a.UIOnboardCompleteGoToDashboardClickEvent -func (u *UIOnboardCompleteGoToDashboardClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiOnboardCompleteGoToDashboardClick{ - UiOnboardCompleteGoToDashboardClick: &prehogv1.UIOnboardCompleteGoToDashboardClickEvent{ +func (u *UIOnboardCompleteGoToDashboardClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiOnboardCompleteGoToDashboardClick{ + UiOnboardCompleteGoToDashboardClick: &prehogv1a.UIOnboardCompleteGoToDashboardClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -126,12 +126,12 @@ func (u *UIOnboardCompleteGoToDashboardClickEvent) Anonymize(a utils.Anonymizer) // UIOnboardAddFirstResourceClickEvent is a UI event sent when a user // clicks the "add first resource" button. -type UIOnboardAddFirstResourceClickEvent prehogv1.UIOnboardAddFirstResourceClickEvent +type UIOnboardAddFirstResourceClickEvent prehogv1a.UIOnboardAddFirstResourceClickEvent -func (u *UIOnboardAddFirstResourceClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiOnboardAddFirstResourceClick{ - UiOnboardAddFirstResourceClick: &prehogv1.UIOnboardAddFirstResourceClickEvent{ +func (u *UIOnboardAddFirstResourceClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiOnboardAddFirstResourceClick{ + UiOnboardAddFirstResourceClick: &prehogv1a.UIOnboardAddFirstResourceClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -140,12 +140,12 @@ func (u *UIOnboardAddFirstResourceClickEvent) Anonymize(a utils.Anonymizer) preh // UIOnboardAddFirstResourceLaterClickEvent is a UI event sent when a user // clicks the "add first resource later" button. -type UIOnboardAddFirstResourceLaterClickEvent prehogv1.UIOnboardAddFirstResourceLaterClickEvent +type UIOnboardAddFirstResourceLaterClickEvent prehogv1a.UIOnboardAddFirstResourceLaterClickEvent -func (u *UIOnboardAddFirstResourceLaterClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiOnboardAddFirstResourceLaterClick{ - UiOnboardAddFirstResourceLaterClick: &prehogv1.UIOnboardAddFirstResourceLaterClickEvent{ +func (u *UIOnboardAddFirstResourceLaterClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiOnboardAddFirstResourceLaterClick{ + UiOnboardAddFirstResourceLaterClick: &prehogv1a.UIOnboardAddFirstResourceLaterClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -154,12 +154,12 @@ func (u *UIOnboardAddFirstResourceLaterClickEvent) Anonymize(a utils.Anonymizer) // UIOnboardSetCredentialSubmitEvent is an UI event sent during registration // when the user configures login credentials. -type UIOnboardSetCredentialSubmitEvent prehogv1.UIOnboardSetCredentialSubmitEvent +type UIOnboardSetCredentialSubmitEvent prehogv1a.UIOnboardSetCredentialSubmitEvent -func (u *UIOnboardSetCredentialSubmitEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiOnboardSetCredentialSubmit{ - UiOnboardSetCredentialSubmit: &prehogv1.UIOnboardSetCredentialSubmitEvent{ +func (u *UIOnboardSetCredentialSubmitEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiOnboardSetCredentialSubmit{ + UiOnboardSetCredentialSubmit: &prehogv1a.UIOnboardSetCredentialSubmitEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -168,12 +168,12 @@ func (u *UIOnboardSetCredentialSubmitEvent) Anonymize(a utils.Anonymizer) prehog // UIOnboardRegisterChallengeSubmitEvent is a UI event sent during registration // when the MFA challenge is completed. -type UIOnboardRegisterChallengeSubmitEvent prehogv1.UIOnboardRegisterChallengeSubmitEvent +type UIOnboardRegisterChallengeSubmitEvent prehogv1a.UIOnboardRegisterChallengeSubmitEvent -func (u *UIOnboardRegisterChallengeSubmitEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiOnboardRegisterChallengeSubmit{ - UiOnboardRegisterChallengeSubmit: &prehogv1.UIOnboardRegisterChallengeSubmitEvent{ +func (u *UIOnboardRegisterChallengeSubmitEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiOnboardRegisterChallengeSubmit{ + UiOnboardRegisterChallengeSubmit: &prehogv1a.UIOnboardRegisterChallengeSubmitEvent{ UserName: a.AnonymizeString(u.UserName), MfaType: u.MfaType, LoginFlow: u.LoginFlow, @@ -183,12 +183,12 @@ func (u *UIOnboardRegisterChallengeSubmitEvent) Anonymize(a utils.Anonymizer) pr } // UIRecoveryCodesContinueClickEvent is a UI event sent when a user configures recovery codes. -type UIRecoveryCodesContinueClickEvent prehogv1.UIRecoveryCodesContinueClickEvent +type UIRecoveryCodesContinueClickEvent prehogv1a.UIRecoveryCodesContinueClickEvent -func (u *UIRecoveryCodesContinueClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiRecoveryCodesContinueClick{ - UiRecoveryCodesContinueClick: &prehogv1.UIRecoveryCodesContinueClickEvent{ +func (u *UIRecoveryCodesContinueClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiRecoveryCodesContinueClick{ + UiRecoveryCodesContinueClick: &prehogv1a.UIRecoveryCodesContinueClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -196,12 +196,12 @@ func (u *UIRecoveryCodesContinueClickEvent) Anonymize(a utils.Anonymizer) prehog } // UIRecoveryCodesCopyClickEvent is a UI event sent when a user copies recovery codes. -type UIRecoveryCodesCopyClickEvent prehogv1.UIRecoveryCodesCopyClickEvent +type UIRecoveryCodesCopyClickEvent prehogv1a.UIRecoveryCodesCopyClickEvent -func (u *UIRecoveryCodesCopyClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiRecoveryCodesCopyClick{ - UiRecoveryCodesCopyClick: &prehogv1.UIRecoveryCodesCopyClickEvent{ +func (u *UIRecoveryCodesCopyClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiRecoveryCodesCopyClick{ + UiRecoveryCodesCopyClick: &prehogv1a.UIRecoveryCodesCopyClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -209,12 +209,12 @@ func (u *UIRecoveryCodesCopyClickEvent) Anonymize(a utils.Anonymizer) prehogv1.S } // UsageUIRecoveryCodesPrintClick is a UI event sent when a user prints recovery codes. -type UsageUIRecoveryCodesPrintClick prehogv1.UIRecoveryCodesPrintClickEvent +type UsageUIRecoveryCodesPrintClick prehogv1a.UIRecoveryCodesPrintClickEvent -func (u *UsageUIRecoveryCodesPrintClick) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiRecoveryCodesPrintClick{ - UiRecoveryCodesPrintClick: &prehogv1.UIRecoveryCodesPrintClickEvent{ +func (u *UsageUIRecoveryCodesPrintClick) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiRecoveryCodesPrintClick{ + UiRecoveryCodesPrintClick: &prehogv1a.UIRecoveryCodesPrintClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -222,17 +222,17 @@ func (u *UsageUIRecoveryCodesPrintClick) Anonymize(a utils.Anonymizer) prehogv1. } // RoleCreateEvent is an event emitted when a custom role is created. -type RoleCreateEvent prehogv1.RoleCreateEvent +type RoleCreateEvent prehogv1a.RoleCreateEvent -func (u *RoleCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { +func (u *RoleCreateEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { role := u.RoleName if !slices.Contains(teleport.PresetRoles, u.RoleName) { role = a.AnonymizeString(u.RoleName) } - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_RoleCreate{ - RoleCreate: &prehogv1.RoleCreateEvent{ + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_RoleCreate{ + RoleCreate: &prehogv1a.RoleCreateEvent{ UserName: a.AnonymizeString(u.UserName), RoleName: role, }, @@ -241,12 +241,12 @@ func (u *RoleCreateEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequ } // UICreateNewRoleClickEvent is a UI event sent when a user prints recovery codes. -type UICreateNewRoleClickEvent prehogv1.UICreateNewRoleClickEvent +type UICreateNewRoleClickEvent prehogv1a.UICreateNewRoleClickEvent -func (u *UICreateNewRoleClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiCreateNewRoleClick{ - UiCreateNewRoleClick: &prehogv1.UICreateNewRoleClickEvent{ +func (u *UICreateNewRoleClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiCreateNewRoleClick{ + UiCreateNewRoleClick: &prehogv1a.UICreateNewRoleClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -254,12 +254,12 @@ func (u *UICreateNewRoleClickEvent) Anonymize(a utils.Anonymizer) prehogv1.Submi } // UICreateNewRoleSaveClickEvent is a UI event sent when a user prints recovery codes. -type UICreateNewRoleSaveClickEvent prehogv1.UICreateNewRoleSaveClickEvent +type UICreateNewRoleSaveClickEvent prehogv1a.UICreateNewRoleSaveClickEvent -func (u *UICreateNewRoleSaveClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiCreateNewRoleSaveClick{ - UiCreateNewRoleSaveClick: &prehogv1.UICreateNewRoleSaveClickEvent{ +func (u *UICreateNewRoleSaveClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiCreateNewRoleSaveClick{ + UiCreateNewRoleSaveClick: &prehogv1a.UICreateNewRoleSaveClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -267,12 +267,12 @@ func (u *UICreateNewRoleSaveClickEvent) Anonymize(a utils.Anonymizer) prehogv1.S } // UICreateNewRoleCancelClickEvent is a UI event sent when a user prints recovery codes. -type UICreateNewRoleCancelClickEvent prehogv1.UICreateNewRoleCancelClickEvent +type UICreateNewRoleCancelClickEvent prehogv1a.UICreateNewRoleCancelClickEvent -func (u *UICreateNewRoleCancelClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiCreateNewRoleCancelClick{ - UiCreateNewRoleCancelClick: &prehogv1.UICreateNewRoleCancelClickEvent{ +func (u *UICreateNewRoleCancelClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiCreateNewRoleCancelClick{ + UiCreateNewRoleCancelClick: &prehogv1a.UICreateNewRoleCancelClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -280,12 +280,12 @@ func (u *UICreateNewRoleCancelClickEvent) Anonymize(a utils.Anonymizer) prehogv1 } // UICreateNewRoleViewDocumentationClickEvent is a UI event sent when a user prints recovery codes. -type UICreateNewRoleViewDocumentationClickEvent prehogv1.UICreateNewRoleViewDocumentationClickEvent +type UICreateNewRoleViewDocumentationClickEvent prehogv1a.UICreateNewRoleViewDocumentationClickEvent -func (u *UICreateNewRoleViewDocumentationClickEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiCreateNewRoleViewDocumentationClick{ - UiCreateNewRoleViewDocumentationClick: &prehogv1.UICreateNewRoleViewDocumentationClickEvent{ +func (u *UICreateNewRoleViewDocumentationClickEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiCreateNewRoleViewDocumentationClick{ + UiCreateNewRoleViewDocumentationClick: &prehogv1a.UICreateNewRoleViewDocumentationClickEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -294,12 +294,12 @@ func (u *UICreateNewRoleViewDocumentationClickEvent) Anonymize(a utils.Anonymize // UserCertificateIssuedEvent is an event emitted when a certificate has been // issued, used to track the duration and restriction. -type UserCertificateIssuedEvent prehogv1.UserCertificateIssuedEvent +type UserCertificateIssuedEvent prehogv1a.UserCertificateIssuedEvent -func (u *UserCertificateIssuedEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UserCertificateIssuedEvent{ - UserCertificateIssuedEvent: &prehogv1.UserCertificateIssuedEvent{ +func (u *UserCertificateIssuedEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UserCertificateIssuedEvent{ + UserCertificateIssuedEvent: &prehogv1a.UserCertificateIssuedEvent{ UserName: a.AnonymizeString(u.UserName), Ttl: u.Ttl, IsBot: u.IsBot, @@ -314,12 +314,12 @@ func (u *UserCertificateIssuedEvent) Anonymize(a utils.Anonymizer) prehogv1.Subm // KubeRequestEvent is an event emitted when a Kubernetes API request is // handled. -type KubeRequestEvent prehogv1.KubeRequestEvent +type KubeRequestEvent prehogv1a.KubeRequestEvent -func (u *KubeRequestEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_KubeRequest{ - KubeRequest: &prehogv1.KubeRequestEvent{ +func (u *KubeRequestEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_KubeRequest{ + KubeRequest: &prehogv1a.KubeRequestEvent{ UserName: a.AnonymizeString(u.UserName), }, }, @@ -327,12 +327,12 @@ func (u *KubeRequestEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventReq } // SFTPEvent is an event emitted for each file operation in a SFTP connection. -type SFTPEvent prehogv1.SFTPEvent +type SFTPEvent prehogv1a.SFTPEvent -func (u *SFTPEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_Sftp{ - Sftp: &prehogv1.SFTPEvent{ +func (u *SFTPEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_Sftp{ + Sftp: &prehogv1a.SFTPEvent{ UserName: a.AnonymizeString(u.UserName), Action: u.Action, }, @@ -341,12 +341,12 @@ func (u *SFTPEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { } // AgentMetadataEvent is an event emitted after an agent first connects to the auth server. -type AgentMetadataEvent prehogv1.AgentMetadataEvent +type AgentMetadataEvent prehogv1a.AgentMetadataEvent -func (u *AgentMetadataEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_AgentMetadataEvent{ - AgentMetadataEvent: &prehogv1.AgentMetadataEvent{ +func (u *AgentMetadataEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_AgentMetadataEvent{ + AgentMetadataEvent: &prehogv1a.AgentMetadataEvent{ Version: u.Version, HostId: a.AnonymizeString(u.HostId), Services: u.Services, @@ -363,15 +363,15 @@ func (u *AgentMetadataEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventR } } -type ResourceKind = prehogv1.ResourceKind +type ResourceKind = prehogv1a.ResourceKind const ( - ResourceKindNode = prehogv1.ResourceKind_RESOURCE_KIND_NODE - ResourceKindAppServer = prehogv1.ResourceKind_RESOURCE_KIND_APP_SERVER - ResourceKindKubeServer = prehogv1.ResourceKind_RESOURCE_KIND_KUBE_SERVER - ResourceKindDBServer = prehogv1.ResourceKind_RESOURCE_KIND_DB_SERVER - ResourceKindWindowsDesktop = prehogv1.ResourceKind_RESOURCE_KIND_WINDOWS_DESKTOP - ResourceKindNodeOpenSSH = prehogv1.ResourceKind_RESOURCE_KIND_NODE_OPENSSH + ResourceKindNode = prehogv1a.ResourceKind_RESOURCE_KIND_NODE + ResourceKindAppServer = prehogv1a.ResourceKind_RESOURCE_KIND_APP_SERVER + ResourceKindKubeServer = prehogv1a.ResourceKind_RESOURCE_KIND_KUBE_SERVER + ResourceKindDBServer = prehogv1a.ResourceKind_RESOURCE_KIND_DB_SERVER + ResourceKindWindowsDesktop = prehogv1a.ResourceKind_RESOURCE_KIND_WINDOWS_DESKTOP + ResourceKindNodeOpenSSH = prehogv1a.ResourceKind_RESOURCE_KIND_NODE_OPENSSH ) func ResourceKindFromKeepAliveType(t types.KeepAlive_KeepAliveType) ResourceKind { @@ -391,14 +391,14 @@ func ResourceKindFromKeepAliveType(t types.KeepAlive_KeepAliveType) ResourceKind type ResourceHeartbeatEvent struct { Name string - Kind prehogv1.ResourceKind + Kind prehogv1a.ResourceKind Static bool } -func (u *ResourceHeartbeatEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_ResourceHeartbeat{ - ResourceHeartbeat: &prehogv1.ResourceHeartbeatEvent{ +func (u *ResourceHeartbeatEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_ResourceHeartbeat{ + ResourceHeartbeat: &prehogv1a.ResourceHeartbeatEvent{ ResourceName: a.AnonymizeNonEmpty(u.Name), ResourceKind: u.Kind, Static: u.Static, diff --git a/lib/usagereporter/teleport/types_discover.go b/lib/usagereporter/teleport/types_discover.go index 42f31ffe11919..4d6f3a78e7241 100644 --- a/lib/usagereporter/teleport/types_discover.go +++ b/lib/usagereporter/teleport/types_discover.go @@ -20,19 +20,19 @@ import ( "github.com/gravitational/trace" usageeventsv1 "github.com/gravitational/teleport/api/gen/proto/go/usageevents/v1" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" "github.com/gravitational/teleport/lib/utils" ) -func discoverMetadataToPrehog(u *usageeventsv1.DiscoverMetadata, userMD UserMetadata) *prehogv1.DiscoverMetadata { - return &prehogv1.DiscoverMetadata{ +func discoverMetadataToPrehog(u *usageeventsv1.DiscoverMetadata, userMD UserMetadata) *prehogv1a.DiscoverMetadata { + return &prehogv1a.DiscoverMetadata{ Id: u.Id, UserName: userMD.Username, Sso: userMD.IsSSO, } } -func validateDiscoverMetadata(u *prehogv1.DiscoverMetadata) error { +func validateDiscoverMetadata(u *prehogv1a.DiscoverMetadata) error { if u == nil { return trace.BadParameter("metadata is required") } @@ -44,48 +44,48 @@ func validateDiscoverMetadata(u *prehogv1.DiscoverMetadata) error { return nil } -func discoverResourceToPrehog(u *usageeventsv1.DiscoverResourceMetadata) *prehogv1.DiscoverResourceMetadata { - return &prehogv1.DiscoverResourceMetadata{ - Resource: prehogv1.DiscoverResource(u.Resource), +func discoverResourceToPrehog(u *usageeventsv1.DiscoverResourceMetadata) *prehogv1a.DiscoverResourceMetadata { + return &prehogv1a.DiscoverResourceMetadata{ + Resource: prehogv1a.DiscoverResource(u.Resource), } } -func validateDiscoverResourceMetadata(u *prehogv1.DiscoverResourceMetadata) error { +func validateDiscoverResourceMetadata(u *prehogv1a.DiscoverResourceMetadata) error { if u == nil { return trace.BadParameter("resource is required") } - if u.Resource == prehogv1.DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED { + if u.Resource == prehogv1a.DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED { return trace.BadParameter("invalid resource") } return nil } -func discoverStatusToPrehog(u *usageeventsv1.DiscoverStepStatus) *prehogv1.DiscoverStepStatus { - return &prehogv1.DiscoverStepStatus{ - Status: prehogv1.DiscoverStatus(u.Status), +func discoverStatusToPrehog(u *usageeventsv1.DiscoverStepStatus) *prehogv1a.DiscoverStepStatus { + return &prehogv1a.DiscoverStepStatus{ + Status: prehogv1a.DiscoverStatus(u.Status), Error: u.Error, } } -func validateDiscoverStatus(u *prehogv1.DiscoverStepStatus) error { +func validateDiscoverStatus(u *prehogv1a.DiscoverStepStatus) error { if u == nil { return trace.BadParameter("status is required") } - if u.Status == prehogv1.DiscoverStatus_DISCOVER_STATUS_UNSPECIFIED { + if u.Status == prehogv1a.DiscoverStatus_DISCOVER_STATUS_UNSPECIFIED { return trace.BadParameter("invalid status.status") } - if u.Status == prehogv1.DiscoverStatus_DISCOVER_STATUS_ERROR && len(u.Error) == 0 { + if u.Status == prehogv1a.DiscoverStatus_DISCOVER_STATUS_ERROR && len(u.Error) == 0 { return trace.BadParameter("status.error is required when status.status is ERROR") } return nil } -func validateDiscoverBaseEventFields(md *prehogv1.DiscoverMetadata, res *prehogv1.DiscoverResourceMetadata, st *prehogv1.DiscoverStepStatus) error { +func validateDiscoverBaseEventFields(md *prehogv1a.DiscoverMetadata, res *prehogv1a.DiscoverResourceMetadata, st *prehogv1a.DiscoverStepStatus) error { if err := validateDiscoverMetadata(md); err != nil { return trace.Wrap(err) } @@ -102,7 +102,7 @@ func validateDiscoverBaseEventFields(md *prehogv1.DiscoverMetadata, res *prehogv } // UIDiscoverStartedEvent is a UI event sent when a user starts the Discover Wizard. -type UIDiscoverStartedEvent prehogv1.UIDiscoverStartedEvent +type UIDiscoverStartedEvent prehogv1a.UIDiscoverStartedEvent func (u *UIDiscoverStartedEvent) CheckAndSetDefaults() error { if err := validateDiscoverMetadata(u.Metadata); err != nil { @@ -115,11 +115,11 @@ func (u *UIDiscoverStartedEvent) CheckAndSetDefaults() error { return nil } -func (u *UIDiscoverStartedEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverStartedEvent{ - UiDiscoverStartedEvent: &prehogv1.UIDiscoverStartedEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverStartedEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverStartedEvent{ + UiDiscoverStartedEvent: &prehogv1a.UIDiscoverStartedEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), Sso: u.Metadata.Sso, @@ -131,17 +131,17 @@ func (u *UIDiscoverStartedEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEv } // UIDiscoverResourceSelectionEvent is a UI event sent when a user starts the Discover Wizard. -type UIDiscoverResourceSelectionEvent prehogv1.UIDiscoverResourceSelectionEvent +type UIDiscoverResourceSelectionEvent prehogv1a.UIDiscoverResourceSelectionEvent func (u *UIDiscoverResourceSelectionEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverResourceSelectionEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverResourceSelectionEvent{ - UiDiscoverResourceSelectionEvent: &prehogv1.UIDiscoverResourceSelectionEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverResourceSelectionEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverResourceSelectionEvent{ + UiDiscoverResourceSelectionEvent: &prehogv1a.UIDiscoverResourceSelectionEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -159,17 +159,17 @@ func (u *UIDiscoverResourceSelectionEvent) Anonymize(a utils.Anonymizer) prehogv // // For Database Access this step is the installation of the teleport 'install-db' script. // It can be skipped if the cluster already has a Database Service capable of proxying the database. -type UIDiscoverDeployServiceEvent prehogv1.UIDiscoverDeployServiceEvent +type UIDiscoverDeployServiceEvent prehogv1a.UIDiscoverDeployServiceEvent func (u *UIDiscoverDeployServiceEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDeployServiceEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDeployServiceEvent{ - UiDiscoverDeployServiceEvent: &prehogv1.UIDiscoverDeployServiceEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDeployServiceEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDeployServiceEvent{ + UiDiscoverDeployServiceEvent: &prehogv1a.UIDiscoverDeployServiceEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -182,17 +182,17 @@ func (u *UIDiscoverDeployServiceEvent) Anonymize(a utils.Anonymizer) prehogv1.Su // UIDiscoverDatabaseRegisterEvent is emitted when a user registers a database resource // and goes to the next step. -type UIDiscoverDatabaseRegisterEvent prehogv1.UIDiscoverDatabaseRegisterEvent +type UIDiscoverDatabaseRegisterEvent prehogv1a.UIDiscoverDatabaseRegisterEvent func (u *UIDiscoverDatabaseRegisterEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDatabaseRegisterEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDatabaseRegisterEvent{ - UiDiscoverDatabaseRegisterEvent: &prehogv1.UIDiscoverDatabaseRegisterEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDatabaseRegisterEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDatabaseRegisterEvent{ + UiDiscoverDatabaseRegisterEvent: &prehogv1a.UIDiscoverDatabaseRegisterEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -205,17 +205,17 @@ func (u *UIDiscoverDatabaseRegisterEvent) Anonymize(a utils.Anonymizer) prehogv1 // UIDiscoverDatabaseConfigureMTLSEvent is emitted when a user configures mutual TLS for self-hosted database // and goes to the next step. -type UIDiscoverDatabaseConfigureMTLSEvent prehogv1.UIDiscoverDatabaseConfigureMTLSEvent +type UIDiscoverDatabaseConfigureMTLSEvent prehogv1a.UIDiscoverDatabaseConfigureMTLSEvent func (u *UIDiscoverDatabaseConfigureMTLSEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDatabaseConfigureMTLSEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDatabaseConfigureMtlsEvent{ - UiDiscoverDatabaseConfigureMtlsEvent: &prehogv1.UIDiscoverDatabaseConfigureMTLSEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDatabaseConfigureMTLSEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDatabaseConfigureMtlsEvent{ + UiDiscoverDatabaseConfigureMtlsEvent: &prehogv1a.UIDiscoverDatabaseConfigureMTLSEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -228,17 +228,17 @@ func (u *UIDiscoverDatabaseConfigureMTLSEvent) Anonymize(a utils.Anonymizer) pre // UIDiscoverDesktopActiveDirectoryToolsInstallEvent is emitted when the user is asked to run the install Active Directory tools script. // This happens on the Desktop flow. -type UIDiscoverDesktopActiveDirectoryToolsInstallEvent prehogv1.UIDiscoverDesktopActiveDirectoryToolsInstallEvent +type UIDiscoverDesktopActiveDirectoryToolsInstallEvent prehogv1a.UIDiscoverDesktopActiveDirectoryToolsInstallEvent func (u *UIDiscoverDesktopActiveDirectoryToolsInstallEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDesktopActiveDirectoryToolsInstallEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDesktopActiveDirectoryToolsInstallEvent{ - UiDiscoverDesktopActiveDirectoryToolsInstallEvent: &prehogv1.UIDiscoverDesktopActiveDirectoryToolsInstallEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDesktopActiveDirectoryToolsInstallEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDesktopActiveDirectoryToolsInstallEvent{ + UiDiscoverDesktopActiveDirectoryToolsInstallEvent: &prehogv1a.UIDiscoverDesktopActiveDirectoryToolsInstallEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -251,17 +251,17 @@ func (u *UIDiscoverDesktopActiveDirectoryToolsInstallEvent) Anonymize(a utils.An // UIDiscoverDesktopActiveDirectoryConfigureEvent is emitted when the user is asked to run the Configure Active Directory script. // This happens on the Desktop flow. -type UIDiscoverDesktopActiveDirectoryConfigureEvent prehogv1.UIDiscoverDesktopActiveDirectoryConfigureEvent +type UIDiscoverDesktopActiveDirectoryConfigureEvent prehogv1a.UIDiscoverDesktopActiveDirectoryConfigureEvent func (u *UIDiscoverDesktopActiveDirectoryConfigureEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDesktopActiveDirectoryConfigureEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDesktopActiveDirectoryConfigureEvent{ - UiDiscoverDesktopActiveDirectoryConfigureEvent: &prehogv1.UIDiscoverDesktopActiveDirectoryConfigureEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDesktopActiveDirectoryConfigureEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDesktopActiveDirectoryConfigureEvent{ + UiDiscoverDesktopActiveDirectoryConfigureEvent: &prehogv1a.UIDiscoverDesktopActiveDirectoryConfigureEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -274,7 +274,7 @@ func (u *UIDiscoverDesktopActiveDirectoryConfigureEvent) Anonymize(a utils.Anony // UIDiscoverAutoDiscoveredResourcesEvent is emitted when the user is presented with the list of auto discovered resources. // resources_count field must contain the number of discovered resources when the user leaves the screen. -type UIDiscoverAutoDiscoveredResourcesEvent prehogv1.UIDiscoverAutoDiscoveredResourcesEvent +type UIDiscoverAutoDiscoveredResourcesEvent prehogv1a.UIDiscoverAutoDiscoveredResourcesEvent func (u *UIDiscoverAutoDiscoveredResourcesEvent) CheckAndSetDefaults() error { if u.ResourcesCount < 0 { @@ -283,11 +283,11 @@ func (u *UIDiscoverAutoDiscoveredResourcesEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverAutoDiscoveredResourcesEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ - UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1.UIDiscoverAutoDiscoveredResourcesEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverAutoDiscoveredResourcesEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ + UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1a.UIDiscoverAutoDiscoveredResourcesEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -301,17 +301,17 @@ func (u *UIDiscoverAutoDiscoveredResourcesEvent) Anonymize(a utils.Anonymizer) p // UIDiscoverDatabaseConfigureIAMPolicyEvent is emitted when a user configured IAM for RDS database // and proceeded to the next step. -type UIDiscoverDatabaseConfigureIAMPolicyEvent prehogv1.UIDiscoverDatabaseConfigureIAMPolicyEvent +type UIDiscoverDatabaseConfigureIAMPolicyEvent prehogv1a.UIDiscoverDatabaseConfigureIAMPolicyEvent func (u *UIDiscoverDatabaseConfigureIAMPolicyEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverDatabaseConfigureIAMPolicyEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverDatabaseConfigureIamPolicyEvent{ - UiDiscoverDatabaseConfigureIamPolicyEvent: &prehogv1.UIDiscoverDatabaseConfigureIAMPolicyEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverDatabaseConfigureIAMPolicyEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverDatabaseConfigureIamPolicyEvent{ + UiDiscoverDatabaseConfigureIamPolicyEvent: &prehogv1a.UIDiscoverDatabaseConfigureIAMPolicyEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -324,17 +324,17 @@ func (u *UIDiscoverDatabaseConfigureIAMPolicyEvent) Anonymize(a utils.Anonymizer // UIDiscoverPrincipalsConfigureEvent emitted on "Setup Access" screen when user has updated their principals // and proceeded to the next step. -type UIDiscoverPrincipalsConfigureEvent prehogv1.UIDiscoverPrincipalsConfigureEvent +type UIDiscoverPrincipalsConfigureEvent prehogv1a.UIDiscoverPrincipalsConfigureEvent func (u *UIDiscoverPrincipalsConfigureEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverPrincipalsConfigureEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverPrincipalsConfigureEvent{ - UiDiscoverPrincipalsConfigureEvent: &prehogv1.UIDiscoverPrincipalsConfigureEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverPrincipalsConfigureEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverPrincipalsConfigureEvent{ + UiDiscoverPrincipalsConfigureEvent: &prehogv1a.UIDiscoverPrincipalsConfigureEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -347,17 +347,17 @@ func (u *UIDiscoverPrincipalsConfigureEvent) Anonymize(a utils.Anonymizer) preho // UIDiscoverTestConnectionEvent emitted on the "Test Connection" screen // when the user clicked tested connection to their resource. -type UIDiscoverTestConnectionEvent prehogv1.UIDiscoverTestConnectionEvent +type UIDiscoverTestConnectionEvent prehogv1a.UIDiscoverTestConnectionEvent func (u *UIDiscoverTestConnectionEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverTestConnectionEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverTestConnectionEvent{ - UiDiscoverTestConnectionEvent: &prehogv1.UIDiscoverTestConnectionEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverTestConnectionEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverTestConnectionEvent{ + UiDiscoverTestConnectionEvent: &prehogv1a.UIDiscoverTestConnectionEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, @@ -369,17 +369,17 @@ func (u *UIDiscoverTestConnectionEvent) Anonymize(a utils.Anonymizer) prehogv1.S } // UIDiscoverCompletedEvent is emitted when user completes the Discover wizard. -type UIDiscoverCompletedEvent prehogv1.UIDiscoverCompletedEvent +type UIDiscoverCompletedEvent prehogv1a.UIDiscoverCompletedEvent func (u *UIDiscoverCompletedEvent) CheckAndSetDefaults() error { return trace.Wrap(validateDiscoverBaseEventFields(u.Metadata, u.Resource, u.Status)) } -func (u *UIDiscoverCompletedEvent) Anonymize(a utils.Anonymizer) prehogv1.SubmitEventRequest { - return prehogv1.SubmitEventRequest{ - Event: &prehogv1.SubmitEventRequest_UiDiscoverCompletedEvent{ - UiDiscoverCompletedEvent: &prehogv1.UIDiscoverCompletedEvent{ - Metadata: &prehogv1.DiscoverMetadata{ +func (u *UIDiscoverCompletedEvent) Anonymize(a utils.Anonymizer) prehogv1a.SubmitEventRequest { + return prehogv1a.SubmitEventRequest{ + Event: &prehogv1a.SubmitEventRequest_UiDiscoverCompletedEvent{ + UiDiscoverCompletedEvent: &prehogv1a.UIDiscoverCompletedEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: u.Metadata.Id, UserName: a.AnonymizeString(u.Metadata.UserName), }, diff --git a/lib/usagereporter/teleport/usagereporter.go b/lib/usagereporter/teleport/usagereporter.go index 9feac1c62a53e..a9507d7ef0547 100644 --- a/lib/usagereporter/teleport/usagereporter.go +++ b/lib/usagereporter/teleport/usagereporter.go @@ -30,8 +30,8 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1ac "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/observability/metrics" "github.com/gravitational/teleport/lib/usagereporter" @@ -92,7 +92,7 @@ type GracefulStopper interface { // cluster name, with a very short buffer for batches and no persistency. type StreamingUsageReporter struct { // usageReporter is an actual reporter that batches and sends events - usageReporter *usagereporter.UsageReporter[prehogv1.SubmitEventRequest] + usageReporter *usagereporter.UsageReporter[prehogv1a.SubmitEventRequest] // anonymizer is the anonymizer used for filtered audit events. anonymizer utils.Anonymizer // clusterName is the cluster's name, used for anonymization and as an event @@ -116,7 +116,7 @@ func (t *StreamingUsageReporter) Run(ctx context.Context) { t.usageReporter.Run(ctx) } -type SubmitFunc = usagereporter.SubmitFunc[prehogv1.SubmitEventRequest] +type SubmitFunc = usagereporter.SubmitFunc[prehogv1a.SubmitEventRequest] // TODO(espadolini): change the call in e/lib/prehog/prehog.go:InitPreHogUsageReporting func NewTeleportUsageReporter(log logrus.FieldLogger, clusterName types.ClusterName, submitter SubmitFunc) (*StreamingUsageReporter, error) { @@ -140,7 +140,7 @@ func NewStreamingUsageReporter(log logrus.FieldLogger, clusterName types.Cluster clock := clockwork.NewRealClock() - reporter := usagereporter.NewUsageReporter(&usagereporter.Options[prehogv1.SubmitEventRequest]{ + reporter := usagereporter.NewUsageReporter(&usagereporter.Options[prehogv1a.SubmitEventRequest]{ Log: log, Submit: submitter, MinBatchSize: usageReporterMinBatchSize, @@ -196,15 +196,15 @@ func NewPrehogSubmitter(ctx context.Context, prehogEndpoint string, clientCert * } httpClient.Timeout = 5 * time.Second - client := prehogv1c.NewTeleportReportingServiceClient(httpClient, prehogEndpoint) + client := prehogv1ac.NewTeleportReportingServiceClient(httpClient, prehogEndpoint) - return func(reporter *usagereporter.UsageReporter[prehogv1.SubmitEventRequest], events []*usagereporter.SubmittedEvent[prehogv1.SubmitEventRequest]) ([]*usagereporter.SubmittedEvent[prehogv1.SubmitEventRequest], error) { - evs := make([]*prehogv1.SubmitEventRequest, 0, len(events)) + return func(reporter *usagereporter.UsageReporter[prehogv1a.SubmitEventRequest], events []*usagereporter.SubmittedEvent[prehogv1a.SubmitEventRequest]) ([]*usagereporter.SubmittedEvent[prehogv1a.SubmitEventRequest], error) { + evs := make([]*prehogv1a.SubmitEventRequest, 0, len(events)) for _, e := range events { evs = append(evs, e.Event) } - req := connect.NewRequest(&prehogv1.SubmitEventsRequest{ + req := connect.NewRequest(&prehogv1a.SubmitEventsRequest{ Events: evs, }) if _, err := client.SubmitEvents(ctx, req); err != nil { diff --git a/lib/usagereporter/teleport/usagereporter_test.go b/lib/usagereporter/teleport/usagereporter_test.go index b2d42aa828174..837fb984b6b43 100644 --- a/lib/usagereporter/teleport/usagereporter_test.go +++ b/lib/usagereporter/teleport/usagereporter_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/require" usageeventsv1 "github.com/gravitational/teleport/api/gen/proto/go/usageevents/v1" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" "github.com/gravitational/teleport/lib/utils" ) @@ -37,7 +37,7 @@ func TestConvertUsageEvent(t *testing.T) { identityUsername string isSSOUser bool errCheck require.ErrorAssertionFunc - expected *prehogv1.SubmitEventRequest + expected *prehogv1a.SubmitEventRequest }{ { name: "discover started event", @@ -49,14 +49,14 @@ func TestConvertUsageEvent(t *testing.T) { }}, identityUsername: "myuser", errCheck: require.NoError, - expected: &prehogv1.SubmitEventRequest{Event: &prehogv1.SubmitEventRequest_UiDiscoverStartedEvent{ - UiDiscoverStartedEvent: &prehogv1.UIDiscoverStartedEvent{ - Metadata: &prehogv1.DiscoverMetadata{ + expected: &prehogv1a.SubmitEventRequest{Event: &prehogv1a.SubmitEventRequest_UiDiscoverStartedEvent{ + UiDiscoverStartedEvent: &prehogv1a.UIDiscoverStartedEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: "someid", UserName: expectedAnonymizedUserString, Sso: false, }, - Status: &prehogv1.DiscoverStepStatus{Status: prehogv1.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, + Status: &prehogv1a.DiscoverStepStatus{Status: prehogv1a.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, }, }}, }, @@ -71,15 +71,15 @@ func TestConvertUsageEvent(t *testing.T) { }}, identityUsername: "myuser", errCheck: require.NoError, - expected: &prehogv1.SubmitEventRequest{Event: &prehogv1.SubmitEventRequest_UiDiscoverResourceSelectionEvent{ - UiDiscoverResourceSelectionEvent: &prehogv1.UIDiscoverResourceSelectionEvent{ - Metadata: &prehogv1.DiscoverMetadata{ + expected: &prehogv1a.SubmitEventRequest{Event: &prehogv1a.SubmitEventRequest_UiDiscoverResourceSelectionEvent{ + UiDiscoverResourceSelectionEvent: &prehogv1a.UIDiscoverResourceSelectionEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: "someid", UserName: expectedAnonymizedUserString, Sso: false, }, - Resource: &prehogv1.DiscoverResourceMetadata{Resource: prehogv1.DiscoverResource_DISCOVER_RESOURCE_SERVER}, - Status: &prehogv1.DiscoverStepStatus{Status: prehogv1.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, + Resource: &prehogv1a.DiscoverResourceMetadata{Resource: prehogv1a.DiscoverResource_DISCOVER_RESOURCE_SERVER}, + Status: &prehogv1a.DiscoverStepStatus{Status: prehogv1a.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, }, }}, }, @@ -137,15 +137,15 @@ func TestConvertUsageEvent(t *testing.T) { }}, identityUsername: "myuser", errCheck: require.NoError, - expected: &prehogv1.SubmitEventRequest{Event: &prehogv1.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ - UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1.UIDiscoverAutoDiscoveredResourcesEvent{ - Metadata: &prehogv1.DiscoverMetadata{ + expected: &prehogv1a.SubmitEventRequest{Event: &prehogv1a.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ + UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1a.UIDiscoverAutoDiscoveredResourcesEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: "someid", UserName: expectedAnonymizedUserString, Sso: false, }, - Resource: &prehogv1.DiscoverResourceMetadata{Resource: prehogv1.DiscoverResource_DISCOVER_RESOURCE_SERVER}, - Status: &prehogv1.DiscoverStepStatus{Status: prehogv1.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, + Resource: &prehogv1a.DiscoverResourceMetadata{Resource: prehogv1a.DiscoverResource_DISCOVER_RESOURCE_SERVER}, + Status: &prehogv1a.DiscoverStepStatus{Status: prehogv1a.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, ResourcesCount: 0, }, }}, @@ -162,15 +162,15 @@ func TestConvertUsageEvent(t *testing.T) { }}, identityUsername: "myuser", errCheck: require.NoError, - expected: &prehogv1.SubmitEventRequest{Event: &prehogv1.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ - UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1.UIDiscoverAutoDiscoveredResourcesEvent{ - Metadata: &prehogv1.DiscoverMetadata{ + expected: &prehogv1a.SubmitEventRequest{Event: &prehogv1a.SubmitEventRequest_UiDiscoverAutoDiscoveredResourcesEvent{ + UiDiscoverAutoDiscoveredResourcesEvent: &prehogv1a.UIDiscoverAutoDiscoveredResourcesEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: "someid", UserName: expectedAnonymizedUserString, Sso: false, }, - Resource: &prehogv1.DiscoverResourceMetadata{Resource: prehogv1.DiscoverResource_DISCOVER_RESOURCE_SERVER}, - Status: &prehogv1.DiscoverStepStatus{Status: prehogv1.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, + Resource: &prehogv1a.DiscoverResourceMetadata{Resource: prehogv1a.DiscoverResource_DISCOVER_RESOURCE_SERVER}, + Status: &prehogv1a.DiscoverStepStatus{Status: prehogv1a.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, ResourcesCount: 2, }, }}, @@ -189,7 +189,8 @@ func TestConvertUsageEvent(t *testing.T) { errCheck: func(tt require.TestingT, err error, i ...interface{}) { require.True(tt, trace.IsBadParameter(err), "exepcted trace.IsBadParameter error, got: %v", err) }, - }, { + }, + { name: "discover started event with sso user", event: &usageeventsv1.UsageEventOneOf{Event: &usageeventsv1.UsageEventOneOf_UiDiscoverStartedEvent{ UiDiscoverStartedEvent: &usageeventsv1.UIDiscoverStartedEvent{ @@ -200,14 +201,14 @@ func TestConvertUsageEvent(t *testing.T) { identityUsername: "myuser", isSSOUser: true, errCheck: require.NoError, - expected: &prehogv1.SubmitEventRequest{Event: &prehogv1.SubmitEventRequest_UiDiscoverStartedEvent{ - UiDiscoverStartedEvent: &prehogv1.UIDiscoverStartedEvent{ - Metadata: &prehogv1.DiscoverMetadata{ + expected: &prehogv1a.SubmitEventRequest{Event: &prehogv1a.SubmitEventRequest_UiDiscoverStartedEvent{ + UiDiscoverStartedEvent: &prehogv1a.UIDiscoverStartedEvent{ + Metadata: &prehogv1a.DiscoverMetadata{ Id: "someid", UserName: expectedAnonymizedUserString, Sso: true, }, - Status: &prehogv1.DiscoverStepStatus{Status: prehogv1.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, + Status: &prehogv1a.DiscoverStepStatus{Status: prehogv1a.DiscoverStatus_DISCOVER_STATUS_SUCCESS}, }, }}, }, diff --git a/tool/tbot/anonymous_telemetry.go b/tool/tbot/anonymous_telemetry.go index a64959dc42316..6a00317a883cf 100644 --- a/tool/tbot/anonymous_telemetry.go +++ b/tool/tbot/anonymous_telemetry.go @@ -28,8 +28,8 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" - prehogv1c "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1ac "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha/prehogv1alphaconnect" "github.com/gravitational/teleport/lib/tbot/config" ) @@ -54,7 +54,7 @@ func telemetryEnabled(envGetter envGetter) bool { return false } -func telemetryClient(envGetter envGetter) prehogv1c.TbotReportingServiceClient { +func telemetryClient(envGetter envGetter) prehogv1ac.TbotReportingServiceClient { // Override the default value using TELEPORT_ANONYMOUS_TELEMETRY_ADDRESS // environment variable. // staging: https://reporting-tbot-staging.teleportinfra.dev @@ -63,7 +63,7 @@ func telemetryClient(envGetter envGetter) prehogv1c.TbotReportingServiceClient { endpoint = env } - return prehogv1c.NewTbotReportingServiceClient( + return prehogv1ac.NewTbotReportingServiceClient( http.DefaultClient, endpoint, ) } @@ -73,7 +73,7 @@ func telemetryClient(envGetter envGetter) prehogv1c.TbotReportingServiceClient { // identifiable information. func sendTelemetry( ctx context.Context, - client prehogv1c.TbotReportingServiceClient, + client prehogv1ac.TbotReportingServiceClient, envGetter envGetter, log logrus.FieldLogger, cfg *config.BotConfig, @@ -85,8 +85,8 @@ func sendTelemetry( } log.Infof("Anonymous telemetry is enabled. Find out more about Machine ID's anonymous telemetry at %s", telemetryDocs) - data := &prehogv1.TbotStartEvent{ - RunMode: prehogv1.TbotStartEvent_RUN_MODE_DAEMON, + data := &prehogv1a.TbotStartEvent{ + RunMode: prehogv1a.TbotStartEvent_RUN_MODE_DAEMON, // Default to reporting the "token" join method to account for // scenarios where initial join has onboarding configured but future // starts renew using credentials. @@ -94,7 +94,7 @@ func sendTelemetry( Version: teleport.Version, } if cfg.Oneshot { - data.RunMode = prehogv1.TbotStartEvent_RUN_MODE_ONE_SHOT + data.RunMode = prehogv1a.TbotStartEvent_RUN_MODE_ONE_SHOT } if helper := envGetter(helperEnv); helper != "" { data.Helper = helper @@ -117,10 +117,10 @@ func sendTelemetry( } distinctID := uuid.New().String() - _, err := client.SubmitTbotEvent(ctx, connect.NewRequest(&prehogv1.SubmitTbotEventRequest{ + _, err := client.SubmitTbotEvent(ctx, connect.NewRequest(&prehogv1a.SubmitTbotEventRequest{ DistinctId: distinctID, Timestamp: timestamppb.New(start), - Event: &prehogv1.SubmitTbotEventRequest_Start{Start: data}, + Event: &prehogv1a.SubmitTbotEventRequest_Start{Start: data}, })) if err != nil { return trace.Wrap(err) diff --git a/tool/tbot/anonymous_telemetry_test.go b/tool/tbot/anonymous_telemetry_test.go index d7cc8d040556a..81d3fc1731c55 100644 --- a/tool/tbot/anonymous_telemetry_test.go +++ b/tool/tbot/anonymous_telemetry_test.go @@ -23,21 +23,21 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" - prehogv1 "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" + prehogv1a "github.com/gravitational/teleport/gen/proto/go/prehog/v1alpha" "github.com/gravitational/teleport/lib/tbot/config" "github.com/gravitational/teleport/lib/utils" ) type mockReportingServiceClient struct { - eventRequest *prehogv1.SubmitTbotEventRequest + eventRequest *prehogv1a.SubmitTbotEventRequest } func (mrsc *mockReportingServiceClient) SubmitTbotEvent( ctx context.Context, - req *connect.Request[prehogv1.SubmitTbotEventRequest], -) (*connect.Response[prehogv1.SubmitTbotEventResponse], error) { + req *connect.Request[prehogv1a.SubmitTbotEventRequest], +) (*connect.Response[prehogv1a.SubmitTbotEventResponse], error) { mrsc.eventRequest = req.Msg - return connect.NewResponse(&prehogv1.SubmitTbotEventResponse{}), nil + return connect.NewResponse(&prehogv1a.SubmitTbotEventResponse{}), nil } func mockEnvGetter(data map[string]string) envGetter { @@ -105,9 +105,9 @@ func TestSendTelemetry(t *testing.T) { require.NotNil(t, mockClient.eventRequest) require.NotZero(t, mockClient.eventRequest.Timestamp) require.NotZero(t, mockClient.eventRequest.DistinctId) - require.Equal(t, &prehogv1.SubmitTbotEventRequest_Start{ - Start: &prehogv1.TbotStartEvent{ - RunMode: prehogv1.TbotStartEvent_RUN_MODE_ONE_SHOT, + require.Equal(t, &prehogv1a.SubmitTbotEventRequest_Start{ + Start: &prehogv1a.TbotStartEvent{ + RunMode: prehogv1a.TbotStartEvent_RUN_MODE_ONE_SHOT, JoinType: string(types.JoinMethodGitHub), Version: teleport.Version, From 20040200f076ac42600d8ec91e1250f67a215b52 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Wed, 5 Apr 2023 16:26:01 +0200 Subject: [PATCH 32/32] Merge remote-tracking branch 'origin/master' into espadolini/usagereporter-onprem --- .drone.yml | 30 +- .github/workflows/build-macos.yaml | 34 +- Makefile | 7 +- api/client/proxy/client.go | 8 +- .../go/teleport/devicetrust/v1/device.pb.go | 163 +- .../v1/device_collected_data.pb.go | 89 +- .../devicetrust/v1/device_profile.pb.go | 236 +++ .../devicetrust/v1/device_source.pb.go | 250 +++ .../devicetrust/v1/devicetrust_service.pb.go | 1501 +++++++++++++---- .../v1/devicetrust_service_grpc.pb.go | 81 + .../proto/go/usageevents/v1/usageevents.pb.go | 391 +++-- .../teleport/devicetrust/v1/device.proto | 10 + .../v1/device_collected_data.proto | 23 + .../devicetrust/v1/device_profile.proto | 49 + .../devicetrust/v1/device_source.proto | 47 + .../devicetrust/v1/devicetrust_service.proto | 122 ++ .../teleport/usageevents/v1/usageevents.proto | 22 + api/types/installers/installer.sh.tmpl | 2 + api/utils/grpc/stream/stream.go | 2 +- build.assets/Makefile | 2 +- docs/config.json | 13 +- .../ssh-approval-discord.mdx | 3 +- .../ssh-approval-email.mdx | 2 +- .../ssh-approval-jira.mdx | 2 +- .../ssh-approval-mattermost.mdx | 3 +- .../ssh-approval-pagerduty.mdx | 2 +- .../access-requests/resource-requests.mdx | 3 +- .../access-requests/role-requests.mdx | 2 +- docs/pages/access-controls/faq.mdx | 27 - .../pages/access-controls/getting-started.mdx | 4 +- .../access-controls/guides/dual-authz.mdx | 2 +- .../guides/hardware-key-support.mdx | 2 +- .../access-controls/guides/impersonation.mdx | 2 +- docs/pages/access-controls/guides/locking.mdx | 2 +- .../guides/per-session-mfa.mdx | 5 +- .../access-controls/guides/role-templates.mdx | 2 +- .../pages/access-controls/guides/webauthn.mdx | 3 +- .../access-controls/idps/saml-grafana.mdx | 2 +- .../pages/access-controls/idps/saml-guide.mdx | 4 +- docs/pages/access-controls/introduction.mdx | 6 +- .../access-controls/login-rules/guide.mdx | 2 +- docs/pages/access-controls/sso/adfs.mdx | 2 +- docs/pages/access-controls/sso/azuread.mdx | 2 +- docs/pages/access-controls/sso/github-sso.mdx | 2 +- docs/pages/access-controls/sso/gitlab.mdx | 2 +- .../access-controls/sso/google-workspace.mdx | 2 +- docs/pages/access-controls/sso/oidc.mdx | 2 +- docs/pages/access-controls/sso/okta.mdx | 3 +- docs/pages/access-controls/sso/one-login.mdx | 2 +- docs/pages/api/rbac.mdx | 2 +- .../application-access/cloud-apis/azure.mdx | 2 +- .../cloud-apis/google-cloud.mdx | 2 +- .../application-access/getting-started.mdx | 3 +- .../application-access/guides/dynamodb.mdx | 2 +- docs/pages/application-access/guides/tcp.mdx | 3 +- .../documentation/style-guide.mdx | 9 +- .../pages/database-access/getting-started.mdx | 2 +- .../guides/aws-cassandra-keyspaces.mdx | 2 +- .../database-access/guides/aws-dynamodb.mdx | 2 +- .../database-access/guides/azure-redis.mdx | 3 +- .../guides/azure-sql-server-ad.mdx | 3 +- .../guides/cassandra-self-hosted.mdx | 3 +- .../guides/cockroachdb-self-hosted.mdx | 4 +- .../guides/dynamic-registration.mdx | 2 +- docs/pages/database-access/guides/elastic.mdx | 2 +- .../database-access/guides/mongodb-atlas.mdx | 9 +- .../guides/mongodb-self-hosted.mdx | 2 +- .../database-access/guides/mysql-cloudsql.mdx | 3 +- .../guides/mysql-self-hosted.mdx | 3 +- .../guides/postgres-cloudsql.mdx | 3 +- .../guides/postgres-redshift.mdx | 3 +- .../guides/postgres-self-hosted.mdx | 3 +- .../database-access/guides/rds-proxy.mdx | 3 +- docs/pages/database-access/guides/rds.mdx | 3 +- .../database-access/guides/redis-aws.mdx | 2 +- .../database-access/guides/redis-cluster.mdx | 2 +- docs/pages/database-access/guides/redis.mdx | 2 +- .../database-access/guides/snowflake.mdx | 2 +- .../database-access/guides/sql-server-ad.mdx | 3 +- .../desktop-access/directory-sharing.mdx | 2 +- docs/pages/desktop-access/getting-started.mdx | 3 +- docs/pages/desktop-access/reference/cli.mdx | 2 +- docs/pages/includes/tctl.mdx | 18 +- .../discovery/google-cloud.mdx | 2 +- .../kubernetes-access/getting-started.mdx | 2 +- .../kubernetes-access/manage-access/rbac.mdx | 2 +- .../dynamic-registration.mdx | 2 +- .../register-clusters/iam-joining.mdx | 2 +- .../register-via-deployment.mdx | 2 +- .../register-clusters/static-kubeconfig.mdx | 3 +- docs/pages/machine-id/guides/applications.mdx | 2 +- docs/pages/machine-id/guides/circleci.mdx | 3 +- docs/pages/machine-id/guides/databases.mdx | 2 +- .../guides/github-actions-kubernetes.mdx | 2 +- .../machine-id/guides/github-actions.mdx | 3 +- docs/pages/machine-id/guides/gitlab.mdx | 3 +- docs/pages/machine-id/guides/jenkins.mdx | 2 +- docs/pages/machine-id/guides/kubernetes.mdx | 2 +- docs/pages/management/admin/adding-nodes.mdx | 5 +- docs/pages/management/admin/labels.mdx | 3 +- .../management/admin/troubleshooting.mdx | 2 +- docs/pages/management/admin/users.mdx | 2 +- .../export-audit-events/datadog.mdx | 2 +- .../export-audit-events/fluentd.mdx | 3 +- .../management/export-audit-events/splunk.mdx | 2 +- .../guides/joining-nodes-aws-ec2.mdx | 2 +- .../guides/joining-nodes-aws-iam.mdx | 4 +- .../management/guides/joining-nodes-azure.mdx | 4 +- .../management/guides/terraform-provider.mdx | 2 +- .../management/operations/ca-rotation.mdx | 2 +- .../security/reduce-blast-radius.mdx | 4 +- docs/pages/reference/resources.mdx | 2 +- docs/pages/server-access/getting-started.mdx | 3 +- docs/pages/server-access/guides/auditd.mdx | 4 +- .../server-access/guides/azure-discovery.mdx | 3 +- .../guides/bpf-session-recording.mdx | 2 +- .../server-access/guides/ec2-discovery.mdx | 3 +- .../guides/host-user-creation.mdx | 2 +- docs/pages/server-access/guides/openssh.mdx | 3 +- .../guides/restricted-session.mdx | 2 +- docs/pages/server-access/rbac.mdx | 9 +- e | 2 +- examples/bench/example.go | 12 +- .../templates/auth/predeploy_config.yaml | 2 +- .../templates/auth/predeploy_job.yaml | 2 +- .../templates/proxy/predeploy_config.yaml | 2 +- .../templates/proxy/predeploy_job.yaml | 2 +- .../templates/delete_hook.yaml | 10 +- .../teleport-kube-agent/templates/hook.yaml | 10 +- gen/proto/go/prehog/v1alpha/connect.pb.go | 194 ++- gen/proto/js/prehog/v1alpha/connect_pb.d.ts | 4 + gen/proto/js/prehog/v1alpha/connect_pb.js | 32 +- go.mod | 6 +- go.sum | 13 +- integration/integration_test.go | 300 +++- integration/kube_integration_test.go | 15 +- lib/agentless/agentless.go | 48 +- lib/benchmark/benchmark.go | 87 +- lib/benchmark/benchmark_suites.go | 250 +++ lib/benchmark/linear.go | 1 - lib/benchmark/linear_test.go | 4 - lib/client/api.go | 365 ++-- lib/client/client.go | 94 +- lib/client/cluster_client.go | 365 ++++ lib/proxy/router.go | 18 +- lib/proxy/router_test.go | 2 +- lib/service/service.go | 40 +- lib/srv/db/sqlserver/connect.go | 89 +- lib/srv/db/sqlserver/connect_test.go | 18 + .../discovery/fetchers/db/aws_rds_proxy.go | 8 - lib/srv/regular/proxy.go | 13 +- lib/srv/transport/transportv1/transport.go | 83 +- .../transport/transportv1/transport_test.go | 73 +- lib/web/apiserver.go | 6 - lib/web/apiserver_test.go | 4 + lib/web/terminal.go | 20 +- proto/prehog/v1alpha/connect.proto | 2 + rfd/0000-rfds.md | 2 +- tool/tsh/tsh.go | 59 +- web/packages/design/src/Icon/Icon.jsx | 4 +- .../components/Highlight/Highlight.story.tsx | 79 + .../shared/components/Highlight/Highlight.tsx | 50 + .../components/Highlight}/index.ts | 2 +- web/packages/shared/hooks/useAsync.ts | 20 + web/packages/shared/package.json | 1 + .../CreateDatabase/CreateDatabase.story.tsx | 6 +- .../CreateDatabase/CreateDatabase.tsx | 6 +- .../Database/MutualTls/MutualTls.story.tsx | 6 +- .../Discover/Database/MutualTls/MutualTls.tsx | 6 +- .../SetupAccess/SetupAccess.story.tsx | 13 +- .../Database/SetupAccess/SetupAccess.tsx | 14 +- .../TestConnection/TestConnection.story.tsx | 4 +- .../TestConnection/TestConnection.tsx | 2 +- .../teleport/src/Discover/Database/index.tsx | 2 +- .../teleport/src/Discover/Discover.test.tsx | 5 +- .../SelectResource/SelectResource.tsx | 14 +- .../SelectResource.story.test.tsx.snap | 528 +++++- .../src/Discover/SelectResource/databases.tsx | 173 +- .../src/Discover/SelectResource/icons.tsx | 2 + .../src/Discover/SelectResource/resources.tsx | 29 +- .../src/Discover/SelectResource/types.ts | 31 +- .../src/Discover/Shared/ResourceKind.ts | 73 - .../src/Discover/useDiscover.test.tsx | 2 - .../teleport/src/Discover/useDiscover.tsx | 31 +- .../teleport/src/services/userEvent/types.ts | 22 + .../src/services/config/appConfigSchema.ts | 15 +- .../src/services/tshd/createClient.ts | 30 +- .../src/services/tshd/fixtures/mocks.ts | 8 +- .../src/services/tshd/mapUsageEvent.ts | 3 +- .../teleterm/src/services/tshd/types.ts | 22 +- .../ClusterResources/Databases/Databases.tsx | 2 +- .../Databases/useDatabases.ts | 9 +- .../ClusterResources/Kubes/useKubes.ts | 9 +- .../ClusterResources/Servers/useServers.ts | 8 +- .../useServerSideResources.ts | 11 +- .../src/ui/DocumentCluster/clusterContext.tsx | 8 +- .../ui/DocumentGateway/useDocumentGateway.ts | 1 + .../useDocumentTerminal.test.tsx | 1 + .../DocumentTerminal/useDocumentTerminal.ts | 8 +- .../ui/Documents/KeyboardShortcutsPanel.tsx | 4 + .../teleterm/src/ui/QuickInput/QuickInput.tsx | 12 +- .../src/ui/QuickInput/useQuickInput.ts | 16 +- .../teleterm/src/ui/Search/SearchBar.test.tsx | 103 ++ .../teleterm/src/ui/Search/SearchBar.tsx | 172 ++ .../teleterm/src/ui/Search/SearchContext.tsx | 143 ++ .../teleterm/src/ui/Search/actions.tsx | 167 ++ web/packages/teleterm/src/ui/Search/index.ts | 17 + .../ui/Search/pickers/ActionPicker.story.tsx | 267 +++ .../src/ui/Search/pickers/ActionPicker.tsx | 501 ++++++ .../src/ui/Search/pickers/ParameterPicker.tsx | 88 + .../src/ui/Search/pickers/ResultList.tsx | 213 +++ .../src/ui/Search/pickers/pickers.tsx | 44 + .../ui/Search/pickers/useSearchAttempts.ts | 113 ++ .../teleterm/src/ui/Search/searchResult.ts | 114 ++ .../src/ui/Search/searchResultTestHelpers.ts | 63 + .../teleterm/src/ui/Search/useSearch.test.ts | 66 + .../teleterm/src/ui/Search/useSearch.ts | 312 ++++ web/packages/teleterm/src/ui/TabHost/index.ts | 1 + .../src/ui/TabHost/useNewTabOpener.ts | 30 +- .../src/ui/TabHost/useTabShortcuts.test.tsx | 2 + .../src/ui/TabHost/useTabShortcuts.ts | 11 +- .../ClusterSelector/ClusterSelector.tsx | 8 +- .../ui/TopBar/Connections/useConnections.ts | 3 +- .../teleterm/src/ui/TopBar/MoreOptions.tsx | 240 +++ .../TopBar/NavigationMenu/NavigationItem.tsx | 52 - .../TopBar/NavigationMenu/NavigationMenu.tsx | 154 -- .../teleterm/src/ui/TopBar/TopBar.tsx | 30 +- web/packages/teleterm/src/ui/appContext.ts | 7 +- .../teleterm/src/ui/commandLauncher.ts | 30 +- .../ui/services/clusters/clustersService.ts | 21 +- .../connectionTrackerService.test.ts | 3 + .../connectionTrackerService.ts | 8 +- .../trackedConnectionOperationsFactory.ts | 20 +- .../keyboardShortcutsService.ts | 1 + .../src/ui/services/quickInput/suggesters.ts | 2 + .../resources/resourcesService.test.ts | 1 + .../ui/services/resources/resourcesService.ts | 86 +- .../src/ui/services/usage/usageService.ts | 5 +- .../documentsService/documentsService.test.ts | 3 + .../documentsService/documentsService.ts | 30 +- .../documentsService/types.test.ts | 1 + .../documentsService/types.ts | 11 + .../workspacesService/workspacesService.ts | 8 + web/packages/teleterm/src/ui/types.ts | 2 + web/packages/teleterm/src/ui/uri.ts | 7 + yarn.lock | 5 + 246 files changed, 8148 insertions(+), 1963 deletions(-) create mode 100644 api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go create mode 100644 api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go create mode 100644 api/proto/teleport/devicetrust/v1/device_profile.proto create mode 100644 api/proto/teleport/devicetrust/v1/device_source.proto delete mode 100644 docs/pages/access-controls/faq.mdx create mode 100644 lib/benchmark/benchmark_suites.go create mode 100644 lib/client/cluster_client.go create mode 100644 web/packages/shared/components/Highlight/Highlight.story.tsx create mode 100644 web/packages/shared/components/Highlight/Highlight.tsx rename web/packages/{teleterm/src/ui/TopBar/NavigationMenu => shared/components/Highlight}/index.ts (92%) create mode 100644 web/packages/teleterm/src/ui/Search/SearchBar.test.tsx create mode 100644 web/packages/teleterm/src/ui/Search/SearchBar.tsx create mode 100644 web/packages/teleterm/src/ui/Search/SearchContext.tsx create mode 100644 web/packages/teleterm/src/ui/Search/actions.tsx create mode 100644 web/packages/teleterm/src/ui/Search/index.ts create mode 100644 web/packages/teleterm/src/ui/Search/pickers/ActionPicker.story.tsx create mode 100644 web/packages/teleterm/src/ui/Search/pickers/ActionPicker.tsx create mode 100644 web/packages/teleterm/src/ui/Search/pickers/ParameterPicker.tsx create mode 100644 web/packages/teleterm/src/ui/Search/pickers/ResultList.tsx create mode 100644 web/packages/teleterm/src/ui/Search/pickers/pickers.tsx create mode 100644 web/packages/teleterm/src/ui/Search/pickers/useSearchAttempts.ts create mode 100644 web/packages/teleterm/src/ui/Search/searchResult.ts create mode 100644 web/packages/teleterm/src/ui/Search/searchResultTestHelpers.ts create mode 100644 web/packages/teleterm/src/ui/Search/useSearch.test.ts create mode 100644 web/packages/teleterm/src/ui/Search/useSearch.ts create mode 100644 web/packages/teleterm/src/ui/TopBar/MoreOptions.tsx delete mode 100644 web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationItem.tsx delete mode 100644 web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx diff --git a/.drone.yml b/.drone.yml index 0758476a53aad..726d0cf74e2d6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -39,7 +39,7 @@ name: push-build-linux-amd64 environment: BUILDBOX_VERSION: teleport13 GID: "1000" - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 UID: "1000" trigger: event: @@ -161,7 +161,7 @@ name: push-build-linux-386 environment: BUILDBOX_VERSION: teleport13 GID: "1000" - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 UID: "1000" trigger: event: @@ -281,7 +281,7 @@ name: push-build-linux-amd64-fips environment: BUILDBOX_VERSION: teleport13 GID: "1000" - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 UID: "1000" trigger: event: @@ -405,7 +405,7 @@ name: push-build-windows-amd64 environment: BUILDBOX_VERSION: teleport13 GID: "1000" - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 UID: "1000" trigger: event: @@ -580,7 +580,7 @@ steps: - tar -C /tmp/build-$DRONE_BUILD_NUMBER-$DRONE_BUILD_CREATED/toolchains -xzf $RUNTIME.darwin-amd64.tar.gz - rm -rf $RUNTIME.darwin-amd64.tar.gz environment: - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 - name: Install Rust Toolchain commands: - set -u @@ -1204,7 +1204,7 @@ name: push-build-linux-arm environment: BUILDBOX_VERSION: teleport13 GID: "1000" - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 UID: "1000" trigger: event: @@ -1741,7 +1741,7 @@ type: kubernetes name: build-linux-amd64-centos7 environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -1950,7 +1950,7 @@ type: kubernetes name: build-linux-amd64-centos7-fips environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -2158,7 +2158,7 @@ type: kubernetes name: build-linux-amd64 environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -2373,7 +2373,7 @@ type: kubernetes name: build-linux-amd64-fips environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -3673,7 +3673,7 @@ type: kubernetes name: build-linux-386 environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -4487,7 +4487,7 @@ steps: - tar -C /tmp/build-$DRONE_BUILD_NUMBER-$DRONE_BUILD_CREATED/toolchains -xzf $RUNTIME.darwin-amd64.tar.gz - rm -rf $RUNTIME.darwin-amd64.tar.gz environment: - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 - name: Install Rust Toolchain commands: - set -u @@ -5079,7 +5079,7 @@ type: kubernetes name: build-linux-arm environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -6408,7 +6408,7 @@ type: kubernetes name: build-windows-amd64 environment: BUILDBOX_VERSION: teleport13 - RUNTIME: go1.20.2 + RUNTIME: go1.20.3 trigger: event: include: @@ -21145,6 +21145,6 @@ image_pull_secrets: - DOCKERHUB_CREDENTIALS --- kind: signature -hmac: d5d88db9889331c6bff21f5184b3f54bed9eb6dac632c75de0b928879f1af5e3 +hmac: 3a2e1dfd7cd5045cb27658233858cb21a861d6595ef5c807321e0d8af0f65bdb ... diff --git a/.github/workflows/build-macos.yaml b/.github/workflows/build-macos.yaml index f6ca5f1d0c1aa..aa7a0813f04a5 100644 --- a/.github/workflows/build-macos.yaml +++ b/.github/workflows/build-macos.yaml @@ -40,15 +40,37 @@ jobs: - name: Checkout Teleport uses: actions/checkout@v3 - - name: Get Go version - id: go-version - shell: bash - run: echo "go-version=$(make --no-print-directory print-go-version | tr -d '\n')" >> $GITHUB_OUTPUT + - name: Determine Toolchain Versions and cache paths + run: | + echo NODE_VERSION=$(make -C build.assets print-node-version) >> $GITHUB_ENV + echo GOLANG_VERSION=$(make -C build.assets print-go-version | sed 's/^go//') >> $GITHUB_ENV + echo RUST_VERSION=$(make -C build.assets print-rust-version) >> $GITHUB_ENV + echo PKG_CONFIG_PATH="$(build.assets/build-fido2-macos.sh pkg_config_path)" >> $GITHUB_ENV - - name: Setup Go + - name: Print versions + run: | + echo "make: $(make --version)" + echo "node: ${NODE_VERSION}" + echo "go: ${GOLANG_VERSION}" + echo "rust: ${RUST_VERSION}" + + - name: Install Node Toolchain + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup yarn + run: | + corepack enable yarn + + - name: Install Go Toolchain uses: actions/setup-go@v3 with: - go-version: ${{ steps.go-version.outputs.go-version }} + go-version: ${{ env.GOLANG_VERSION }} + + - name: Configure Rust Toolchain + run: | + rustup override set ${{ env.RUST_VERSION }} - name: Build run: make binaries diff --git a/Makefile b/Makefile index 89f118161b06d..37171562187cf 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,11 @@ endif CHECK_CARGO := $(shell cargo --version 2>/dev/null) CHECK_RUST := $(shell rustc --version 2>/dev/null) +# Have cargo use sparse crates.io protocol: +# https://blog.rust-lang.org/2023/03/09/Rust-1.68.0.html +# TODO: Delete when it becomes default in Rust 1.70.0 +export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse + with_rdpclient := no RDPCLIENT_MESSAGE := without-Windows-RDP-client @@ -207,7 +212,7 @@ ifeq ("$(ARCH)","arm64") CGOFLAG += CC=aarch64-linux-gnu-gcc endif else ifeq ("$(ARCH)","arm") -CGOFLAG = CGO_ENABLED=1 +CGOFLAG = CGO_ENABLED=1 # ARM builds need to specify the correct C compiler ifeq ($(IS_NATIVE_BUILD),"no") diff --git a/api/client/proxy/client.go b/api/client/proxy/client.go index a8efd7fe880d8..b0299c8fa97bf 100644 --- a/api/client/proxy/client.go +++ b/api/client/proxy/client.go @@ -212,8 +212,10 @@ func NewClient(ctx context.Context, cfg ClientConfig) (*Client, error) { } clt, sshErr := newSSHClient(ctx, &cfg) - if sshErr == nil { - return clt, nil + // Only aggregate errors if there was an issue dialing the grpc server so + // that helpers like trace.IsAccessDenied will still work. + if grpcErr == nil { + return clt, trace.Wrap(sshErr) } return nil, trace.NewAggregate(grpcErr, sshErr) @@ -241,7 +243,7 @@ func (c *clusterName) set(name string) { // clusterCredentials is a [credentials.TransportCredentials] implementation // that obtains the name of the cluster being connected to from the certificate // presented by the server. This allows the client to determine the cluster name when -// connecting via using jump hosts. +// connecting via jump hosts. type clusterCredentials struct { credentials.TransportCredentials clusterName *clusterName diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go index c6e79c7835ab2..36fef2d25f046 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go @@ -135,6 +135,12 @@ type Device struct { // Only present in certain read modes. // Transient. CollectedData []*DeviceCollectedData `protobuf:"bytes,10,rep,name=collected_data,json=collectedData,proto3" json:"collected_data,omitempty"` + // Source of the device. + // Devices managed directly via Teleport (`tctl`, Web UI, etc) have no + // assigned source. + Source *DeviceSource `protobuf:"bytes,11,opt,name=source,proto3" json:"source,omitempty"` + // Device information acquired from an external source. + Profile *DeviceProfile `protobuf:"bytes,12,opt,name=profile,proto3" json:"profile,omitempty"` } func (x *Device) Reset() { @@ -239,6 +245,20 @@ func (x *Device) GetCollectedData() []*DeviceCollectedData { return nil } +func (x *Device) GetSource() *DeviceSource { + if x != nil { + return x.Source + } + return nil +} + +func (x *Device) GetProfile() *DeviceProfile { + if x != nil { + return x.Profile + } + return nil +} + // DeviceCredential represents the current enrolled public key of a device. type DeviceCredential struct { state protoimpl.MessageState @@ -312,65 +332,78 @@ var file_teleport_devicetrust_v1_device_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x31, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, - 0x31, 0x2f, 0x6f, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, - 0xcb, 0x04, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, - 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, - 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, + 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2b, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x73, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcc, 0x05, 0x0a, 0x06, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x73, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x53, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x6f, 0x73, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x61, 0x67, 0x12, 0x3b, 0x0a, + 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x65, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, + 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0b, 0x65, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x0d, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, + 0x72, 0x6f, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x65, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x49, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x53, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x6f, - 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, - 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, - 0x61, 0x67, 0x12, 0x3b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x0c, - 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0b, - 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x0d, 0x65, - 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x0c, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x49, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0a, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x53, 0x0a, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0x48, 0x0a, - 0x10, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, - 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x2a, 0x84, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, - 0x0a, 0x20, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x45, - 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4e, 0x4f, 0x54, - 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x44, - 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x42, 0x4c, - 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x12, 0x53, 0x0a, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x40, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x48, 0x0a, 0x10, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x24, 0x0a, + 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x44, 0x65, 0x72, 0x2a, 0x84, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, + 0x72, 0x6f, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x45, + 0x56, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, + 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4e, 0x52, + 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x56, 0x49, 0x43, + 0x45, 0x5f, 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x45, 0x4e, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x42, 0x4c, 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -395,6 +428,8 @@ var file_teleport_devicetrust_v1_device_proto_goTypes = []interface{}{ (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp (*DeviceEnrollToken)(nil), // 5: teleport.devicetrust.v1.DeviceEnrollToken (*DeviceCollectedData)(nil), // 6: teleport.devicetrust.v1.DeviceCollectedData + (*DeviceSource)(nil), // 7: teleport.devicetrust.v1.DeviceSource + (*DeviceProfile)(nil), // 8: teleport.devicetrust.v1.DeviceProfile } var file_teleport_devicetrust_v1_device_proto_depIdxs = []int32{ 3, // 0: teleport.devicetrust.v1.Device.os_type:type_name -> teleport.devicetrust.v1.OSType @@ -404,11 +439,13 @@ var file_teleport_devicetrust_v1_device_proto_depIdxs = []int32{ 0, // 4: teleport.devicetrust.v1.Device.enroll_status:type_name -> teleport.devicetrust.v1.DeviceEnrollStatus 2, // 5: teleport.devicetrust.v1.Device.credential:type_name -> teleport.devicetrust.v1.DeviceCredential 6, // 6: teleport.devicetrust.v1.Device.collected_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 7, // 7: teleport.devicetrust.v1.Device.source:type_name -> teleport.devicetrust.v1.DeviceSource + 8, // 8: teleport.devicetrust.v1.Device.profile:type_name -> teleport.devicetrust.v1.DeviceProfile + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_teleport_devicetrust_v1_device_proto_init() } @@ -418,6 +455,8 @@ func file_teleport_devicetrust_v1_device_proto_init() { } file_teleport_devicetrust_v1_device_collected_data_proto_init() file_teleport_devicetrust_v1_device_enroll_token_proto_init() + file_teleport_devicetrust_v1_device_profile_proto_init() + file_teleport_devicetrust_v1_device_source_proto_init() file_teleport_devicetrust_v1_os_type_proto_init() if !protoimpl.UnsafeEnabled { file_teleport_devicetrust_v1_device_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go index e11c445f3fc3c..348b67c844781 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go @@ -56,6 +56,23 @@ type DeviceCollectedData struct { // Device serial number. // Required for macOS devices. SerialNumber string `protobuf:"bytes,4,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty"` + // Non-descriptive model identifier. + // Example: "MacBookPro9,2". + ModelIdentifier string `protobuf:"bytes,5,opt,name=model_identifier,json=modelIdentifier,proto3" json:"model_identifier,omitempty"` + // OS version number, without the leading 'v'. + // Example: "13.2.1". + OsVersion string `protobuf:"bytes,6,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` + // OS build identifier. Augments the os_version. + // Example: "22D68". + OsBuild string `protobuf:"bytes,7,opt,name=os_build,json=osBuild,proto3" json:"os_build,omitempty"` + // OS username (distinct from the Teleport user). + OsUsername string `protobuf:"bytes,8,opt,name=os_username,json=osUsername,proto3" json:"os_username,omitempty"` + // Jamf binary version, without the leading 'v'. + // Example: "9.27" or "10.44.1-t1677509507". + JamfBinaryVersion string `protobuf:"bytes,9,opt,name=jamf_binary_version,json=jamfBinaryVersion,proto3" json:"jamf_binary_version,omitempty"` + // Unmodified output of `/usr/bin/profiles status -type enrollment`. + // Used to verify the presence of an enrollment profile. + MacosEnrollmentProfiles string `protobuf:"bytes,10,opt,name=macos_enrollment_profiles,json=macosEnrollmentProfiles,proto3" json:"macos_enrollment_profiles,omitempty"` } func (x *DeviceCollectedData) Reset() { @@ -118,6 +135,48 @@ func (x *DeviceCollectedData) GetSerialNumber() string { return "" } +func (x *DeviceCollectedData) GetModelIdentifier() string { + if x != nil { + return x.ModelIdentifier + } + return "" +} + +func (x *DeviceCollectedData) GetOsVersion() string { + if x != nil { + return x.OsVersion + } + return "" +} + +func (x *DeviceCollectedData) GetOsBuild() string { + if x != nil { + return x.OsBuild + } + return "" +} + +func (x *DeviceCollectedData) GetOsUsername() string { + if x != nil { + return x.OsUsername + } + return "" +} + +func (x *DeviceCollectedData) GetJamfBinaryVersion() string { + if x != nil { + return x.JamfBinaryVersion + } + return "" +} + +func (x *DeviceCollectedData) GetMacosEnrollmentProfiles() string { + if x != nil { + return x.MacosEnrollmentProfiles + } + return "" +} + var File_teleport_devicetrust_v1_device_collected_data_proto protoreflect.FileDescriptor var file_teleport_devicetrust_v1_device_collected_data_proto_rawDesc = []byte{ @@ -130,7 +189,7 @@ var file_teleport_devicetrust_v1_device_collected_data_proto_rawDesc = []byte{ 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf0, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe2, 0x03, 0x0a, 0x13, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, @@ -145,12 +204,28 @@ var file_teleport_devicetrust_v1_device_collected_data_proto_rawDesc = []byte{ 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x53, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x6f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x4c, 0x5a, 0x4a, 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, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x73, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x73, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x6f, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x73, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x2e, 0x0a, 0x13, 0x6a, 0x61, 0x6d, 0x66, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6a, 0x61, + 0x6d, 0x66, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x3a, 0x0a, 0x19, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x5f, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x17, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, + 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go new file mode 100644 index 0000000000000..af1de8d989368 --- /dev/null +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc (unknown) +// source: teleport/devicetrust/v1/device_profile.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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) +) + +// Device profile information acquired from an external source. +// If present, it's used to further validate collected data. +type DeviceProfile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Latest profile update time. + // System managed. + UpdateTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` + // Non-descriptive model identifier. + // Example: "MacBookPro9,2". + ModelIdentifier string `protobuf:"bytes,2,opt,name=model_identifier,json=modelIdentifier,proto3" json:"model_identifier,omitempty"` + // OS version number, without the leading 'v'. + // See the Device's os_type for the general OS category. + // Example: "13.2.1". + OsVersion string `protobuf:"bytes,3,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` + // OS build identifier. Augments the os_version. + // Example: "22D68". + OsBuild string `protobuf:"bytes,4,opt,name=os_build,json=osBuild,proto3" json:"os_build,omitempty"` + // Known OS users (distinct from the Teleport user). + OsUsernames []string `protobuf:"bytes,5,rep,name=os_usernames,json=osUsernames,proto3" json:"os_usernames,omitempty"` + // Jamf binary version, without the leading 'v'. + // Example: "9.27" or "10.44.1-t1677509507". + JamfBinaryVersion string `protobuf:"bytes,6,opt,name=jamf_binary_version,json=jamfBinaryVersion,proto3" json:"jamf_binary_version,omitempty"` +} + +func (x *DeviceProfile) Reset() { + *x = DeviceProfile{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_device_profile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeviceProfile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceProfile) ProtoMessage() {} + +func (x *DeviceProfile) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_device_profile_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 DeviceProfile.ProtoReflect.Descriptor instead. +func (*DeviceProfile) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_device_profile_proto_rawDescGZIP(), []int{0} +} + +func (x *DeviceProfile) GetUpdateTime() *timestamppb.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *DeviceProfile) GetModelIdentifier() string { + if x != nil { + return x.ModelIdentifier + } + return "" +} + +func (x *DeviceProfile) GetOsVersion() string { + if x != nil { + return x.OsVersion + } + return "" +} + +func (x *DeviceProfile) GetOsBuild() string { + if x != nil { + return x.OsBuild + } + return "" +} + +func (x *DeviceProfile) GetOsUsernames() []string { + if x != nil { + return x.OsUsernames + } + return nil +} + +func (x *DeviceProfile) GetJamfBinaryVersion() string { + if x != nil { + return x.JamfBinaryVersion + } + return "" +} + +var File_teleport_devicetrust_v1_device_profile_proto protoreflect.FileDescriptor + +var file_teleport_devicetrust_v1_device_profile_proto_rawDesc = []byte{ + 0x0a, 0x2c, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 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, 0x84, 0x02, 0x0a, 0x0d, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x73, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x73, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, + 0x6f, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x73, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, + 0x2e, 0x0a, 0x13, 0x6a, 0x61, 0x6d, 0x66, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6a, 0x61, + 0x6d, 0x66, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, + 0x4c, 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_teleport_devicetrust_v1_device_profile_proto_rawDescOnce sync.Once + file_teleport_devicetrust_v1_device_profile_proto_rawDescData = file_teleport_devicetrust_v1_device_profile_proto_rawDesc +) + +func file_teleport_devicetrust_v1_device_profile_proto_rawDescGZIP() []byte { + file_teleport_devicetrust_v1_device_profile_proto_rawDescOnce.Do(func() { + file_teleport_devicetrust_v1_device_profile_proto_rawDescData = protoimpl.X.CompressGZIP(file_teleport_devicetrust_v1_device_profile_proto_rawDescData) + }) + return file_teleport_devicetrust_v1_device_profile_proto_rawDescData +} + +var file_teleport_devicetrust_v1_device_profile_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_teleport_devicetrust_v1_device_profile_proto_goTypes = []interface{}{ + (*DeviceProfile)(nil), // 0: teleport.devicetrust.v1.DeviceProfile + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp +} +var file_teleport_devicetrust_v1_device_profile_proto_depIdxs = []int32{ + 1, // 0: teleport.devicetrust.v1.DeviceProfile.update_time:type_name -> google.protobuf.Timestamp + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_teleport_devicetrust_v1_device_profile_proto_init() } +func file_teleport_devicetrust_v1_device_profile_proto_init() { + if File_teleport_devicetrust_v1_device_profile_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_teleport_devicetrust_v1_device_profile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeviceProfile); 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_devicetrust_v1_device_profile_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_teleport_devicetrust_v1_device_profile_proto_goTypes, + DependencyIndexes: file_teleport_devicetrust_v1_device_profile_proto_depIdxs, + MessageInfos: file_teleport_devicetrust_v1_device_profile_proto_msgTypes, + }.Build() + File_teleport_devicetrust_v1_device_profile_proto = out.File + file_teleport_devicetrust_v1_device_profile_proto_rawDesc = nil + file_teleport_devicetrust_v1_device_profile_proto_goTypes = nil + file_teleport_devicetrust_v1_device_profile_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go new file mode 100644 index 0000000000000..518fb77c6cb8c --- /dev/null +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go @@ -0,0 +1,250 @@ +// 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/devicetrust/v1/device_source.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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) +) + +// Origin of a device. +type DeviceOrigin int32 + +const ( + // Unspecified or absent origin. + DeviceOrigin_DEVICE_ORIGIN_UNSPECIFIED DeviceOrigin = 0 + // Devices originated from direct API usage. + DeviceOrigin_DEVICE_ORIGIN_API DeviceOrigin = 1 + // Devices originated from Jamf sync. + DeviceOrigin_DEVICE_ORIGIN_JAMF DeviceOrigin = 2 + // Source originated from Microsoft Intune sync. + DeviceOrigin_DEVICE_ORIGIN_INTUNE DeviceOrigin = 3 +) + +// Enum value maps for DeviceOrigin. +var ( + DeviceOrigin_name = map[int32]string{ + 0: "DEVICE_ORIGIN_UNSPECIFIED", + 1: "DEVICE_ORIGIN_API", + 2: "DEVICE_ORIGIN_JAMF", + 3: "DEVICE_ORIGIN_INTUNE", + } + DeviceOrigin_value = map[string]int32{ + "DEVICE_ORIGIN_UNSPECIFIED": 0, + "DEVICE_ORIGIN_API": 1, + "DEVICE_ORIGIN_JAMF": 2, + "DEVICE_ORIGIN_INTUNE": 3, + } +) + +func (x DeviceOrigin) Enum() *DeviceOrigin { + p := new(DeviceOrigin) + *p = x + return p +} + +func (x DeviceOrigin) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeviceOrigin) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_devicetrust_v1_device_source_proto_enumTypes[0].Descriptor() +} + +func (DeviceOrigin) Type() protoreflect.EnumType { + return &file_teleport_devicetrust_v1_device_source_proto_enumTypes[0] +} + +func (x DeviceOrigin) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeviceOrigin.Descriptor instead. +func (DeviceOrigin) EnumDescriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_device_source_proto_rawDescGZIP(), []int{0} +} + +// Source of device, for devices that are managed by external systems +// (for example, MDMs). +type DeviceSource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name of the source. + // Matches the name of the corresponding MDM service, if applicable. + // Readonly. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Origin of the source. + // Readonly. + Origin DeviceOrigin `protobuf:"varint,2,opt,name=origin,proto3,enum=teleport.devicetrust.v1.DeviceOrigin" json:"origin,omitempty"` +} + +func (x *DeviceSource) Reset() { + *x = DeviceSource{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_device_source_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeviceSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceSource) ProtoMessage() {} + +func (x *DeviceSource) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_device_source_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 DeviceSource.ProtoReflect.Descriptor instead. +func (*DeviceSource) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_device_source_proto_rawDescGZIP(), []int{0} +} + +func (x *DeviceSource) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *DeviceSource) GetOrigin() DeviceOrigin { + if x != nil { + return x.Origin + } + return DeviceOrigin_DEVICE_ORIGIN_UNSPECIFIED +} + +var File_teleport_devicetrust_v1_device_source_proto protoreflect.FileDescriptor + +var file_teleport_devicetrust_v1_device_source_proto_rawDesc = []byte{ + 0x0a, 0x2b, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x22, 0x61, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x2a, 0x76, 0x0a, 0x0c, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x56, + 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x56, 0x49, + 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x5f, 0x41, 0x50, 0x49, 0x10, 0x01, 0x12, + 0x16, 0x0a, 0x12, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, + 0x5f, 0x4a, 0x41, 0x4d, 0x46, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x56, 0x49, 0x43, + 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x5f, 0x49, 0x4e, 0x54, 0x55, 0x4e, 0x45, 0x10, + 0x03, 0x42, 0x4c, 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_teleport_devicetrust_v1_device_source_proto_rawDescOnce sync.Once + file_teleport_devicetrust_v1_device_source_proto_rawDescData = file_teleport_devicetrust_v1_device_source_proto_rawDesc +) + +func file_teleport_devicetrust_v1_device_source_proto_rawDescGZIP() []byte { + file_teleport_devicetrust_v1_device_source_proto_rawDescOnce.Do(func() { + file_teleport_devicetrust_v1_device_source_proto_rawDescData = protoimpl.X.CompressGZIP(file_teleport_devicetrust_v1_device_source_proto_rawDescData) + }) + return file_teleport_devicetrust_v1_device_source_proto_rawDescData +} + +var file_teleport_devicetrust_v1_device_source_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_teleport_devicetrust_v1_device_source_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_teleport_devicetrust_v1_device_source_proto_goTypes = []interface{}{ + (DeviceOrigin)(0), // 0: teleport.devicetrust.v1.DeviceOrigin + (*DeviceSource)(nil), // 1: teleport.devicetrust.v1.DeviceSource +} +var file_teleport_devicetrust_v1_device_source_proto_depIdxs = []int32{ + 0, // 0: teleport.devicetrust.v1.DeviceSource.origin:type_name -> teleport.devicetrust.v1.DeviceOrigin + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_teleport_devicetrust_v1_device_source_proto_init() } +func file_teleport_devicetrust_v1_device_source_proto_init() { + if File_teleport_devicetrust_v1_device_source_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_teleport_devicetrust_v1_device_source_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeviceSource); 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_devicetrust_v1_device_source_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_teleport_devicetrust_v1_device_source_proto_goTypes, + DependencyIndexes: file_teleport_devicetrust_v1_device_source_proto_depIdxs, + EnumInfos: file_teleport_devicetrust_v1_device_source_proto_enumTypes, + MessageInfos: file_teleport_devicetrust_v1_device_source_proto_msgTypes, + }.Build() + File_teleport_devicetrust_v1_device_source_proto = out.File + file_teleport_devicetrust_v1_device_source_proto_rawDesc = nil + file_teleport_devicetrust_v1_device_source_proto_goTypes = nil + file_teleport_devicetrust_v1_device_source_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go index b57dd63d99c6a..176836f634bc2 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go @@ -91,6 +91,115 @@ func (DeviceView) EnumDescriptor() ([]byte, []int) { return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{0} } +// Mode of sync for SyncInventory. +type SyncInventoryMode int32 + +const ( + SyncInventoryMode_SYNC_INVENTORY_MODE_UNSPECIFIED SyncInventoryMode = 0 + // Partial inventory sync; not all devices in the external inventory are sent + // to the stream. + // Partial sync precludes inventory cleanup. + SyncInventoryMode_SYNC_INVENTORY_MODE_PARTIAL SyncInventoryMode = 1 + // Full inventory sync; all devices in the external inventory must be sent to + // the stream. + // Full sync allows for handling of missing devices. + SyncInventoryMode_SYNC_INVENTORY_MODE_FULL SyncInventoryMode = 2 +) + +// Enum value maps for SyncInventoryMode. +var ( + SyncInventoryMode_name = map[int32]string{ + 0: "SYNC_INVENTORY_MODE_UNSPECIFIED", + 1: "SYNC_INVENTORY_MODE_PARTIAL", + 2: "SYNC_INVENTORY_MODE_FULL", + } + SyncInventoryMode_value = map[string]int32{ + "SYNC_INVENTORY_MODE_UNSPECIFIED": 0, + "SYNC_INVENTORY_MODE_PARTIAL": 1, + "SYNC_INVENTORY_MODE_FULL": 2, + } +) + +func (x SyncInventoryMode) Enum() *SyncInventoryMode { + p := new(SyncInventoryMode) + *p = x + return p +} + +func (x SyncInventoryMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SyncInventoryMode) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes[1].Descriptor() +} + +func (SyncInventoryMode) Type() protoreflect.EnumType { + return &file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes[1] +} + +func (x SyncInventoryMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SyncInventoryMode.Descriptor instead. +func (SyncInventoryMode) EnumDescriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{1} +} + +// Device action for SyncInventory. +// Typically triggered at the end of FULL syncs.. +type SyncInventoryDeviceAction int32 + +const ( + SyncInventoryDeviceAction_SYNC_INVENTORY_DEVICE_ACTION_UNSPECIFIED SyncInventoryDeviceAction = 0 + // Noop is a no-action. + SyncInventoryDeviceAction_SYNC_INVENTORY_DEVICE_ACTION_NOOP SyncInventoryDeviceAction = 1 + // Deletes the device if the action condition is met. + SyncInventoryDeviceAction_SYNC_INVENTORY_DEVICE_ACTION_DELETE SyncInventoryDeviceAction = 2 +) + +// Enum value maps for SyncInventoryDeviceAction. +var ( + SyncInventoryDeviceAction_name = map[int32]string{ + 0: "SYNC_INVENTORY_DEVICE_ACTION_UNSPECIFIED", + 1: "SYNC_INVENTORY_DEVICE_ACTION_NOOP", + 2: "SYNC_INVENTORY_DEVICE_ACTION_DELETE", + } + SyncInventoryDeviceAction_value = map[string]int32{ + "SYNC_INVENTORY_DEVICE_ACTION_UNSPECIFIED": 0, + "SYNC_INVENTORY_DEVICE_ACTION_NOOP": 1, + "SYNC_INVENTORY_DEVICE_ACTION_DELETE": 2, + } +) + +func (x SyncInventoryDeviceAction) Enum() *SyncInventoryDeviceAction { + p := new(SyncInventoryDeviceAction) + *p = x + return p +} + +func (x SyncInventoryDeviceAction) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SyncInventoryDeviceAction) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes[2].Descriptor() +} + +func (SyncInventoryDeviceAction) Type() protoreflect.EnumType { + return &file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes[2] +} + +func (x SyncInventoryDeviceAction) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SyncInventoryDeviceAction.Descriptor instead. +func (SyncInventoryDeviceAction) EnumDescriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{2} +} + // Request for CreateDevice. type CreateDeviceRequest struct { state protoimpl.MessageState @@ -737,6 +846,9 @@ type DeviceOrStatus struct { // ID of the created device. // Only present if the status is OK. Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + // If true the action attempted against the device was a delete, instead of a + // create or update. + Deleted bool `protobuf:"varint,3,opt,name=deleted,proto3" json:"deleted,omitempty"` } func (x *DeviceOrStatus) Reset() { @@ -785,6 +897,13 @@ func (x *DeviceOrStatus) GetId() string { return "" } +func (x *DeviceOrStatus) GetDeleted() bool { + if x != nil { + return x.Deleted + } + return false +} + // Request for CreateDeviceEnrollToken. type CreateDeviceEnrollTokenRequest struct { state protoimpl.MessageState @@ -793,6 +912,10 @@ type CreateDeviceEnrollTokenRequest struct { // ID of the device. DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + // Device collected data. + // Used to authorize issuance of device enrollment tokens for auto-enrollment. + // Not required otherwise. + DeviceData *DeviceCollectedData `protobuf:"bytes,2,opt,name=device_data,json=deviceData,proto3" json:"device_data,omitempty"` } func (x *CreateDeviceEnrollTokenRequest) Reset() { @@ -834,6 +957,13 @@ func (x *CreateDeviceEnrollTokenRequest) GetDeviceId() string { return "" } +func (x *CreateDeviceEnrollTokenRequest) GetDeviceData() *DeviceCollectedData { + if x != nil { + return x.DeviceData + } + return nil +} + // Request for EnrollDevice. // // macOS enrollment flow: @@ -1626,6 +1756,477 @@ func (x *AuthenticateDeviceChallengeResponse) GetSignature() []byte { return nil } +// Request for SyncInventory. +// +// A typical message sequence is as follows: +// (-> means client-to-server, <- means server-to-client) +// -> SyncInventoryStart +// <- SyncInventoryAck +// (loop) +// -> SyncInventoryDevices (add/remove devices) +// <- SyncInventoryResult +// (end loop) +// -> SyncInventoryEnd +// <- SyncInventoryResult (missing devices) +type SyncInventoryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Payload: + // + // *SyncInventoryRequest_Start + // *SyncInventoryRequest_End + // *SyncInventoryRequest_DevicesToUpsert + // *SyncInventoryRequest_DevicesToRemove + Payload isSyncInventoryRequest_Payload `protobuf_oneof:"payload"` +} + +func (x *SyncInventoryRequest) Reset() { + *x = SyncInventoryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryRequest) ProtoMessage() {} + +func (x *SyncInventoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[25] + 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 SyncInventoryRequest.ProtoReflect.Descriptor instead. +func (*SyncInventoryRequest) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{25} +} + +func (m *SyncInventoryRequest) GetPayload() isSyncInventoryRequest_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (x *SyncInventoryRequest) GetStart() *SyncInventoryStart { + if x, ok := x.GetPayload().(*SyncInventoryRequest_Start); ok { + return x.Start + } + return nil +} + +func (x *SyncInventoryRequest) GetEnd() *SyncInventoryEnd { + if x, ok := x.GetPayload().(*SyncInventoryRequest_End); ok { + return x.End + } + return nil +} + +func (x *SyncInventoryRequest) GetDevicesToUpsert() *SyncInventoryDevices { + if x, ok := x.GetPayload().(*SyncInventoryRequest_DevicesToUpsert); ok { + return x.DevicesToUpsert + } + return nil +} + +func (x *SyncInventoryRequest) GetDevicesToRemove() *SyncInventoryDevices { + if x, ok := x.GetPayload().(*SyncInventoryRequest_DevicesToRemove); ok { + return x.DevicesToRemove + } + return nil +} + +type isSyncInventoryRequest_Payload interface { + isSyncInventoryRequest_Payload() +} + +type SyncInventoryRequest_Start struct { + Start *SyncInventoryStart `protobuf:"bytes,1,opt,name=start,proto3,oneof"` +} + +type SyncInventoryRequest_End struct { + End *SyncInventoryEnd `protobuf:"bytes,2,opt,name=end,proto3,oneof"` +} + +type SyncInventoryRequest_DevicesToUpsert struct { + DevicesToUpsert *SyncInventoryDevices `protobuf:"bytes,3,opt,name=devices_to_upsert,json=devicesToUpsert,proto3,oneof"` +} + +type SyncInventoryRequest_DevicesToRemove struct { + DevicesToRemove *SyncInventoryDevices `protobuf:"bytes,4,opt,name=devices_to_remove,json=devicesToRemove,proto3,oneof"` +} + +func (*SyncInventoryRequest_Start) isSyncInventoryRequest_Payload() {} + +func (*SyncInventoryRequest_End) isSyncInventoryRequest_Payload() {} + +func (*SyncInventoryRequest_DevicesToUpsert) isSyncInventoryRequest_Payload() {} + +func (*SyncInventoryRequest_DevicesToRemove) isSyncInventoryRequest_Payload() {} + +// Response for SyncInventory. +type SyncInventoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Payload: + // + // *SyncInventoryResponse_Ack + // *SyncInventoryResponse_Result + Payload isSyncInventoryResponse_Payload `protobuf_oneof:"payload"` +} + +func (x *SyncInventoryResponse) Reset() { + *x = SyncInventoryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryResponse) ProtoMessage() {} + +func (x *SyncInventoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[26] + 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 SyncInventoryResponse.ProtoReflect.Descriptor instead. +func (*SyncInventoryResponse) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{26} +} + +func (m *SyncInventoryResponse) GetPayload() isSyncInventoryResponse_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (x *SyncInventoryResponse) GetAck() *SyncInventoryAck { + if x, ok := x.GetPayload().(*SyncInventoryResponse_Ack); ok { + return x.Ack + } + return nil +} + +func (x *SyncInventoryResponse) GetResult() *SyncInventoryResult { + if x, ok := x.GetPayload().(*SyncInventoryResponse_Result); ok { + return x.Result + } + return nil +} + +type isSyncInventoryResponse_Payload interface { + isSyncInventoryResponse_Payload() +} + +type SyncInventoryResponse_Ack struct { + Ack *SyncInventoryAck `protobuf:"bytes,1,opt,name=ack,proto3,oneof"` +} + +type SyncInventoryResponse_Result struct { + Result *SyncInventoryResult `protobuf:"bytes,2,opt,name=result,proto3,oneof"` +} + +func (*SyncInventoryResponse_Ack) isSyncInventoryResponse_Payload() {} + +func (*SyncInventoryResponse_Result) isSyncInventoryResponse_Payload() {} + +// SyncInventoryStart starts the inventory sync. +type SyncInventoryStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Source of the inventory sync. + // Acquired from the mTLS certificate for MDM services, must be explicitly + // supplied otherwise. + // This source is used for all devices. The `source` field in individual + // devices is ignored by this RPC. + Source *DeviceSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + // Mode of syncing. + // Required. + Mode SyncInventoryMode `protobuf:"varint,2,opt,name=mode,proto3,enum=teleport.devicetrust.v1.SyncInventoryMode" json:"mode,omitempty"` + // Action for devices missing from the external inventory, but present in + // Teleport. + // Only executed if mode is FULL and mdm_sync_successful is set to true in the + // SyncInventoryEnd message. + OnMissingAction SyncInventoryDeviceAction `protobuf:"varint,3,opt,name=on_missing_action,json=onMissingAction,proto3,enum=teleport.devicetrust.v1.SyncInventoryDeviceAction" json:"on_missing_action,omitempty"` +} + +func (x *SyncInventoryStart) Reset() { + *x = SyncInventoryStart{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryStart) ProtoMessage() {} + +func (x *SyncInventoryStart) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[27] + 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 SyncInventoryStart.ProtoReflect.Descriptor instead. +func (*SyncInventoryStart) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{27} +} + +func (x *SyncInventoryStart) GetSource() *DeviceSource { + if x != nil { + return x.Source + } + return nil +} + +func (x *SyncInventoryStart) GetMode() SyncInventoryMode { + if x != nil { + return x.Mode + } + return SyncInventoryMode_SYNC_INVENTORY_MODE_UNSPECIFIED +} + +func (x *SyncInventoryStart) GetOnMissingAction() SyncInventoryDeviceAction { + if x != nil { + return x.OnMissingAction + } + return SyncInventoryDeviceAction_SYNC_INVENTORY_DEVICE_ACTION_UNSPECIFIED +} + +// SyncInventoryEnd ends the inventory sync, signaling that no more +// SyncInventoryDevices messages will be sent by the client. +type SyncInventoryEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // True if the external sync was fully successful (for example, all reads from + // an MDM API were successful). + // If false the sync's on_missing_action is skipped. + ExternalSyncSuccessful bool `protobuf:"varint,1,opt,name=external_sync_successful,json=externalSyncSuccessful,proto3" json:"external_sync_successful,omitempty"` +} + +func (x *SyncInventoryEnd) Reset() { + *x = SyncInventoryEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryEnd) ProtoMessage() {} + +func (x *SyncInventoryEnd) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[28] + 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 SyncInventoryEnd.ProtoReflect.Descriptor instead. +func (*SyncInventoryEnd) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{28} +} + +func (x *SyncInventoryEnd) GetExternalSyncSuccessful() bool { + if x != nil { + return x.ExternalSyncSuccessful + } + return false +} + +// SyncInventoryDevices transports devices to add/update/remove. +// Removals only need identifying fields to be set. +type SyncInventoryDevices struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Devices to sync. + Devices []*Device `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` +} + +func (x *SyncInventoryDevices) Reset() { + *x = SyncInventoryDevices{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryDevices) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryDevices) ProtoMessage() {} + +func (x *SyncInventoryDevices) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[29] + 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 SyncInventoryDevices.ProtoReflect.Descriptor instead. +func (*SyncInventoryDevices) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{29} +} + +func (x *SyncInventoryDevices) GetDevices() []*Device { + if x != nil { + return x.Devices + } + return nil +} + +// SyncInventoryAck is used to confirm successful processing of messages that +// lack a more specific response. +type SyncInventoryAck struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SyncInventoryAck) Reset() { + *x = SyncInventoryAck{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryAck) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryAck) ProtoMessage() {} + +func (x *SyncInventoryAck) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[30] + 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 SyncInventoryAck.ProtoReflect.Descriptor instead. +func (*SyncInventoryAck) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{30} +} + +// SyncInventoryResult is the response for SyncInventoryDevices or +// SyncInventoryEnd +// It lists all synced/deleted devices. +type SyncInventoryResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Devices modified, in the same order as the input when applicable. + Devices []*DeviceOrStatus `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` +} + +func (x *SyncInventoryResult) Reset() { + *x = SyncInventoryResult{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncInventoryResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncInventoryResult) ProtoMessage() {} + +func (x *SyncInventoryResult) ProtoReflect() protoreflect.Message { + mi := &file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[31] + 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 SyncInventoryResult.ProtoReflect.Descriptor instead. +func (*SyncInventoryResult) Descriptor() ([]byte, []int) { + return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP(), []int{31} +} + +func (x *SyncInventoryResult) GetDevices() []*DeviceOrStatus { + if x != nil { + return x.Devices + } + return nil +} + var File_teleport_devicetrust_v1_devicetrust_service_proto protoreflect.FileDescriptor var file_teleport_devicetrust_v1_devicetrust_service_proto_rawDesc = []byte{ @@ -1648,283 +2249,382 @@ var file_teleport_devicetrust_v1_devicetrust_service_proto_rawDesc = []byte{ 0x31, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xac, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x65, - 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, - 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, - 0x73, 0x6b, 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, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, - 0x22, 0x7c, 0x0a, 0x13, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x6f, 0x1a, 0x2b, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0xac, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, + 0x8b, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x32, - 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x64, 0x22, 0x30, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x6f, - 0x72, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x64, 0x4f, - 0x72, 0x54, 0x61, 0x67, 0x22, 0x50, 0x0a, 0x13, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, - 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x0a, 0x04, 0x76, 0x69, - 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, - 0x69, 0x65, 0x77, 0x22, 0x78, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, - 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x83, 0x01, - 0x0a, 0x18, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, - 0x61, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x5e, 0x0a, 0x19, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x22, 0x3d, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, - 0x22, 0xd4, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, - 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x71, 0x0a, 0x18, 0x6d, 0x61, 0x63, - 0x6f, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, - 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x48, 0x00, 0x52, 0x16, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x43, 0x68, 0x61, 0x6c, 0x6c, - 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xc5, 0x01, 0x0a, 0x14, 0x45, 0x6e, 0x72, 0x6f, - 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x48, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, - 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, - 0x00, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x6d, 0x61, - 0x63, 0x6f, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, - 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x43, 0x68, 0x61, 0x6c, 0x6c, - 0x65, 0x6e, 0x67, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, - 0xdf, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x6e, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, - 0x4d, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x7c, 0x0a, + 0x13, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, - 0x0a, 0x05, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, - 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x05, 0x6d, 0x61, 0x63, 0x6f, - 0x73, 0x22, 0x4e, 0x0a, 0x13, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x0a, + 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x32, 0x0a, 0x13, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, + 0x30, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x6f, 0x72, 0x5f, 0x74, + 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x64, 0x4f, 0x72, 0x54, 0x61, + 0x67, 0x22, 0x50, 0x0a, 0x13, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, + 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, + 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, + 0x22, 0x78, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x22, 0x3a, 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x22, 0x34, 0x0a, - 0x14, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, - 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, - 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, - 0x6e, 0x67, 0x65, 0x22, 0x3c, 0x0a, 0x1c, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, - 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x22, 0xdc, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x45, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, + 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x18, 0x42, + 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x5f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x22, 0x5e, 0x0a, 0x19, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, + 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x66, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x1e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0xd4, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x3f, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, - 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x6d, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, + 0x12, 0x71, 0x0a, 0x18, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, - 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x22, 0xd7, 0x01, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x54, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, - 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, - 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x48, 0x00, 0x52, 0x10, 0x75, - 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x42, - 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xe4, 0x01, 0x0a, 0x16, 0x41, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, + 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x16, 0x6d, 0x61, 0x63, + 0x6f, 0x73, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xc5, + 0x01, 0x0a, 0x14, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, + 0x65, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x63, + 0x6f, 0x73, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x05, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x52, 0x05, 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x22, 0x4e, 0x0a, 0x13, 0x45, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x37, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x3a, 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x4f, + 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x24, + 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x44, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x14, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, + 0x6f, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x22, 0x3c, 0x0a, 0x1c, 0x4d, 0x61, + 0x63, 0x4f, 0x53, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xdc, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x56, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x10, 0x75, 0x73, 0x65, - 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x65, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x6d, 0x0a, + 0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, + 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xd7, 0x01, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, + 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x58, 0x0a, 0x11, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x73, 0x48, 0x00, 0x52, 0x10, 0x75, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0xe4, 0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x56, 0x0a, 0x11, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x22, 0x3b, 0x0a, 0x1b, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x22, 0x43, - 0x0a, 0x23, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x2a, 0x59, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, - 0x77, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, - 0x0a, 0x10, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x4c, 0x49, - 0x53, 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x56, - 0x49, 0x45, 0x57, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x32, 0xa5, - 0x09, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x73, 0x52, 0x10, 0x75, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x0b, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3b, 0x0a, 0x1b, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, + 0x65, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, + 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x22, 0x43, 0x0a, 0x23, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, + 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xdf, 0x02, 0x0a, 0x14, 0x53, + 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, + 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3d, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x64, + 0x48, 0x00, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x5b, 0x0a, 0x11, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x48, 0x00, 0x52, 0x0f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x54, 0x6f, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x12, 0x5b, 0x0a, 0x11, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5f, + 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, + 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x00, + 0x52, 0x0f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xa9, 0x01, 0x0a, + 0x15, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, + 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x63, 0x6b, 0x48, 0x00, + 0x52, 0x03, 0x61, 0x63, 0x6b, 0x12, 0x46, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x09, 0x0a, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xf3, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, + 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x3d, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x3e, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, + 0x74, 0x6f, 0x72, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x5e, + 0x0a, 0x11, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, + 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x6f, + 0x6e, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4c, + 0x0a, 0x10, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x45, + 0x6e, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x79, + 0x6e, 0x63, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x51, 0x0a, 0x14, + 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x12, 0x0a, 0x10, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, + 0x41, 0x63, 0x6b, 0x22, 0x58, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2a, 0x59, 0x0a, + 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x17, 0x44, + 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x45, 0x56, 0x49, + 0x43, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01, 0x12, 0x18, + 0x0a, 0x14, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x52, 0x45, + 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x2a, 0x77, 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, + 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, + 0x1f, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, + 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x49, 0x41, + 0x4c, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x45, + 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x55, 0x4c, 0x4c, 0x10, + 0x02, 0x2a, 0x99, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, + 0x6f, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2c, 0x0a, 0x28, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, 0x54, 0x4f, 0x52, + 0x59, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, + 0x21, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, + 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, + 0x4f, 0x50, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, + 0x45, 0x4e, 0x54, 0x4f, 0x52, 0x59, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x41, 0x43, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x02, 0x32, 0x99, 0x0a, + 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 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, 0x68, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x68, 0x0a, 0x0b, 0x4c, - 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x11, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x7e, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x6f, 0x0a, 0x0c, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x54, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, - 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 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, 0x68, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, + 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x29, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x4c, 0x5a, 0x4a, 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, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x68, 0x0a, 0x0b, 0x4c, 0x69, + 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x11, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x6c, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7e, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x6f, 0x0a, 0x0c, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, + 0x01, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x72, 0x0a, 0x0d, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x4c, 0x5a, 0x4a, 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, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1939,94 +2639,118 @@ func file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescGZIP() []byte return file_teleport_devicetrust_v1_devicetrust_service_proto_rawDescData } -var file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes = make([]protoimpl.MessageInfo, 25) +var file_teleport_devicetrust_v1_devicetrust_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes = make([]protoimpl.MessageInfo, 32) var file_teleport_devicetrust_v1_devicetrust_service_proto_goTypes = []interface{}{ (DeviceView)(0), // 0: teleport.devicetrust.v1.DeviceView - (*CreateDeviceRequest)(nil), // 1: teleport.devicetrust.v1.CreateDeviceRequest - (*UpdateDeviceRequest)(nil), // 2: teleport.devicetrust.v1.UpdateDeviceRequest - (*UpsertDeviceRequest)(nil), // 3: teleport.devicetrust.v1.UpsertDeviceRequest - (*DeleteDeviceRequest)(nil), // 4: teleport.devicetrust.v1.DeleteDeviceRequest - (*FindDevicesRequest)(nil), // 5: teleport.devicetrust.v1.FindDevicesRequest - (*FindDevicesResponse)(nil), // 6: teleport.devicetrust.v1.FindDevicesResponse - (*GetDeviceRequest)(nil), // 7: teleport.devicetrust.v1.GetDeviceRequest - (*ListDevicesRequest)(nil), // 8: teleport.devicetrust.v1.ListDevicesRequest - (*ListDevicesResponse)(nil), // 9: teleport.devicetrust.v1.ListDevicesResponse - (*BulkCreateDevicesRequest)(nil), // 10: teleport.devicetrust.v1.BulkCreateDevicesRequest - (*BulkCreateDevicesResponse)(nil), // 11: teleport.devicetrust.v1.BulkCreateDevicesResponse - (*DeviceOrStatus)(nil), // 12: teleport.devicetrust.v1.DeviceOrStatus - (*CreateDeviceEnrollTokenRequest)(nil), // 13: teleport.devicetrust.v1.CreateDeviceEnrollTokenRequest - (*EnrollDeviceRequest)(nil), // 14: teleport.devicetrust.v1.EnrollDeviceRequest - (*EnrollDeviceResponse)(nil), // 15: teleport.devicetrust.v1.EnrollDeviceResponse - (*EnrollDeviceInit)(nil), // 16: teleport.devicetrust.v1.EnrollDeviceInit - (*EnrollDeviceSuccess)(nil), // 17: teleport.devicetrust.v1.EnrollDeviceSuccess - (*MacOSEnrollPayload)(nil), // 18: teleport.devicetrust.v1.MacOSEnrollPayload - (*MacOSEnrollChallenge)(nil), // 19: teleport.devicetrust.v1.MacOSEnrollChallenge - (*MacOSEnrollChallengeResponse)(nil), // 20: teleport.devicetrust.v1.MacOSEnrollChallengeResponse - (*AuthenticateDeviceRequest)(nil), // 21: teleport.devicetrust.v1.AuthenticateDeviceRequest - (*AuthenticateDeviceResponse)(nil), // 22: teleport.devicetrust.v1.AuthenticateDeviceResponse - (*AuthenticateDeviceInit)(nil), // 23: teleport.devicetrust.v1.AuthenticateDeviceInit - (*AuthenticateDeviceChallenge)(nil), // 24: teleport.devicetrust.v1.AuthenticateDeviceChallenge - (*AuthenticateDeviceChallengeResponse)(nil), // 25: teleport.devicetrust.v1.AuthenticateDeviceChallengeResponse - (*Device)(nil), // 26: teleport.devicetrust.v1.Device - (*fieldmaskpb.FieldMask)(nil), // 27: google.protobuf.FieldMask - (*status.Status)(nil), // 28: google.rpc.Status - (*DeviceCollectedData)(nil), // 29: teleport.devicetrust.v1.DeviceCollectedData - (*UserCertificates)(nil), // 30: teleport.devicetrust.v1.UserCertificates - (*emptypb.Empty)(nil), // 31: google.protobuf.Empty - (*DeviceEnrollToken)(nil), // 32: teleport.devicetrust.v1.DeviceEnrollToken + (SyncInventoryMode)(0), // 1: teleport.devicetrust.v1.SyncInventoryMode + (SyncInventoryDeviceAction)(0), // 2: teleport.devicetrust.v1.SyncInventoryDeviceAction + (*CreateDeviceRequest)(nil), // 3: teleport.devicetrust.v1.CreateDeviceRequest + (*UpdateDeviceRequest)(nil), // 4: teleport.devicetrust.v1.UpdateDeviceRequest + (*UpsertDeviceRequest)(nil), // 5: teleport.devicetrust.v1.UpsertDeviceRequest + (*DeleteDeviceRequest)(nil), // 6: teleport.devicetrust.v1.DeleteDeviceRequest + (*FindDevicesRequest)(nil), // 7: teleport.devicetrust.v1.FindDevicesRequest + (*FindDevicesResponse)(nil), // 8: teleport.devicetrust.v1.FindDevicesResponse + (*GetDeviceRequest)(nil), // 9: teleport.devicetrust.v1.GetDeviceRequest + (*ListDevicesRequest)(nil), // 10: teleport.devicetrust.v1.ListDevicesRequest + (*ListDevicesResponse)(nil), // 11: teleport.devicetrust.v1.ListDevicesResponse + (*BulkCreateDevicesRequest)(nil), // 12: teleport.devicetrust.v1.BulkCreateDevicesRequest + (*BulkCreateDevicesResponse)(nil), // 13: teleport.devicetrust.v1.BulkCreateDevicesResponse + (*DeviceOrStatus)(nil), // 14: teleport.devicetrust.v1.DeviceOrStatus + (*CreateDeviceEnrollTokenRequest)(nil), // 15: teleport.devicetrust.v1.CreateDeviceEnrollTokenRequest + (*EnrollDeviceRequest)(nil), // 16: teleport.devicetrust.v1.EnrollDeviceRequest + (*EnrollDeviceResponse)(nil), // 17: teleport.devicetrust.v1.EnrollDeviceResponse + (*EnrollDeviceInit)(nil), // 18: teleport.devicetrust.v1.EnrollDeviceInit + (*EnrollDeviceSuccess)(nil), // 19: teleport.devicetrust.v1.EnrollDeviceSuccess + (*MacOSEnrollPayload)(nil), // 20: teleport.devicetrust.v1.MacOSEnrollPayload + (*MacOSEnrollChallenge)(nil), // 21: teleport.devicetrust.v1.MacOSEnrollChallenge + (*MacOSEnrollChallengeResponse)(nil), // 22: teleport.devicetrust.v1.MacOSEnrollChallengeResponse + (*AuthenticateDeviceRequest)(nil), // 23: teleport.devicetrust.v1.AuthenticateDeviceRequest + (*AuthenticateDeviceResponse)(nil), // 24: teleport.devicetrust.v1.AuthenticateDeviceResponse + (*AuthenticateDeviceInit)(nil), // 25: teleport.devicetrust.v1.AuthenticateDeviceInit + (*AuthenticateDeviceChallenge)(nil), // 26: teleport.devicetrust.v1.AuthenticateDeviceChallenge + (*AuthenticateDeviceChallengeResponse)(nil), // 27: teleport.devicetrust.v1.AuthenticateDeviceChallengeResponse + (*SyncInventoryRequest)(nil), // 28: teleport.devicetrust.v1.SyncInventoryRequest + (*SyncInventoryResponse)(nil), // 29: teleport.devicetrust.v1.SyncInventoryResponse + (*SyncInventoryStart)(nil), // 30: teleport.devicetrust.v1.SyncInventoryStart + (*SyncInventoryEnd)(nil), // 31: teleport.devicetrust.v1.SyncInventoryEnd + (*SyncInventoryDevices)(nil), // 32: teleport.devicetrust.v1.SyncInventoryDevices + (*SyncInventoryAck)(nil), // 33: teleport.devicetrust.v1.SyncInventoryAck + (*SyncInventoryResult)(nil), // 34: teleport.devicetrust.v1.SyncInventoryResult + (*Device)(nil), // 35: teleport.devicetrust.v1.Device + (*fieldmaskpb.FieldMask)(nil), // 36: google.protobuf.FieldMask + (*status.Status)(nil), // 37: google.rpc.Status + (*DeviceCollectedData)(nil), // 38: teleport.devicetrust.v1.DeviceCollectedData + (*UserCertificates)(nil), // 39: teleport.devicetrust.v1.UserCertificates + (*DeviceSource)(nil), // 40: teleport.devicetrust.v1.DeviceSource + (*emptypb.Empty)(nil), // 41: google.protobuf.Empty + (*DeviceEnrollToken)(nil), // 42: teleport.devicetrust.v1.DeviceEnrollToken } var file_teleport_devicetrust_v1_devicetrust_service_proto_depIdxs = []int32{ - 26, // 0: teleport.devicetrust.v1.CreateDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device - 26, // 1: teleport.devicetrust.v1.UpdateDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device - 27, // 2: teleport.devicetrust.v1.UpdateDeviceRequest.update_mask:type_name -> google.protobuf.FieldMask - 26, // 3: teleport.devicetrust.v1.UpsertDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device - 26, // 4: teleport.devicetrust.v1.FindDevicesResponse.devices:type_name -> teleport.devicetrust.v1.Device + 35, // 0: teleport.devicetrust.v1.CreateDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device + 35, // 1: teleport.devicetrust.v1.UpdateDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device + 36, // 2: teleport.devicetrust.v1.UpdateDeviceRequest.update_mask:type_name -> google.protobuf.FieldMask + 35, // 3: teleport.devicetrust.v1.UpsertDeviceRequest.device:type_name -> teleport.devicetrust.v1.Device + 35, // 4: teleport.devicetrust.v1.FindDevicesResponse.devices:type_name -> teleport.devicetrust.v1.Device 0, // 5: teleport.devicetrust.v1.ListDevicesRequest.view:type_name -> teleport.devicetrust.v1.DeviceView - 26, // 6: teleport.devicetrust.v1.ListDevicesResponse.devices:type_name -> teleport.devicetrust.v1.Device - 26, // 7: teleport.devicetrust.v1.BulkCreateDevicesRequest.devices:type_name -> teleport.devicetrust.v1.Device - 12, // 8: teleport.devicetrust.v1.BulkCreateDevicesResponse.devices:type_name -> teleport.devicetrust.v1.DeviceOrStatus - 28, // 9: teleport.devicetrust.v1.DeviceOrStatus.status:type_name -> google.rpc.Status - 16, // 10: teleport.devicetrust.v1.EnrollDeviceRequest.init:type_name -> teleport.devicetrust.v1.EnrollDeviceInit - 20, // 11: teleport.devicetrust.v1.EnrollDeviceRequest.macos_challenge_response:type_name -> teleport.devicetrust.v1.MacOSEnrollChallengeResponse - 17, // 12: teleport.devicetrust.v1.EnrollDeviceResponse.success:type_name -> teleport.devicetrust.v1.EnrollDeviceSuccess - 19, // 13: teleport.devicetrust.v1.EnrollDeviceResponse.macos_challenge:type_name -> teleport.devicetrust.v1.MacOSEnrollChallenge - 29, // 14: teleport.devicetrust.v1.EnrollDeviceInit.device_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData - 18, // 15: teleport.devicetrust.v1.EnrollDeviceInit.macos:type_name -> teleport.devicetrust.v1.MacOSEnrollPayload - 26, // 16: teleport.devicetrust.v1.EnrollDeviceSuccess.device:type_name -> teleport.devicetrust.v1.Device - 23, // 17: teleport.devicetrust.v1.AuthenticateDeviceRequest.init:type_name -> teleport.devicetrust.v1.AuthenticateDeviceInit - 25, // 18: teleport.devicetrust.v1.AuthenticateDeviceRequest.challenge_response:type_name -> teleport.devicetrust.v1.AuthenticateDeviceChallengeResponse - 24, // 19: teleport.devicetrust.v1.AuthenticateDeviceResponse.challenge:type_name -> teleport.devicetrust.v1.AuthenticateDeviceChallenge - 30, // 20: teleport.devicetrust.v1.AuthenticateDeviceResponse.user_certificates:type_name -> teleport.devicetrust.v1.UserCertificates - 30, // 21: teleport.devicetrust.v1.AuthenticateDeviceInit.user_certificates:type_name -> teleport.devicetrust.v1.UserCertificates - 29, // 22: teleport.devicetrust.v1.AuthenticateDeviceInit.device_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData - 1, // 23: teleport.devicetrust.v1.DeviceTrustService.CreateDevice:input_type -> teleport.devicetrust.v1.CreateDeviceRequest - 2, // 24: teleport.devicetrust.v1.DeviceTrustService.UpdateDevice:input_type -> teleport.devicetrust.v1.UpdateDeviceRequest - 3, // 25: teleport.devicetrust.v1.DeviceTrustService.UpsertDevice:input_type -> teleport.devicetrust.v1.UpsertDeviceRequest - 4, // 26: teleport.devicetrust.v1.DeviceTrustService.DeleteDevice:input_type -> teleport.devicetrust.v1.DeleteDeviceRequest - 5, // 27: teleport.devicetrust.v1.DeviceTrustService.FindDevices:input_type -> teleport.devicetrust.v1.FindDevicesRequest - 7, // 28: teleport.devicetrust.v1.DeviceTrustService.GetDevice:input_type -> teleport.devicetrust.v1.GetDeviceRequest - 8, // 29: teleport.devicetrust.v1.DeviceTrustService.ListDevices:input_type -> teleport.devicetrust.v1.ListDevicesRequest - 10, // 30: teleport.devicetrust.v1.DeviceTrustService.BulkCreateDevices:input_type -> teleport.devicetrust.v1.BulkCreateDevicesRequest - 13, // 31: teleport.devicetrust.v1.DeviceTrustService.CreateDeviceEnrollToken:input_type -> teleport.devicetrust.v1.CreateDeviceEnrollTokenRequest - 14, // 32: teleport.devicetrust.v1.DeviceTrustService.EnrollDevice:input_type -> teleport.devicetrust.v1.EnrollDeviceRequest - 21, // 33: teleport.devicetrust.v1.DeviceTrustService.AuthenticateDevice:input_type -> teleport.devicetrust.v1.AuthenticateDeviceRequest - 26, // 34: teleport.devicetrust.v1.DeviceTrustService.CreateDevice:output_type -> teleport.devicetrust.v1.Device - 26, // 35: teleport.devicetrust.v1.DeviceTrustService.UpdateDevice:output_type -> teleport.devicetrust.v1.Device - 26, // 36: teleport.devicetrust.v1.DeviceTrustService.UpsertDevice:output_type -> teleport.devicetrust.v1.Device - 31, // 37: teleport.devicetrust.v1.DeviceTrustService.DeleteDevice:output_type -> google.protobuf.Empty - 6, // 38: teleport.devicetrust.v1.DeviceTrustService.FindDevices:output_type -> teleport.devicetrust.v1.FindDevicesResponse - 26, // 39: teleport.devicetrust.v1.DeviceTrustService.GetDevice:output_type -> teleport.devicetrust.v1.Device - 9, // 40: teleport.devicetrust.v1.DeviceTrustService.ListDevices:output_type -> teleport.devicetrust.v1.ListDevicesResponse - 11, // 41: teleport.devicetrust.v1.DeviceTrustService.BulkCreateDevices:output_type -> teleport.devicetrust.v1.BulkCreateDevicesResponse - 32, // 42: teleport.devicetrust.v1.DeviceTrustService.CreateDeviceEnrollToken:output_type -> teleport.devicetrust.v1.DeviceEnrollToken - 15, // 43: teleport.devicetrust.v1.DeviceTrustService.EnrollDevice:output_type -> teleport.devicetrust.v1.EnrollDeviceResponse - 22, // 44: teleport.devicetrust.v1.DeviceTrustService.AuthenticateDevice:output_type -> teleport.devicetrust.v1.AuthenticateDeviceResponse - 34, // [34:45] is the sub-list for method output_type - 23, // [23:34] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name + 35, // 6: teleport.devicetrust.v1.ListDevicesResponse.devices:type_name -> teleport.devicetrust.v1.Device + 35, // 7: teleport.devicetrust.v1.BulkCreateDevicesRequest.devices:type_name -> teleport.devicetrust.v1.Device + 14, // 8: teleport.devicetrust.v1.BulkCreateDevicesResponse.devices:type_name -> teleport.devicetrust.v1.DeviceOrStatus + 37, // 9: teleport.devicetrust.v1.DeviceOrStatus.status:type_name -> google.rpc.Status + 38, // 10: teleport.devicetrust.v1.CreateDeviceEnrollTokenRequest.device_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData + 18, // 11: teleport.devicetrust.v1.EnrollDeviceRequest.init:type_name -> teleport.devicetrust.v1.EnrollDeviceInit + 22, // 12: teleport.devicetrust.v1.EnrollDeviceRequest.macos_challenge_response:type_name -> teleport.devicetrust.v1.MacOSEnrollChallengeResponse + 19, // 13: teleport.devicetrust.v1.EnrollDeviceResponse.success:type_name -> teleport.devicetrust.v1.EnrollDeviceSuccess + 21, // 14: teleport.devicetrust.v1.EnrollDeviceResponse.macos_challenge:type_name -> teleport.devicetrust.v1.MacOSEnrollChallenge + 38, // 15: teleport.devicetrust.v1.EnrollDeviceInit.device_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData + 20, // 16: teleport.devicetrust.v1.EnrollDeviceInit.macos:type_name -> teleport.devicetrust.v1.MacOSEnrollPayload + 35, // 17: teleport.devicetrust.v1.EnrollDeviceSuccess.device:type_name -> teleport.devicetrust.v1.Device + 25, // 18: teleport.devicetrust.v1.AuthenticateDeviceRequest.init:type_name -> teleport.devicetrust.v1.AuthenticateDeviceInit + 27, // 19: teleport.devicetrust.v1.AuthenticateDeviceRequest.challenge_response:type_name -> teleport.devicetrust.v1.AuthenticateDeviceChallengeResponse + 26, // 20: teleport.devicetrust.v1.AuthenticateDeviceResponse.challenge:type_name -> teleport.devicetrust.v1.AuthenticateDeviceChallenge + 39, // 21: teleport.devicetrust.v1.AuthenticateDeviceResponse.user_certificates:type_name -> teleport.devicetrust.v1.UserCertificates + 39, // 22: teleport.devicetrust.v1.AuthenticateDeviceInit.user_certificates:type_name -> teleport.devicetrust.v1.UserCertificates + 38, // 23: teleport.devicetrust.v1.AuthenticateDeviceInit.device_data:type_name -> teleport.devicetrust.v1.DeviceCollectedData + 30, // 24: teleport.devicetrust.v1.SyncInventoryRequest.start:type_name -> teleport.devicetrust.v1.SyncInventoryStart + 31, // 25: teleport.devicetrust.v1.SyncInventoryRequest.end:type_name -> teleport.devicetrust.v1.SyncInventoryEnd + 32, // 26: teleport.devicetrust.v1.SyncInventoryRequest.devices_to_upsert:type_name -> teleport.devicetrust.v1.SyncInventoryDevices + 32, // 27: teleport.devicetrust.v1.SyncInventoryRequest.devices_to_remove:type_name -> teleport.devicetrust.v1.SyncInventoryDevices + 33, // 28: teleport.devicetrust.v1.SyncInventoryResponse.ack:type_name -> teleport.devicetrust.v1.SyncInventoryAck + 34, // 29: teleport.devicetrust.v1.SyncInventoryResponse.result:type_name -> teleport.devicetrust.v1.SyncInventoryResult + 40, // 30: teleport.devicetrust.v1.SyncInventoryStart.source:type_name -> teleport.devicetrust.v1.DeviceSource + 1, // 31: teleport.devicetrust.v1.SyncInventoryStart.mode:type_name -> teleport.devicetrust.v1.SyncInventoryMode + 2, // 32: teleport.devicetrust.v1.SyncInventoryStart.on_missing_action:type_name -> teleport.devicetrust.v1.SyncInventoryDeviceAction + 35, // 33: teleport.devicetrust.v1.SyncInventoryDevices.devices:type_name -> teleport.devicetrust.v1.Device + 14, // 34: teleport.devicetrust.v1.SyncInventoryResult.devices:type_name -> teleport.devicetrust.v1.DeviceOrStatus + 3, // 35: teleport.devicetrust.v1.DeviceTrustService.CreateDevice:input_type -> teleport.devicetrust.v1.CreateDeviceRequest + 4, // 36: teleport.devicetrust.v1.DeviceTrustService.UpdateDevice:input_type -> teleport.devicetrust.v1.UpdateDeviceRequest + 5, // 37: teleport.devicetrust.v1.DeviceTrustService.UpsertDevice:input_type -> teleport.devicetrust.v1.UpsertDeviceRequest + 6, // 38: teleport.devicetrust.v1.DeviceTrustService.DeleteDevice:input_type -> teleport.devicetrust.v1.DeleteDeviceRequest + 7, // 39: teleport.devicetrust.v1.DeviceTrustService.FindDevices:input_type -> teleport.devicetrust.v1.FindDevicesRequest + 9, // 40: teleport.devicetrust.v1.DeviceTrustService.GetDevice:input_type -> teleport.devicetrust.v1.GetDeviceRequest + 10, // 41: teleport.devicetrust.v1.DeviceTrustService.ListDevices:input_type -> teleport.devicetrust.v1.ListDevicesRequest + 12, // 42: teleport.devicetrust.v1.DeviceTrustService.BulkCreateDevices:input_type -> teleport.devicetrust.v1.BulkCreateDevicesRequest + 15, // 43: teleport.devicetrust.v1.DeviceTrustService.CreateDeviceEnrollToken:input_type -> teleport.devicetrust.v1.CreateDeviceEnrollTokenRequest + 16, // 44: teleport.devicetrust.v1.DeviceTrustService.EnrollDevice:input_type -> teleport.devicetrust.v1.EnrollDeviceRequest + 23, // 45: teleport.devicetrust.v1.DeviceTrustService.AuthenticateDevice:input_type -> teleport.devicetrust.v1.AuthenticateDeviceRequest + 28, // 46: teleport.devicetrust.v1.DeviceTrustService.SyncInventory:input_type -> teleport.devicetrust.v1.SyncInventoryRequest + 35, // 47: teleport.devicetrust.v1.DeviceTrustService.CreateDevice:output_type -> teleport.devicetrust.v1.Device + 35, // 48: teleport.devicetrust.v1.DeviceTrustService.UpdateDevice:output_type -> teleport.devicetrust.v1.Device + 35, // 49: teleport.devicetrust.v1.DeviceTrustService.UpsertDevice:output_type -> teleport.devicetrust.v1.Device + 41, // 50: teleport.devicetrust.v1.DeviceTrustService.DeleteDevice:output_type -> google.protobuf.Empty + 8, // 51: teleport.devicetrust.v1.DeviceTrustService.FindDevices:output_type -> teleport.devicetrust.v1.FindDevicesResponse + 35, // 52: teleport.devicetrust.v1.DeviceTrustService.GetDevice:output_type -> teleport.devicetrust.v1.Device + 11, // 53: teleport.devicetrust.v1.DeviceTrustService.ListDevices:output_type -> teleport.devicetrust.v1.ListDevicesResponse + 13, // 54: teleport.devicetrust.v1.DeviceTrustService.BulkCreateDevices:output_type -> teleport.devicetrust.v1.BulkCreateDevicesResponse + 42, // 55: teleport.devicetrust.v1.DeviceTrustService.CreateDeviceEnrollToken:output_type -> teleport.devicetrust.v1.DeviceEnrollToken + 17, // 56: teleport.devicetrust.v1.DeviceTrustService.EnrollDevice:output_type -> teleport.devicetrust.v1.EnrollDeviceResponse + 24, // 57: teleport.devicetrust.v1.DeviceTrustService.AuthenticateDevice:output_type -> teleport.devicetrust.v1.AuthenticateDeviceResponse + 29, // 58: teleport.devicetrust.v1.DeviceTrustService.SyncInventory:output_type -> teleport.devicetrust.v1.SyncInventoryResponse + 47, // [47:59] is the sub-list for method output_type + 35, // [35:47] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_teleport_devicetrust_v1_devicetrust_service_proto_init() } @@ -2037,6 +2761,7 @@ func file_teleport_devicetrust_v1_devicetrust_service_proto_init() { file_teleport_devicetrust_v1_device_proto_init() file_teleport_devicetrust_v1_device_collected_data_proto_init() file_teleport_devicetrust_v1_device_enroll_token_proto_init() + file_teleport_devicetrust_v1_device_source_proto_init() file_teleport_devicetrust_v1_user_certificates_proto_init() if !protoimpl.UnsafeEnabled { file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { @@ -2339,6 +3064,90 @@ func file_teleport_devicetrust_v1_devicetrust_service_proto_init() { return nil } } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryStart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryEnd); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryDevices); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncInventoryResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[13].OneofWrappers = []interface{}{ (*EnrollDeviceRequest_Init)(nil), @@ -2356,13 +3165,23 @@ func file_teleport_devicetrust_v1_devicetrust_service_proto_init() { (*AuthenticateDeviceResponse_Challenge)(nil), (*AuthenticateDeviceResponse_UserCertificates)(nil), } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[25].OneofWrappers = []interface{}{ + (*SyncInventoryRequest_Start)(nil), + (*SyncInventoryRequest_End)(nil), + (*SyncInventoryRequest_DevicesToUpsert)(nil), + (*SyncInventoryRequest_DevicesToRemove)(nil), + } + file_teleport_devicetrust_v1_devicetrust_service_proto_msgTypes[26].OneofWrappers = []interface{}{ + (*SyncInventoryResponse_Ack)(nil), + (*SyncInventoryResponse_Result)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_devicetrust_v1_devicetrust_service_proto_rawDesc, - NumEnums: 1, - NumMessages: 25, + NumEnums: 3, + NumMessages: 32, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go index e0db094e17f49..c3ceceac5de19 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go @@ -45,6 +45,7 @@ const ( DeviceTrustService_CreateDeviceEnrollToken_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/CreateDeviceEnrollToken" DeviceTrustService_EnrollDevice_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/EnrollDevice" DeviceTrustService_AuthenticateDevice_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/AuthenticateDevice" + DeviceTrustService_SyncInventory_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/SyncInventory" ) // DeviceTrustServiceClient is the client API for DeviceTrustService service. @@ -113,6 +114,13 @@ type DeviceTrustServiceClient interface { // // Only registered and enrolled devices may perform device authentication. AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_AuthenticateDeviceClient, error) + // Syncs device inventory from a source exterior to Teleport, for example an + // MDM. + // Allows both partial and full syncs; for the latter, devices missing from + // the external inventory are handled as specified. + // Authorized either by a valid MDM service certificate or the appropriate + // "device" permissions (create/update/delete). + SyncInventory(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_SyncInventoryClient, error) } type deviceTrustServiceClient struct { @@ -266,6 +274,37 @@ func (x *deviceTrustServiceAuthenticateDeviceClient) Recv() (*AuthenticateDevice return m, nil } +func (c *deviceTrustServiceClient) SyncInventory(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_SyncInventoryClient, error) { + stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[2], DeviceTrustService_SyncInventory_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &deviceTrustServiceSyncInventoryClient{stream} + return x, nil +} + +type DeviceTrustService_SyncInventoryClient interface { + Send(*SyncInventoryRequest) error + Recv() (*SyncInventoryResponse, error) + grpc.ClientStream +} + +type deviceTrustServiceSyncInventoryClient struct { + grpc.ClientStream +} + +func (x *deviceTrustServiceSyncInventoryClient) Send(m *SyncInventoryRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *deviceTrustServiceSyncInventoryClient) Recv() (*SyncInventoryResponse, error) { + m := new(SyncInventoryResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // DeviceTrustServiceServer is the server API for DeviceTrustService service. // All implementations must embed UnimplementedDeviceTrustServiceServer // for forward compatibility @@ -332,6 +371,13 @@ type DeviceTrustServiceServer interface { // // Only registered and enrolled devices may perform device authentication. AuthenticateDevice(DeviceTrustService_AuthenticateDeviceServer) error + // Syncs device inventory from a source exterior to Teleport, for example an + // MDM. + // Allows both partial and full syncs; for the latter, devices missing from + // the external inventory are handled as specified. + // Authorized either by a valid MDM service certificate or the appropriate + // "device" permissions (create/update/delete). + SyncInventory(DeviceTrustService_SyncInventoryServer) error mustEmbedUnimplementedDeviceTrustServiceServer() } @@ -372,6 +418,9 @@ func (UnimplementedDeviceTrustServiceServer) EnrollDevice(DeviceTrustService_Enr func (UnimplementedDeviceTrustServiceServer) AuthenticateDevice(DeviceTrustService_AuthenticateDeviceServer) error { return status.Errorf(codes.Unimplemented, "method AuthenticateDevice not implemented") } +func (UnimplementedDeviceTrustServiceServer) SyncInventory(DeviceTrustService_SyncInventoryServer) error { + return status.Errorf(codes.Unimplemented, "method SyncInventory not implemented") +} func (UnimplementedDeviceTrustServiceServer) mustEmbedUnimplementedDeviceTrustServiceServer() {} // UnsafeDeviceTrustServiceServer may be embedded to opt out of forward compatibility for this service. @@ -599,6 +648,32 @@ func (x *deviceTrustServiceAuthenticateDeviceServer) Recv() (*AuthenticateDevice return m, nil } +func _DeviceTrustService_SyncInventory_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(DeviceTrustServiceServer).SyncInventory(&deviceTrustServiceSyncInventoryServer{stream}) +} + +type DeviceTrustService_SyncInventoryServer interface { + Send(*SyncInventoryResponse) error + Recv() (*SyncInventoryRequest, error) + grpc.ServerStream +} + +type deviceTrustServiceSyncInventoryServer struct { + grpc.ServerStream +} + +func (x *deviceTrustServiceSyncInventoryServer) Send(m *SyncInventoryResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *deviceTrustServiceSyncInventoryServer) Recv() (*SyncInventoryRequest, error) { + m := new(SyncInventoryRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // DeviceTrustService_ServiceDesc is the grpc.ServiceDesc for DeviceTrustService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -656,6 +731,12 @@ var DeviceTrustService_ServiceDesc = grpc.ServiceDesc{ ServerStreams: true, ClientStreams: true, }, + { + StreamName: "SyncInventory", + Handler: _DeviceTrustService_SyncInventory_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, Metadata: "teleport/devicetrust/v1/devicetrust_service.proto", } diff --git a/api/gen/proto/go/usageevents/v1/usageevents.pb.go b/api/gen/proto/go/usageevents/v1/usageevents.pb.go index 36033888d48a4..57e663baa9f46 100644 --- a/api/gen/proto/go/usageevents/v1/usageevents.pb.go +++ b/api/gen/proto/go/usageevents/v1/usageevents.pb.go @@ -26,24 +26,43 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type DiscoverResource int32 const ( - DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED DiscoverResource = 0 - DiscoverResource_DISCOVER_RESOURCE_SERVER DiscoverResource = 1 - DiscoverResource_DISCOVER_RESOURCE_KUBERNETES DiscoverResource = 2 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED DiscoverResource = 3 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED DiscoverResource = 4 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED DiscoverResource = 5 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS DiscoverResource = 6 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_RDS DiscoverResource = 7 - DiscoverResource_DISCOVER_RESOURCE_APPLICATION_HTTP DiscoverResource = 8 - DiscoverResource_DISCOVER_RESOURCE_APPLICATION_TCP DiscoverResource = 9 - DiscoverResource_DISCOVER_RESOURCE_WINDOWS_DESKTOP DiscoverResource = 10 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS DiscoverResource = 11 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT DiscoverResource = 12 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED DiscoverResource = 13 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED DiscoverResource = 14 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP DiscoverResource = 15 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_GCP DiscoverResource = 16 - DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP DiscoverResource = 17 + DiscoverResource_DISCOVER_RESOURCE_UNSPECIFIED DiscoverResource = 0 + DiscoverResource_DISCOVER_RESOURCE_SERVER DiscoverResource = 1 + DiscoverResource_DISCOVER_RESOURCE_KUBERNETES DiscoverResource = 2 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED DiscoverResource = 3 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED DiscoverResource = 4 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED DiscoverResource = 5 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS DiscoverResource = 6 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_RDS DiscoverResource = 7 + DiscoverResource_DISCOVER_RESOURCE_APPLICATION_HTTP DiscoverResource = 8 + DiscoverResource_DISCOVER_RESOURCE_APPLICATION_TCP DiscoverResource = 9 + DiscoverResource_DISCOVER_RESOURCE_WINDOWS_DESKTOP DiscoverResource = 10 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS DiscoverResource = 11 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT DiscoverResource = 12 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED DiscoverResource = 13 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED DiscoverResource = 14 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP DiscoverResource = 15 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_GCP DiscoverResource = 16 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP DiscoverResource = 17 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS DiscoverResource = 18 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE DiscoverResource = 19 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_DYNAMODB DiscoverResource = 20 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES DiscoverResource = 21 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED DiscoverResource = 22 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED DiscoverResource = 23 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE DiscoverResource = 24 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB DiscoverResource = 25 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE DiscoverResource = 26 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED DiscoverResource = 27 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE DiscoverResource = 28 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE DiscoverResource = 29 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT DiscoverResource = 30 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED DiscoverResource = 31 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS DiscoverResource = 32 + DiscoverResource_DISCOVER_RESOURCE_DATABASE_SNOWFLAKE DiscoverResource = 33 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY DiscoverResource = 34 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY DiscoverResource = 35 + DiscoverResource_DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION DiscoverResource = 36 ) var DiscoverResource_name = map[int32]string{ @@ -65,27 +84,65 @@ var DiscoverResource_name = map[int32]string{ 15: "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP", 16: "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP", 17: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP", + 18: "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS", + 19: "DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE", + 20: "DISCOVER_RESOURCE_DATABASE_DYNAMODB", + 21: "DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES", + 22: "DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED", + 23: "DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED", + 24: "DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE", + 25: "DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB", + 26: "DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE", + 27: "DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED", + 28: "DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE", + 29: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE", + 30: "DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT", + 31: "DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED", + 32: "DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS", + 33: "DISCOVER_RESOURCE_DATABASE_SNOWFLAKE", + 34: "DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY", + 35: "DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY", + 36: "DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION", } var DiscoverResource_value = map[string]int32{ - "DISCOVER_RESOURCE_UNSPECIFIED": 0, - "DISCOVER_RESOURCE_SERVER": 1, - "DISCOVER_RESOURCE_KUBERNETES": 2, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED": 3, - "DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED": 4, - "DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED": 5, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS": 6, - "DISCOVER_RESOURCE_DATABASE_MYSQL_RDS": 7, - "DISCOVER_RESOURCE_APPLICATION_HTTP": 8, - "DISCOVER_RESOURCE_APPLICATION_TCP": 9, - "DISCOVER_RESOURCE_WINDOWS_DESKTOP": 10, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS": 11, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT": 12, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED": 13, - "DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED": 14, - "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP": 15, - "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP": 16, - "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP": 17, + "DISCOVER_RESOURCE_UNSPECIFIED": 0, + "DISCOVER_RESOURCE_SERVER": 1, + "DISCOVER_RESOURCE_KUBERNETES": 2, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_SELF_HOSTED": 3, + "DISCOVER_RESOURCE_DATABASE_MYSQL_SELF_HOSTED": 4, + "DISCOVER_RESOURCE_DATABASE_MONGODB_SELF_HOSTED": 5, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_RDS": 6, + "DISCOVER_RESOURCE_DATABASE_MYSQL_RDS": 7, + "DISCOVER_RESOURCE_APPLICATION_HTTP": 8, + "DISCOVER_RESOURCE_APPLICATION_TCP": 9, + "DISCOVER_RESOURCE_WINDOWS_DESKTOP": 10, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_RDS": 11, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT": 12, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_SELF_HOSTED": 13, + "DISCOVER_RESOURCE_DATABASE_REDIS_SELF_HOSTED": 14, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP": 15, + "DISCOVER_RESOURCE_DATABASE_MYSQL_GCP": 16, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP": 17, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS": 18, + "DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE": 19, + "DISCOVER_RESOURCE_DATABASE_DYNAMODB": 20, + "DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES": 21, + "DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED": 22, + "DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED": 23, + "DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE": 24, + "DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB": 25, + "DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE": 26, + "DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED": 27, + "DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE": 28, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE": 29, + "DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT": 30, + "DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED": 31, + "DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS": 32, + "DISCOVER_RESOURCE_DATABASE_SNOWFLAKE": 33, + "DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY": 34, + "DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY": 35, + "DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION": 36, } func (x DiscoverResource) String() string { @@ -2135,129 +2192,145 @@ func init() { } var fileDescriptor_94cf2ca1c69fd564 = []byte{ - // 1949 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x9a, 0xcd, 0x6f, 0xe4, 0x48, - 0xf9, 0xc7, 0xed, 0xce, 0x4c, 0x26, 0x53, 0xb3, 0x93, 0xf4, 0xd6, 0x6f, 0x76, 0xa6, 0x33, 0x79, - 0xef, 0x79, 0x4b, 0x32, 0xa3, 0xf4, 0x66, 0x76, 0x7f, 0xb0, 0xb0, 0x0b, 0xa3, 0x8e, 0xdb, 0x89, - 0x7b, 0x27, 0x49, 0xf7, 0x96, 0xbb, 0x67, 0x05, 0x97, 0x52, 0xc5, 0xae, 0xf4, 0x58, 0xe3, 0xb6, - 0x5b, 0x76, 0xb9, 0x43, 0x0b, 0xa1, 0x15, 0x08, 0x21, 0xa4, 0x91, 0x10, 0xdc, 0x90, 0x90, 0x90, - 0x10, 0x1c, 0x10, 0xe2, 0xce, 0x99, 0x1b, 0x47, 0xfe, 0x04, 0x34, 0xff, 0x03, 0x07, 0x10, 0x68, - 0x91, 0xcb, 0xaf, 0xfd, 0x6a, 0x33, 0xe2, 0x94, 0xbd, 0xc5, 0xae, 0xa7, 0x9e, 0xe7, 0xf3, 0x3c, - 0x55, 0xb6, 0xeb, 0xfb, 0xa4, 0xc1, 0x0e, 0xa3, 0x26, 0xed, 0xd9, 0x0e, 0xab, 0x78, 0x2e, 0xe9, - 0x50, 0xda, 0xa7, 0x16, 0x73, 0x2b, 0xfd, 0xfd, 0xf4, 0xe5, 0x5e, 0xcf, 0xb1, 0x99, 0x0d, 0xef, - 0x44, 0xa6, 0x7b, 0xe9, 0xb1, 0xfe, 0x7e, 0x79, 0x17, 0xc0, 0x76, 0xfd, 0x80, 0x58, 0x16, 0x75, - 0x24, 0xd3, 0xd0, 0x5e, 0xc9, 0xfe, 0x08, 0xbc, 0x05, 0xae, 0x12, 0x93, 0x3a, 0xac, 0x24, 0x6e, - 0x8a, 0xdb, 0xd7, 0x51, 0x70, 0x51, 0x3e, 0x04, 0xdb, 0xed, 0x7a, 0xc3, 0x3a, 0xb3, 0x89, 0xa3, - 0x4b, 0x76, 0xb7, 0x67, 0x52, 0x46, 0x8f, 0xec, 0x96, 0x5d, 0x23, 0xee, 0xcb, 0xe0, 0x66, 0xe2, - 0xe1, 0x2e, 0x58, 0xf0, 0x5c, 0xea, 0x58, 0xa4, 0x4b, 0x43, 0x27, 0xf1, 0x75, 0xf9, 0x01, 0xb8, - 0x17, 0xfb, 0xa9, 0xea, 0xfa, 0xa1, 0xe1, 0xb8, 0x0c, 0x51, 0xd7, 0xf6, 0x1c, 0x8d, 0x26, 0x2e, - 0xca, 0xbb, 0xa9, 0x70, 0xa3, 0x66, 0xc7, 0x84, 0xa5, 0x81, 0xcb, 0xcf, 0xc0, 0x56, 0x6c, 0xab, - 0x52, 0x26, 0x39, 0x54, 0xa7, 0x16, 0x33, 0x88, 0xa9, 0x7a, 0x67, 0x5d, 0x83, 0x65, 0x33, 0xfd, - 0x00, 0x3c, 0x88, 0x1d, 0x20, 0xda, 0x31, 0x5c, 0xdf, 0xff, 0x4b, 0x62, 0x9a, 0xd4, 0xea, 0xd0, - 0x9c, 0x4e, 0xe0, 0x32, 0x58, 0xe8, 0x9e, 0x13, 0xcc, 0x06, 0x3d, 0x5a, 0x2a, 0xf0, 0xb1, 0x6b, - 0xdd, 0x73, 0xd2, 0x1a, 0xf4, 0x28, 0x5c, 0x03, 0xc0, 0xb4, 0x3b, 0x86, 0x85, 0xcf, 0x4d, 0xfb, - 0xa2, 0x34, 0xc7, 0x07, 0xaf, 0xf3, 0x3b, 0x87, 0xa6, 0x7d, 0x11, 0xf0, 0x23, 0xaa, 0xd9, 0x7d, - 0xea, 0x0c, 0x24, 0x5b, 0xa7, 0xae, 0x64, 0x5b, 0xcc, 0xb0, 0x3c, 0x9a, 0xb3, 0xa6, 0x1f, 0x83, - 0xb5, 0x31, 0x07, 0xbd, 0x41, 0xce, 0xc9, 0x9f, 0x80, 0xf5, 0x91, 0xc9, 0x4d, 0xc7, 0xb0, 0x58, - 0xce, 0xd9, 0x65, 0x50, 0xac, 0x19, 0x2e, 0x9f, 0x7c, 0x42, 0x19, 0xd1, 0x09, 0x23, 0x70, 0x11, - 0x14, 0x0c, 0x3d, 0xb4, 0x2c, 0x18, 0x7a, 0x99, 0x80, 0x52, 0x64, 0x13, 0x2d, 0x61, 0x6c, 0x2b, - 0x83, 0x05, 0x27, 0xbc, 0xc7, 0x67, 0x2c, 0x3e, 0xdd, 0xd9, 0x9b, 0xb2, 0x5d, 0xf7, 0x46, 0x9d, - 0xa0, 0x78, 0x6a, 0xf9, 0x15, 0x80, 0xd1, 0xa8, 0xca, 0x68, 0x4f, 0x65, 0x84, 0x79, 0x2e, 0x7c, - 0x06, 0xe6, 0x5d, 0xfe, 0x57, 0xe8, 0xfa, 0x51, 0xa6, 0xeb, 0x60, 0x22, 0x0a, 0xa7, 0xf9, 0x8f, - 0x02, 0x75, 0x1c, 0xdb, 0x09, 0x17, 0x34, 0xb8, 0x28, 0xff, 0x4e, 0x04, 0xb7, 0xdb, 0xf5, 0xd4, - 0x14, 0x87, 0x51, 0x3d, 0x28, 0x95, 0x0c, 0x16, 0xba, 0x61, 0x6a, 0x3c, 0xe6, 0x8d, 0x1c, 0xe9, - 0x44, 0xb5, 0x40, 0xf1, 0x54, 0x28, 0xc5, 0xe0, 0x05, 0xee, 0xe4, 0x71, 0x0e, 0xf0, 0x28, 0xeb, - 0x08, 0xbe, 0xfc, 0x6f, 0x11, 0x6c, 0x26, 0x98, 0x51, 0xd1, 0x54, 0x6a, 0x52, 0x8d, 0x19, 0xb6, - 0xf5, 0x3f, 0x05, 0x3e, 0x49, 0x2d, 0x63, 0x80, 0xbc, 0x9f, 0x7b, 0x19, 0x13, 0x77, 0x91, 0x8b, - 0x54, 0xfe, 0x73, 0x6f, 0x9f, 0xff, 0x3f, 0x44, 0xb0, 0x9a, 0xe4, 0x5f, 0xa3, 0x3d, 0xd3, 0x1e, - 0xa8, 0xd4, 0xe9, 0x1b, 0x1a, 0xbd, 0xf4, 0xb9, 0xff, 0x4b, 0x04, 0x1b, 0xa9, 0xdc, 0x09, 0x23, - 0x67, 0xc4, 0xa5, 0xd1, 0xbb, 0xed, 0xd2, 0xa7, 0xff, 0xc3, 0x02, 0xb8, 0x3f, 0x9e, 0xbe, 0x64, - 0x5b, 0xe7, 0x46, 0xc7, 0x73, 0xe8, 0x49, 0xeb, 0x58, 0xbd, 0xf4, 0x35, 0xf8, 0x59, 0x01, 0xec, - 0xa7, 0xb7, 0xbf, 0xfb, 0x8a, 0xd9, 0xbd, 0xaa, 0xc6, 0x8c, 0x3e, 0xad, 0x19, 0x0e, 0xd5, 0x98, - 0xed, 0x0c, 0x5a, 0xb6, 0x6d, 0xba, 0x75, 0xcb, 0x65, 0xc4, 0x34, 0x2f, 0x7d, 0x41, 0x5e, 0x17, - 0xc0, 0x5e, 0x56, 0x41, 0xe2, 0x2d, 0x72, 0xe9, 0xab, 0xf1, 0xc7, 0x02, 0x78, 0x98, 0x54, 0xa3, - 0xea, 0x31, 0x3b, 0xfa, 0x9b, 0xea, 0x51, 0x6c, 0xf7, 0xb2, 0x57, 0x01, 0x3e, 0x02, 0x4b, 0x91, - 0x43, 0x17, 0x6b, 0xb6, 0x67, 0xb1, 0xd2, 0x95, 0x4d, 0x71, 0x7b, 0x0e, 0x2d, 0xc6, 0xb7, 0x25, - 0xff, 0x6e, 0xf9, 0x27, 0x05, 0xb0, 0x33, 0xe3, 0x8d, 0x52, 0xaf, 0x9e, 0x34, 0x6d, 0xd3, 0xd0, - 0x06, 0x97, 0x7e, 0xdf, 0x7c, 0x29, 0x82, 0x72, 0x52, 0x08, 0xff, 0xa8, 0xa8, 0x19, 0x3d, 0x62, - 0xba, 0x5f, 0x9d, 0x27, 0xe7, 0x9f, 0xa2, 0x7f, 0xdc, 0x8e, 0x0c, 0x5a, 0xd4, 0x65, 0x92, 0x6d, - 0x59, 0x5f, 0x91, 0x43, 0xd5, 0xdf, 0x45, 0x50, 0x4a, 0x92, 0x8f, 0x84, 0xa0, 0x7e, 0xe9, 0xf3, - 0x5e, 0x01, 0xcb, 0xed, 0xba, 0xe4, 0x50, 0xc2, 0xe8, 0x29, 0xbd, 0x40, 0xb6, 0x99, 0x16, 0xab, - 0x1b, 0xfe, 0x86, 0x18, 0x1a, 0x54, 0x49, 0x3f, 0x6d, 0xb0, 0xe5, 0x9f, 0xc6, 0x86, 0x67, 0x13, - 0x4b, 0xa3, 0x66, 0xca, 0xe4, 0x09, 0xd8, 0x1d, 0x31, 0x79, 0x61, 0xd0, 0x8b, 0x9a, 0xad, 0x79, - 0x5d, 0x6a, 0x31, 0xe2, 0x6f, 0xae, 0x94, 0xf5, 0x97, 0x2b, 0x60, 0xa9, 0xed, 0xc3, 0xf3, 0xcb, - 0x86, 0x45, 0x1b, 0xe7, 0xb0, 0x0d, 0x96, 0x3c, 0x03, 0x9f, 0x71, 0x39, 0x8f, 0x35, 0xdf, 0x36, - 0x5c, 0x84, 0xe9, 0x09, 0x8f, 0xab, 0x7f, 0x45, 0x40, 0x37, 0x3d, 0x23, 0x75, 0x17, 0xfe, 0x4a, - 0x04, 0x3b, 0x9e, 0x81, 0xed, 0x40, 0x1e, 0x63, 0x2d, 0x5c, 0x72, 0xdc, 0xb1, 0x31, 0xb3, 0xb1, - 0x1e, 0xc9, 0xff, 0x30, 0x62, 0x50, 0xe2, 0xea, 0x8c, 0x88, 0xf9, 0x7a, 0x08, 0x8a, 0x80, 0xee, - 0x79, 0x46, 0xa6, 0x2d, 0x7c, 0x2d, 0x82, 0x7b, 0x29, 0x3a, 0xa2, 0xeb, 0xf8, 0xdc, 0x70, 0x5c, - 0x86, 0xa3, 0xe5, 0x0f, 0xb9, 0xae, 0x70, 0xae, 0x4f, 0xb2, 0xb9, 0xa6, 0xf7, 0x24, 0x14, 0x01, - 0xad, 0xc7, 0x48, 0x13, 0xcd, 0x46, 0x6b, 0x35, 0x81, 0xc6, 0x24, 0x2c, 0x5e, 0x9d, 0xab, 0x79, - 0x6b, 0x95, 0xd1, 0x00, 0x19, 0xaa, 0xd5, 0x74, 0x5b, 0xf8, 0x63, 0x11, 0x6c, 0xa6, 0xe8, 0x5c, - 0xca, 0xb0, 0x16, 0xf7, 0x4a, 0xb0, 0xcb, 0xfb, 0x1c, 0xa5, 0x79, 0x0e, 0xf5, 0xcd, 0x6c, 0xa8, - 0x69, 0x9d, 0x16, 0x45, 0x40, 0xab, 0x31, 0xcd, 0x04, 0x23, 0xf8, 0x73, 0x11, 0xdc, 0x4f, 0x61, - 0x38, 0xa1, 0x28, 0xc1, 0x5a, 0xd4, 0x71, 0x89, 0x50, 0xae, 0x71, 0x94, 0x6f, 0x67, 0xa3, 0xcc, - 0xea, 0xd9, 0x28, 0x02, 0xda, 0x8c, 0x71, 0xa6, 0x18, 0x46, 0x95, 0x71, 0xc2, 0x2e, 0x08, 0xd6, - 0x6c, 0x9d, 0x9f, 0x07, 0x82, 0x2e, 0x4c, 0xb8, 0x5c, 0x0b, 0x99, 0x95, 0xc9, 0xe8, 0xe1, 0x04, - 0x95, 0x99, 0x6e, 0x04, 0xbf, 0x07, 0x56, 0x27, 0x51, 0xf4, 0x06, 0x21, 0xc1, 0x75, 0x4e, 0xf0, - 0xb5, 0xfc, 0x04, 0xe9, 0x26, 0x90, 0x22, 0xa0, 0xd2, 0x58, 0xf4, 0xd0, 0x00, 0x7e, 0x1f, 0xac, - 0x8d, 0x47, 0xee, 0x39, 0x86, 0xc5, 0xc2, 0xd0, 0x80, 0x87, 0xfe, 0x7a, 0xde, 0xd0, 0x23, 0x2d, - 0x24, 0x45, 0x40, 0xcb, 0x23, 0xb1, 0x13, 0x0b, 0x68, 0x82, 0x65, 0xcf, 0xc0, 0x7a, 0xf8, 0xf2, - 0xc5, 0x6e, 0xd0, 0x50, 0xc1, 0xdc, 0x79, 0xe9, 0x06, 0x0f, 0x5c, 0x99, 0x11, 0x78, 0x52, 0x23, - 0x46, 0x11, 0xd0, 0x6d, 0xcf, 0x98, 0xd8, 0xa2, 0x79, 0x1d, 0x6c, 0xbf, 0x38, 0x5c, 0xfc, 0x68, - 0xba, 0x51, 0x67, 0x24, 0x8c, 0xfc, 0x0e, 0x8f, 0xfc, 0x8d, 0x1c, 0x91, 0x27, 0xf7, 0x56, 0x82, - 0x9d, 0x97, 0xd1, 0x7f, 0xf9, 0x82, 0x6f, 0xbc, 0x18, 0x46, 0xe7, 0x5d, 0x0a, 0xec, 0x06, 0x6d, - 0x8a, 0x10, 0xe4, 0x26, 0x07, 0xf9, 0xff, 0x1c, 0x20, 0xe3, 0x4d, 0x8e, 0x60, 0xcf, 0xcd, 0x68, - 0x82, 0xfc, 0x34, 0x78, 0x81, 0x26, 0x04, 0xe1, 0xd1, 0x36, 0x79, 0x2e, 0x03, 0x88, 0x45, 0x0e, - 0xf1, 0x51, 0x1e, 0x88, 0x49, 0xdd, 0x06, 0x45, 0x40, 0x1b, 0x29, 0x8e, 0x89, 0x0d, 0x89, 0x5f, - 0x06, 0x6f, 0xcf, 0x71, 0x14, 0x2d, 0x3a, 0x5b, 0xe2, 0x2e, 0x33, 0xdd, 0x10, 0x68, 0x89, 0x03, - 0x7d, 0xeb, 0xbf, 0x00, 0x1a, 0xd7, 0xff, 0x8a, 0x80, 0xee, 0x8f, 0x53, 0x25, 0x76, 0xcc, 0x0c, - 0x25, 0xd0, 0x9f, 0x45, 0xf0, 0xd1, 0xf0, 0x3a, 0x71, 0xf5, 0x88, 0x09, 0x97, 0x8f, 0x58, 0x8f, - 0xf4, 0x23, 0x66, 0xbe, 0xa2, 0xc6, 0x46, 0x20, 0xa9, 0x43, 0xd2, 0x22, 0x27, 0xfd, 0x34, 0xd7, - 0xfa, 0xe5, 0x52, 0xe9, 0x8a, 0x80, 0xf6, 0xd3, 0x8b, 0x9a, 0x4f, 0xda, 0xff, 0x49, 0x04, 0x1f, - 0xe6, 0xca, 0x21, 0x29, 0x77, 0xc0, 0xff, 0x2e, 0xe7, 0x3f, 0x7a, 0x6b, 0xfe, 0x61, 0x69, 0xa0, - 0x08, 0x68, 0x2f, 0x0b, 0x7e, 0x44, 0x4c, 0xfc, 0x5a, 0x04, 0x8f, 0xd3, 0xe4, 0xc4, 0xf3, 0x4f, - 0x1e, 0xb1, 0x5c, 0xc5, 0x89, 0x84, 0x0b, 0x80, 0x21, 0x07, 0x7e, 0x96, 0x03, 0x78, 0x96, 0xee, - 0x55, 0x04, 0xf4, 0x30, 0x01, 0x9d, 0xa9, 0x90, 0x7f, 0x2f, 0x82, 0x4a, 0xc6, 0xce, 0x35, 0x48, - 0x17, 0xf7, 0xb8, 0x44, 0x0c, 0x21, 0xff, 0x8f, 0x43, 0x1e, 0xbc, 0xcd, 0xfe, 0x1d, 0x56, 0x9b, - 0x8a, 0x80, 0x76, 0x66, 0x6c, 0xe2, 0x3a, 0xe9, 0xa6, 0xa5, 0xe9, 0x2f, 0x44, 0xf0, 0x30, 0x8d, - 0xda, 0x8b, 0x15, 0xdc, 0xd8, 0xba, 0xdf, 0xe2, 0x84, 0x1f, 0xe7, 0x20, 0x9c, 0x26, 0x03, 0x15, - 0x01, 0x95, 0x13, 0xb4, 0xa9, 0x62, 0xf1, 0x47, 0x22, 0xd8, 0x4a, 0x33, 0x31, 0xea, 0x32, 0x9f, - 0xc6, 0x1a, 0x7a, 0x1f, 0xbf, 0x97, 0xf9, 0xf5, 0x9b, 0xa1, 0xc9, 0x14, 0x01, 0xad, 0x25, 0x24, - 0x93, 0x44, 0x9b, 0x03, 0x56, 0xd2, 0x0c, 0xd1, 0x39, 0x37, 0xfa, 0x0e, 0xdd, 0xce, 0x10, 0x22, - 0xd3, 0x44, 0x51, 0xf0, 0xd9, 0x9d, 0x22, 0x98, 0x4c, 0x50, 0xf2, 0x0c, 0xff, 0x10, 0x46, 0x18, - 0xc5, 0x16, 0xbd, 0xc0, 0x8e, 0x6d, 0x46, 0xc7, 0x8d, 0x3b, 0x3c, 0xe0, 0xd3, 0x19, 0x01, 0xa7, - 0xc8, 0x11, 0x45, 0x40, 0xb7, 0x3c, 0x63, 0x7c, 0x10, 0x0e, 0xf8, 0x47, 0x7e, 0x34, 0x9a, 0x4b, - 0xfa, 0x51, 0xc8, 0x52, 0x66, 0x85, 0x67, 0x88, 0x9c, 0x20, 0xd1, 0xc9, 0x06, 0xf0, 0x0b, 0xb0, - 0x31, 0x29, 0x51, 0x2e, 0x82, 0xc2, 0xe0, 0xcb, 0x99, 0x1f, 0x98, 0x99, 0x02, 0x4a, 0x11, 0xd0, - 0xdd, 0xd1, 0xac, 0x13, 0x13, 0xf8, 0x9b, 0xe0, 0x15, 0x32, 0x4a, 0xd0, 0x37, 0xe8, 0x05, 0xd6, - 0xd3, 0x22, 0x2b, 0xa4, 0xb9, 0xcb, 0x69, 0xa4, 0xbc, 0x34, 0x33, 0xb4, 0x9a, 0x22, 0xa0, 0x07, - 0x23, 0x60, 0x93, 0xad, 0x0f, 0xae, 0x81, 0xab, 0x3c, 0xc0, 0xa7, 0x57, 0x16, 0x0a, 0xc5, 0x39, - 0xff, 0xbb, 0x1d, 0x1f, 0x92, 0x3b, 0x94, 0xc5, 0xe7, 0x22, 0x0e, 0xb8, 0xfb, 0x87, 0xf9, 0xe4, - 0x3f, 0x6f, 0xd1, 0xdb, 0x08, 0x6e, 0x81, 0xb5, 0x5a, 0x5d, 0x95, 0x1a, 0x2f, 0x64, 0x84, 0x91, - 0xac, 0x36, 0xda, 0x48, 0x92, 0x71, 0xfb, 0x54, 0x6d, 0xca, 0x52, 0xfd, 0xb0, 0x2e, 0xd7, 0x8a, - 0x02, 0x5c, 0x05, 0xa5, 0x71, 0x13, 0x55, 0x46, 0x2f, 0x64, 0x54, 0x14, 0xe1, 0x26, 0x58, 0x1d, - 0x1f, 0x7d, 0xde, 0x3e, 0x90, 0xd1, 0xa9, 0xdc, 0x92, 0xd5, 0x62, 0x01, 0x7e, 0x00, 0x2a, 0xe3, - 0x16, 0xb5, 0x6a, 0xab, 0x7a, 0x50, 0x55, 0x65, 0xdc, 0x6c, 0xa8, 0xad, 0x23, 0x24, 0xab, 0x58, - 0x95, 0x8f, 0x0f, 0xb1, 0xd2, 0x50, 0x5b, 0x72, 0xad, 0x38, 0x07, 0xdf, 0x07, 0x4f, 0x66, 0x4c, - 0x3a, 0xf9, 0x8e, 0xfa, 0xd9, 0xf1, 0xd0, 0x8c, 0x2b, 0xf0, 0x29, 0xd8, 0x9b, 0x35, 0xa3, 0x71, - 0x7a, 0xd4, 0xa8, 0x1d, 0x0c, 0xcd, 0xb9, 0x0a, 0x1f, 0x83, 0x47, 0x79, 0xd0, 0x50, 0x4d, 0x2d, - 0xce, 0xc3, 0x6d, 0x70, 0x3f, 0x13, 0xc9, 0xb7, 0xbc, 0x06, 0x1f, 0x82, 0xf2, 0xb8, 0x65, 0xb5, - 0xd9, 0x3c, 0xae, 0x4b, 0xd5, 0x56, 0xbd, 0x71, 0x8a, 0x95, 0x56, 0xab, 0x59, 0x5c, 0x80, 0x0f, - 0xc0, 0xd6, 0x6c, 0xbb, 0x96, 0xd4, 0x2c, 0x5e, 0x9f, 0x6c, 0xf6, 0x79, 0xfd, 0xb4, 0xd6, 0xf8, - 0x5c, 0xc5, 0x35, 0x59, 0x7d, 0xde, 0x6a, 0x34, 0x8b, 0x00, 0x3e, 0x01, 0xdb, 0x33, 0xf8, 0xd4, - 0xcf, 0x8e, 0x83, 0x35, 0xe3, 0x8c, 0x37, 0x32, 0x0a, 0x9c, 0xa4, 0x2e, 0xd7, 0x54, 0xa5, 0x7e, - 0xd8, 0x2a, 0xbe, 0x03, 0x3f, 0x04, 0xef, 0xe7, 0xf2, 0x9f, 0x2e, 0xf1, 0xcd, 0x8c, 0x38, 0x48, - 0xae, 0xd5, 0x87, 0x97, 0x7e, 0x31, 0xef, 0xa2, 0x1c, 0x49, 0xcd, 0xe2, 0x52, 0xae, 0x45, 0xf1, - 0x2d, 0x8b, 0xb9, 0xcb, 0xe3, 0x5b, 0xbf, 0xbb, 0xfb, 0x5b, 0x11, 0x2c, 0x0e, 0xff, 0x8b, 0x17, - 0x6e, 0x80, 0x95, 0xd8, 0x81, 0xda, 0xaa, 0xb6, 0xda, 0xea, 0xc8, 0x83, 0xb2, 0x02, 0xee, 0x8c, - 0x1a, 0xa8, 0x6d, 0x49, 0x92, 0x55, 0xb5, 0x28, 0x4e, 0x1c, 0x7c, 0x5e, 0x6f, 0x36, 0xe5, 0x5a, - 0xb1, 0x00, 0x97, 0xc1, 0x7b, 0xa3, 0x83, 0x32, 0x42, 0x0d, 0x54, 0x9c, 0x9b, 0x34, 0xaf, 0x7a, - 0xd0, 0x40, 0x7c, 0xcf, 0x1f, 0x34, 0xfe, 0xf2, 0x66, 0x5d, 0xfc, 0xeb, 0x9b, 0x75, 0xf1, 0x6f, - 0x6f, 0xd6, 0xc5, 0xef, 0x56, 0x3b, 0x06, 0x7b, 0xe9, 0x9d, 0xed, 0x69, 0x76, 0xb7, 0xd2, 0x71, - 0x48, 0xdf, 0x08, 0x5e, 0x15, 0xc4, 0xac, 0xc4, 0xbf, 0xfc, 0x20, 0x3d, 0xa3, 0xd2, 0xa1, 0x56, - 0x85, 0xff, 0xcc, 0xa3, 0xd2, 0xb1, 0x47, 0x7e, 0x0a, 0x72, 0x36, 0xcf, 0x07, 0x3e, 0xf8, 0x4f, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x0c, 0x1f, 0xda, 0x74, 0x2c, 0x22, 0x00, 0x00, + // 2201 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcb, 0x6f, 0xdb, 0xc8, + 0xfd, 0x17, 0xe5, 0x3c, 0x9c, 0xc9, 0xc6, 0xe1, 0xce, 0xe6, 0x21, 0xc7, 0x6f, 0xe5, 0x69, 0x27, + 0x3f, 0x7b, 0xf3, 0xf8, 0x6d, 0xd2, 0x66, 0xdb, 0x80, 0x22, 0x69, 0x93, 0x6b, 0xd9, 0xd4, 0x0e, + 0xa9, 0xa4, 0xde, 0xcb, 0x80, 0x96, 0xc6, 0x0a, 0x11, 0x8a, 0x14, 0xc8, 0x91, 0x5c, 0xa3, 0x28, + 0x16, 0x2d, 0x8a, 0xa2, 0x40, 0x80, 0xa2, 0xbd, 0x15, 0x28, 0x50, 0xa0, 0x68, 0x0f, 0x3d, 0xf4, + 0xde, 0x73, 0x6f, 0x3d, 0xf6, 0x4f, 0x28, 0x72, 0xef, 0xb1, 0x87, 0x16, 0x2d, 0xb6, 0xd0, 0xf0, + 0xa9, 0x27, 0xb9, 0x41, 0x4f, 0xde, 0x9b, 0xc9, 0xf9, 0x3e, 0x3e, 0xdf, 0x07, 0x67, 0xe6, 0xf3, + 0xb5, 0xc0, 0x3a, 0x25, 0x36, 0xe9, 0xb8, 0x1e, 0xdd, 0xea, 0xfa, 0x66, 0x8b, 0x90, 0x1e, 0x71, + 0xa8, 0xbf, 0xd5, 0x7b, 0x98, 0x7e, 0xdc, 0xec, 0x78, 0x2e, 0x75, 0xe1, 0xf5, 0x48, 0x74, 0x33, + 0xbd, 0xd6, 0x7b, 0x58, 0xde, 0x00, 0xb0, 0xae, 0x56, 0x4c, 0xc7, 0x21, 0x9e, 0x68, 0x5b, 0x8d, + 0x37, 0x72, 0x7f, 0x05, 0x5e, 0x01, 0x67, 0x4d, 0x9b, 0x78, 0xb4, 0xc4, 0xad, 0x72, 0xf7, 0x2e, + 0xa0, 0xe0, 0xa1, 0xbc, 0x0d, 0xee, 0xd5, 0x55, 0xcd, 0x39, 0x74, 0x4d, 0xaf, 0x29, 0xba, 0xed, + 0x8e, 0x4d, 0x28, 0xd9, 0x71, 0x0d, 0x57, 0x32, 0xfd, 0xd7, 0xc1, 0xcb, 0xc4, 0xc2, 0x0d, 0x30, + 0xdb, 0xf5, 0x89, 0xe7, 0x98, 0x6d, 0x12, 0x1a, 0x89, 0x9f, 0xcb, 0xb7, 0xc1, 0xcd, 0xd8, 0x8e, + 0xd0, 0x6c, 0x6e, 0x5b, 0x9e, 0x4f, 0x11, 0xf1, 0xdd, 0xae, 0xd7, 0x20, 0x89, 0x89, 0xf2, 0x46, + 0xca, 0xdd, 0xb0, 0x58, 0xd5, 0xa4, 0x69, 0xc0, 0xe5, 0x17, 0x60, 0x2d, 0x96, 0xd5, 0x09, 0x15, + 0x3d, 0xd2, 0x24, 0x0e, 0xb5, 0x4c, 0x5b, 0xef, 0x1e, 0xb6, 0x2d, 0x9a, 0x8d, 0xe9, 0x87, 0xe0, + 0x76, 0x6c, 0x00, 0x91, 0x96, 0xe5, 0xf7, 0xed, 0xbf, 0x36, 0x6d, 0x9b, 0x38, 0x2d, 0x92, 0xd3, + 0x08, 0x9c, 0x07, 0xb3, 0xed, 0x23, 0x13, 0xd3, 0x93, 0x0e, 0x29, 0x15, 0xd9, 0xda, 0xf9, 0xf6, + 0x91, 0x69, 0x9c, 0x74, 0x08, 0x5c, 0x02, 0xc0, 0x76, 0x5b, 0x96, 0x83, 0x8f, 0x6c, 0xf7, 0xb8, + 0x34, 0xc3, 0x16, 0x2f, 0xb0, 0x37, 0xdb, 0xb6, 0x7b, 0x1c, 0xe0, 0x47, 0xa4, 0xe1, 0xf6, 0x88, + 0x77, 0x22, 0xba, 0x4d, 0xe2, 0x8b, 0xae, 0x43, 0x2d, 0xa7, 0x4b, 0x72, 0xe6, 0xf4, 0x39, 0x58, + 0x1a, 0x31, 0xd0, 0x39, 0xc9, 0xa9, 0xfc, 0x29, 0x58, 0x1e, 0x52, 0xae, 0x79, 0x96, 0x43, 0x73, + 0x6a, 0x97, 0x01, 0x2f, 0x59, 0x3e, 0x53, 0xde, 0x23, 0xd4, 0x6c, 0x9a, 0xd4, 0x84, 0x73, 0xa0, + 0x68, 0x35, 0x43, 0xc9, 0xa2, 0xd5, 0x2c, 0x9b, 0xa0, 0x14, 0xc9, 0x44, 0x25, 0x8c, 0x65, 0x65, + 0x30, 0xeb, 0x85, 0xef, 0x98, 0xc6, 0xdc, 0xa3, 0xf5, 0xcd, 0x09, 0xed, 0xba, 0x39, 0x6c, 0x04, + 0xc5, 0xaa, 0xe5, 0x37, 0x00, 0x46, 0xab, 0x3a, 0x25, 0x1d, 0x9d, 0x9a, 0xb4, 0xeb, 0xc3, 0x17, + 0xe0, 0x9c, 0xcf, 0xfe, 0x0a, 0x4d, 0xdf, 0xcd, 0x34, 0x1d, 0x28, 0xa2, 0x50, 0xad, 0xff, 0x29, + 0x10, 0xcf, 0x73, 0xbd, 0xb0, 0xa0, 0xc1, 0x43, 0xf9, 0xf7, 0x1c, 0xb8, 0x56, 0x57, 0x53, 0x2a, + 0x1e, 0x25, 0xcd, 0x20, 0x55, 0x32, 0x98, 0x6d, 0x87, 0xa1, 0x31, 0x9f, 0x17, 0x73, 0x84, 0x13, + 0xe5, 0x02, 0xc5, 0xaa, 0x50, 0x8c, 0x81, 0x17, 0x99, 0x91, 0xfb, 0x39, 0x80, 0x47, 0x51, 0x47, + 0xe0, 0xcb, 0xff, 0xe1, 0xc0, 0x6a, 0x02, 0x33, 0x4a, 0x9a, 0x4e, 0x6c, 0xd2, 0xa0, 0x96, 0xeb, + 0xfc, 0x4f, 0x01, 0xef, 0xa5, 0xca, 0x18, 0x40, 0x7e, 0x98, 0xbb, 0x8c, 0x89, 0xb9, 0xc8, 0x44, + 0x2a, 0xfe, 0x99, 0xf7, 0x8f, 0xff, 0x9f, 0x1c, 0x58, 0x4c, 0xe2, 0x97, 0x48, 0xc7, 0x76, 0x4f, + 0x74, 0xe2, 0xf5, 0xac, 0x06, 0x39, 0xf5, 0xb1, 0xff, 0x9b, 0x03, 0x2b, 0xa9, 0xd8, 0x4d, 0x6a, + 0x1e, 0x9a, 0x3e, 0x89, 0xf6, 0xb6, 0x53, 0x1f, 0xfe, 0x8f, 0x8a, 0xe0, 0xd6, 0x68, 0xf8, 0xa2, + 0xeb, 0x1c, 0x59, 0xad, 0xae, 0x47, 0xf6, 0x8c, 0xaa, 0x7e, 0xea, 0x73, 0xf0, 0xf3, 0x22, 0x78, + 0x98, 0x6e, 0x7f, 0xff, 0x0d, 0x75, 0x3b, 0x42, 0x83, 0x5a, 0x3d, 0x22, 0x59, 0x1e, 0x69, 0x50, + 0xd7, 0x3b, 0x31, 0x5c, 0xd7, 0xf6, 0x55, 0xc7, 0xa7, 0xa6, 0x6d, 0x9f, 0xfa, 0x84, 0xbc, 0x2d, + 0x82, 0xcd, 0xac, 0x84, 0xc4, 0x2d, 0x72, 0xea, 0xb3, 0xf1, 0xc7, 0x22, 0xb8, 0x93, 0x64, 0x43, + 0xe8, 0x52, 0x37, 0xfa, 0x9b, 0x34, 0x23, 0xdf, 0xfe, 0x69, 0xcf, 0x02, 0xbc, 0x0b, 0x2e, 0x47, + 0x06, 0x7d, 0xdc, 0x70, 0xbb, 0x0e, 0x2d, 0x9d, 0x59, 0xe5, 0xee, 0xcd, 0xa0, 0xb9, 0xf8, 0xb5, + 0xd8, 0x7f, 0x5b, 0xfe, 0x69, 0x11, 0xac, 0x4f, 0xd9, 0x51, 0x54, 0x61, 0xaf, 0xe6, 0xda, 0x56, + 0xe3, 0xe4, 0xd4, 0xf7, 0xcd, 0x57, 0x1c, 0x28, 0x27, 0x89, 0xe8, 0x5f, 0x15, 0x1b, 0x56, 0xc7, + 0xb4, 0xfd, 0x6f, 0xce, 0x97, 0xf3, 0x2f, 0xae, 0x7f, 0xdd, 0x8e, 0x04, 0x0c, 0xe2, 0x53, 0xd1, + 0x75, 0x9c, 0x6f, 0xc8, 0xa5, 0xea, 0x1f, 0x1c, 0x28, 0x25, 0xc1, 0x47, 0x44, 0xb0, 0x79, 0xea, + 0xe3, 0x5e, 0x00, 0xf3, 0x75, 0x55, 0xf4, 0x88, 0x49, 0xc9, 0x3e, 0x39, 0x46, 0xae, 0x9d, 0x26, + 0xab, 0x2b, 0xfd, 0x86, 0x18, 0x58, 0xd4, 0xcd, 0x5e, 0x5a, 0x60, 0xad, 0x7f, 0x1b, 0x1b, 0xd4, + 0x36, 0x9d, 0x06, 0xb1, 0x53, 0x22, 0x0f, 0xc0, 0xc6, 0x90, 0xc8, 0x4b, 0x8b, 0x1c, 0x4b, 0x6e, + 0xa3, 0xdb, 0x26, 0x0e, 0x35, 0xfb, 0xcd, 0x95, 0x92, 0xfe, 0x6a, 0x01, 0x5c, 0xae, 0xf7, 0xc1, + 0xb3, 0x47, 0xcd, 0x21, 0xda, 0x11, 0xac, 0x83, 0xcb, 0x5d, 0x0b, 0x1f, 0x32, 0x3a, 0x8f, 0x1b, + 0x7d, 0xd9, 0xb0, 0x08, 0x93, 0x03, 0x1e, 0x65, 0xff, 0x4a, 0x01, 0x5d, 0xea, 0x5a, 0xa9, 0xb7, + 0xf0, 0xd7, 0x1c, 0x58, 0xef, 0x5a, 0xd8, 0x0d, 0xe8, 0x31, 0x6e, 0x84, 0x25, 0xc7, 0x2d, 0x17, + 0x53, 0x17, 0x37, 0x23, 0xfa, 0x1f, 0x7a, 0x0c, 0x52, 0x2c, 0x4c, 0xf1, 0x98, 0x6f, 0x86, 0xa0, + 0x14, 0xd0, 0xcd, 0xae, 0x95, 0x29, 0x0b, 0xdf, 0x72, 0xe0, 0x66, 0x0a, 0x9d, 0xd9, 0x6c, 0xe2, + 0x23, 0xcb, 0xf3, 0x29, 0x8e, 0xca, 0x1f, 0xe2, 0x3a, 0xc3, 0x70, 0x7d, 0x9a, 0x8d, 0x6b, 0xf2, + 0x4c, 0x42, 0x29, 0xa0, 0xe5, 0x18, 0xd2, 0x58, 0xb1, 0xe1, 0x5c, 0x8d, 0x41, 0x63, 0x9b, 0x34, + 0xae, 0xce, 0xd9, 0xbc, 0xb9, 0xca, 0x18, 0x80, 0x0c, 0xe4, 0x6a, 0xb2, 0x2c, 0xfc, 0x09, 0x07, + 0x56, 0x53, 0xe8, 0x7c, 0x42, 0x71, 0x23, 0x9e, 0x95, 0x60, 0x9f, 0xcd, 0x39, 0x4a, 0xe7, 0x18, + 0xa8, 0x6f, 0x67, 0x83, 0x9a, 0x34, 0x69, 0x51, 0x0a, 0x68, 0x31, 0x46, 0x33, 0x46, 0x08, 0xfe, + 0x82, 0x03, 0xb7, 0x52, 0x30, 0xbc, 0x90, 0x94, 0xe0, 0x46, 0x34, 0x71, 0x89, 0xa0, 0x9c, 0x67, + 0x50, 0xbe, 0x9b, 0x0d, 0x65, 0xda, 0xcc, 0x46, 0x29, 0xa0, 0xd5, 0x18, 0xce, 0x04, 0xc1, 0x28, + 0x33, 0x5e, 0x38, 0x05, 0xc1, 0x0d, 0xb7, 0xc9, 0xee, 0x03, 0xc1, 0x14, 0x26, 0x2c, 0xd7, 0x6c, + 0x66, 0x66, 0x32, 0x66, 0x38, 0x41, 0x66, 0x26, 0x0b, 0xc1, 0xef, 0x83, 0xc5, 0x71, 0x28, 0x3a, + 0x27, 0x21, 0x82, 0x0b, 0x0c, 0xc1, 0x27, 0xf9, 0x11, 0xa4, 0x87, 0x40, 0x4a, 0x01, 0x95, 0x46, + 0xbc, 0x87, 0x02, 0xf0, 0x07, 0x60, 0x69, 0xd4, 0x73, 0xc7, 0xb3, 0x1c, 0x1a, 0xba, 0x06, 0xcc, + 0xf5, 0xd3, 0xbc, 0xae, 0x87, 0x46, 0x48, 0x4a, 0x01, 0xcd, 0x0f, 0xf9, 0x4e, 0x24, 0xa0, 0x0d, + 0xe6, 0xbb, 0x16, 0x6e, 0x86, 0x9b, 0x2f, 0xf6, 0x83, 0x81, 0x0a, 0x66, 0xc6, 0x4b, 0x17, 0x99, + 0xe3, 0xad, 0x29, 0x8e, 0xc7, 0x0d, 0x62, 0x94, 0x02, 0xba, 0xd6, 0xb5, 0xc6, 0x8e, 0x68, 0xde, + 0x06, 0xed, 0x17, 0xbb, 0x8b, 0x3f, 0x4d, 0x3f, 0x9a, 0x8c, 0x84, 0x9e, 0x3f, 0x60, 0x9e, 0xbf, + 0x95, 0xc3, 0xf3, 0xf8, 0xd9, 0x4a, 0xd0, 0x79, 0x19, 0xf3, 0x97, 0x2f, 0x59, 0xe3, 0xc5, 0x60, + 0x9a, 0x6c, 0x4a, 0x81, 0xfd, 0x60, 0x4c, 0x11, 0x02, 0xb9, 0xc4, 0x80, 0xfc, 0x7f, 0x0e, 0x20, + 0xa3, 0x43, 0x8e, 0xa0, 0xe7, 0xa6, 0x0c, 0x41, 0x7e, 0x16, 0x6c, 0xa0, 0x09, 0x82, 0xf0, 0x6a, + 0x9b, 0x7c, 0x97, 0x01, 0x88, 0x39, 0x06, 0xe2, 0x59, 0x1e, 0x10, 0xe3, 0xa6, 0x0d, 0x4a, 0x01, + 0xad, 0xa4, 0x70, 0x8c, 0x1d, 0x48, 0xfc, 0x2a, 0xd8, 0x3d, 0x47, 0xa1, 0x34, 0xa2, 0xbb, 0x25, + 0x6e, 0x53, 0xdb, 0x0f, 0x01, 0x5d, 0x66, 0x80, 0xbe, 0xf3, 0x35, 0x00, 0x8d, 0xf2, 0x7f, 0xa5, + 0x80, 0x6e, 0x8d, 0xa2, 0x4a, 0xe4, 0xa8, 0x1d, 0x52, 0xa0, 0x3f, 0x73, 0xe0, 0xd9, 0x60, 0x9d, + 0x18, 0x7b, 0xc4, 0x26, 0xa3, 0x8f, 0xb8, 0x19, 0xf1, 0x47, 0x4c, 0xfb, 0x8c, 0x1a, 0x5b, 0x01, + 0xa5, 0x0e, 0x91, 0xf2, 0x0c, 0xe9, 0x67, 0xb9, 0xea, 0x97, 0x8b, 0xa5, 0x2b, 0x05, 0xf4, 0x30, + 0x5d, 0xd4, 0x7c, 0xd4, 0xfe, 0x4f, 0x1c, 0x78, 0x92, 0x2b, 0x86, 0x24, 0xdd, 0x01, 0xfe, 0x0f, + 0x19, 0xfe, 0x9d, 0xf7, 0xc6, 0x3f, 0x48, 0x0d, 0x94, 0x02, 0xda, 0xcc, 0x02, 0x3f, 0x44, 0x26, + 0x7e, 0xc3, 0x81, 0xfb, 0x69, 0xe4, 0x66, 0xb7, 0x7f, 0xf3, 0x88, 0xe9, 0x2a, 0x4e, 0x28, 0x5c, + 0x00, 0x18, 0x32, 0xc0, 0x2f, 0x72, 0x00, 0x9e, 0xc6, 0x7b, 0x95, 0x02, 0xba, 0x93, 0x00, 0x9d, + 0xca, 0x90, 0xff, 0xc0, 0x81, 0xad, 0x8c, 0xce, 0xb5, 0xcc, 0x36, 0xee, 0x30, 0x8a, 0x18, 0x82, + 0xfc, 0x88, 0x81, 0xac, 0xbc, 0x4f, 0xff, 0x0e, 0xb2, 0x4d, 0xa5, 0x80, 0xd6, 0xa7, 0x34, 0xb1, + 0x6a, 0xb6, 0xd3, 0xd4, 0xf4, 0x97, 0x1c, 0xb8, 0x93, 0x86, 0xda, 0x89, 0x19, 0xdc, 0x48, 0xdd, + 0xaf, 0x30, 0x84, 0xcf, 0x73, 0x20, 0x9c, 0x44, 0x03, 0x95, 0x02, 0x2a, 0x27, 0xd0, 0x26, 0x92, + 0xc5, 0x1f, 0x73, 0x60, 0x2d, 0x8d, 0x89, 0x12, 0x9f, 0xf6, 0xd1, 0x38, 0x03, 0xfb, 0xf1, 0xd5, + 0xcc, 0xd3, 0x6f, 0x0a, 0x27, 0x53, 0x0a, 0x68, 0x29, 0x41, 0x32, 0x8e, 0xb4, 0x79, 0x60, 0x21, + 0x8d, 0x21, 0xba, 0xe7, 0x46, 0xe7, 0xd0, 0xb5, 0x0c, 0x22, 0x32, 0x89, 0x14, 0x05, 0xc7, 0xee, + 0x04, 0xc2, 0x64, 0x83, 0x52, 0xd7, 0xea, 0x5f, 0xc2, 0x4c, 0x4a, 0xb0, 0x43, 0x8e, 0xb1, 0xe7, + 0xda, 0xd1, 0x75, 0xe3, 0x3a, 0x73, 0xf8, 0x68, 0x8a, 0xc3, 0x09, 0x74, 0x44, 0x29, 0xa0, 0x2b, + 0x5d, 0x6b, 0x74, 0x11, 0x9e, 0xb0, 0x43, 0x7e, 0xd8, 0x9b, 0x6f, 0xf6, 0x22, 0x97, 0xa5, 0xcc, + 0x0c, 0x4f, 0x21, 0x39, 0x41, 0xa0, 0xe3, 0x05, 0xe0, 0x97, 0x60, 0x65, 0x5c, 0xa0, 0x8c, 0x04, + 0x85, 0xce, 0xe7, 0x33, 0x0f, 0x98, 0xa9, 0x04, 0x4a, 0x29, 0xa0, 0x1b, 0xc3, 0x51, 0x27, 0x22, + 0xf0, 0xb7, 0xc1, 0x16, 0x32, 0x8c, 0xa0, 0x67, 0x91, 0x63, 0xdc, 0x4c, 0x93, 0xac, 0x10, 0xcd, + 0x0d, 0x86, 0x46, 0xcc, 0x8b, 0x66, 0x0a, 0x57, 0x53, 0x0a, 0xe8, 0xf6, 0x10, 0xb0, 0xf1, 0xd2, + 0x95, 0xf3, 0xe0, 0x2c, 0x73, 0xf0, 0xd9, 0x99, 0xd9, 0x22, 0x3f, 0xd3, 0x3f, 0xb7, 0xe3, 0x4b, + 0x72, 0x8b, 0xd0, 0xf8, 0x5e, 0xc4, 0x00, 0x6e, 0xfc, 0xfd, 0x52, 0xf2, 0x9f, 0xb7, 0x68, 0x37, + 0x82, 0x6b, 0x60, 0x49, 0x52, 0x75, 0x51, 0x7b, 0x29, 0x23, 0x8c, 0x64, 0x5d, 0xab, 0x23, 0x51, + 0xc6, 0xf5, 0x7d, 0xbd, 0x26, 0x8b, 0xea, 0xb6, 0x2a, 0x4b, 0x7c, 0x01, 0x2e, 0x82, 0xd2, 0xa8, + 0x88, 0x2e, 0xa3, 0x97, 0x32, 0xe2, 0x39, 0xb8, 0x0a, 0x16, 0x47, 0x57, 0x77, 0xeb, 0x15, 0x19, + 0xed, 0xcb, 0x86, 0xac, 0xf3, 0x45, 0xf8, 0x18, 0x6c, 0x8d, 0x4a, 0x48, 0x82, 0x21, 0x54, 0x04, + 0x5d, 0xc6, 0x35, 0x4d, 0x37, 0x76, 0x90, 0xac, 0x63, 0x5d, 0xae, 0x6e, 0x63, 0x45, 0xd3, 0x0d, + 0x59, 0xe2, 0x67, 0xe0, 0xc7, 0xe0, 0xc1, 0x14, 0xa5, 0xbd, 0x03, 0xfd, 0xf3, 0xea, 0x80, 0xc6, + 0x19, 0xf8, 0x08, 0x6c, 0x4e, 0xd3, 0xd0, 0xf6, 0x77, 0x34, 0xa9, 0x32, 0xa0, 0x73, 0x16, 0xde, + 0x07, 0x77, 0xf3, 0x40, 0x43, 0x92, 0xce, 0x9f, 0x83, 0xf7, 0xc0, 0xad, 0x4c, 0x48, 0x7d, 0xc9, + 0xf3, 0xf0, 0x0e, 0x28, 0x8f, 0x4a, 0x0a, 0xb5, 0x5a, 0x55, 0x15, 0x05, 0x43, 0xd5, 0xf6, 0xb1, + 0x62, 0x18, 0x35, 0x7e, 0x16, 0xde, 0x06, 0x6b, 0xd3, 0xe5, 0x0c, 0xb1, 0xc6, 0x5f, 0x18, 0x2f, + 0xf6, 0x4a, 0xdd, 0x97, 0xb4, 0x57, 0x3a, 0x96, 0x64, 0x7d, 0xd7, 0xd0, 0x6a, 0x3c, 0x80, 0x0f, + 0xc0, 0xbd, 0x29, 0xf8, 0xf4, 0xcf, 0xab, 0x41, 0xcd, 0x18, 0xc6, 0x8b, 0x19, 0x09, 0x4e, 0x42, + 0x97, 0x25, 0x5d, 0x51, 0xb7, 0x0d, 0xfe, 0x03, 0xf8, 0x04, 0x7c, 0x9c, 0xcb, 0x7e, 0x3a, 0xc5, + 0x97, 0x32, 0xfc, 0x20, 0x59, 0x52, 0x07, 0x4b, 0x3f, 0x97, 0xb7, 0x28, 0x3b, 0x62, 0x8d, 0xbf, + 0x9c, 0xab, 0x28, 0x7d, 0x49, 0x3e, 0x77, 0x7a, 0xfa, 0xd2, 0x1f, 0xc2, 0xe7, 0xe0, 0xe9, 0xd7, + 0x49, 0x4f, 0xf8, 0x3d, 0x54, 0x65, 0x5d, 0xe7, 0x21, 0xfc, 0x3f, 0xb0, 0x9e, 0x47, 0x59, 0xf8, + 0xa2, 0x8e, 0x64, 0xfe, 0x23, 0x78, 0x17, 0xdc, 0x9c, 0x22, 0x2e, 0x1d, 0xec, 0x0b, 0x7b, 0x9a, + 0x54, 0xe1, 0xaf, 0x64, 0xb4, 0xb8, 0x28, 0xe8, 0xba, 0xb0, 0x2f, 0x21, 0x01, 0xef, 0xca, 0x07, + 0x7a, 0x4d, 0x10, 0x65, 0x9d, 0xbf, 0x9a, 0x51, 0xb5, 0x44, 0x27, 0x5d, 0x83, 0x6b, 0xf0, 0x19, + 0x78, 0x32, 0x45, 0x4b, 0xae, 0x0a, 0xba, 0xa1, 0x8a, 0xba, 0x2c, 0x20, 0x51, 0x19, 0xd0, 0xbc, + 0x9e, 0xab, 0xde, 0xa1, 0xbe, 0x20, 0x2a, 0x32, 0x5f, 0xca, 0xc8, 0x56, 0xa0, 0xb1, 0x27, 0xef, + 0x69, 0xe8, 0x40, 0xaa, 0xf0, 0xf3, 0xb9, 0x1c, 0xb0, 0xcc, 0xe2, 0xc0, 0xc1, 0x8d, 0x8c, 0x60, + 0x02, 0x0d, 0xb1, 0x5a, 0xd7, 0x8d, 0xa1, 0xe6, 0x5d, 0x80, 0x1b, 0xe0, 0x4e, 0x66, 0x77, 0x05, + 0x55, 0x5c, 0x84, 0x9b, 0x60, 0x23, 0x57, 0x7f, 0x05, 0xf2, 0x4b, 0x19, 0xc5, 0x4c, 0xe4, 0xf7, + 0x54, 0x11, 0x69, 0xba, 0xb6, 0x6d, 0xf0, 0xcb, 0xf0, 0x13, 0xf0, 0x68, 0x5a, 0x31, 0x35, 0x71, + 0x17, 0x69, 0x82, 0xa8, 0x0c, 0xed, 0x73, 0x2b, 0x19, 0xbd, 0x1f, 0xed, 0x8d, 0x82, 0x51, 0x15, + 0x74, 0x7e, 0x35, 0xe3, 0x9b, 0xd2, 0xf7, 0xb5, 0x57, 0xdb, 0x55, 0x61, 0x57, 0xe6, 0xd7, 0x26, + 0xd8, 0xd5, 0xc4, 0x54, 0x76, 0x25, 0x1d, 0xd7, 0x90, 0xf6, 0xbd, 0x03, 0xbe, 0x3c, 0xa1, 0x15, + 0xd3, 0xd2, 0x8a, 0xba, 0xa3, 0x60, 0xe1, 0xa5, 0xa0, 0x56, 0x85, 0x8a, 0x5a, 0x55, 0x8d, 0x03, + 0xfe, 0x26, 0x7c, 0x0a, 0x1e, 0x67, 0x68, 0xb1, 0x2f, 0x44, 0x15, 0x31, 0x92, 0x77, 0x54, 0xdd, + 0x40, 0x6c, 0xeb, 0xe4, 0x6f, 0x6d, 0xfc, 0x8e, 0x03, 0x73, 0x83, 0xbf, 0xd2, 0x80, 0x2b, 0x60, + 0x21, 0xb6, 0xa5, 0x1b, 0x82, 0x51, 0xd7, 0x87, 0xce, 0xba, 0x05, 0x70, 0x7d, 0x58, 0x40, 0xaf, + 0x8b, 0x62, 0xff, 0xb3, 0xe6, 0xc6, 0x2e, 0xee, 0xaa, 0xb5, 0x9a, 0x2c, 0xf1, 0x45, 0x38, 0x0f, + 0xae, 0x0e, 0x2f, 0xca, 0x08, 0x69, 0x88, 0x9f, 0x19, 0xa7, 0x27, 0x54, 0x34, 0xc4, 0x8e, 0xad, + 0x8a, 0xf6, 0x97, 0x77, 0xcb, 0xdc, 0x5f, 0xdf, 0x2d, 0x73, 0x7f, 0x7b, 0xb7, 0xcc, 0x7d, 0x21, + 0xb4, 0x2c, 0xfa, 0xba, 0x7b, 0xb8, 0xd9, 0x70, 0xdb, 0x5b, 0x2d, 0xcf, 0xec, 0x59, 0xc1, 0x69, + 0x6f, 0xda, 0x5b, 0xf1, 0x8f, 0xb7, 0xcc, 0x8e, 0xb5, 0xd5, 0x22, 0xce, 0x16, 0xfb, 0xa5, 0xd6, + 0x56, 0xcb, 0x1d, 0xfa, 0x35, 0xd7, 0xe1, 0x39, 0xb6, 0xf0, 0xf8, 0xbf, 0x01, 0x00, 0x00, 0xff, + 0xff, 0x8a, 0x26, 0xe3, 0x99, 0xef, 0x25, 0x00, 0x00, } func (m *UIBannerClickEvent) Marshal() (dAtA []byte, err error) { diff --git a/api/proto/teleport/devicetrust/v1/device.proto b/api/proto/teleport/devicetrust/v1/device.proto index 95762fb0b6f27..5a8164834fc34 100644 --- a/api/proto/teleport/devicetrust/v1/device.proto +++ b/api/proto/teleport/devicetrust/v1/device.proto @@ -19,6 +19,8 @@ package teleport.devicetrust.v1; import "google/protobuf/timestamp.proto"; import "teleport/devicetrust/v1/device_collected_data.proto"; import "teleport/devicetrust/v1/device_enroll_token.proto"; +import "teleport/devicetrust/v1/device_profile.proto"; +import "teleport/devicetrust/v1/device_source.proto"; import "teleport/devicetrust/v1/os_type.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"; @@ -76,6 +78,14 @@ message Device { // Only present in certain read modes. // Transient. repeated DeviceCollectedData collected_data = 10; + + // Source of the device. + // Devices managed directly via Teleport (`tctl`, Web UI, etc) have no + // assigned source. + DeviceSource source = 11; + + // Device information acquired from an external source. + DeviceProfile profile = 12; } // DeviceCredential represents the current enrolled public key of a device. diff --git a/api/proto/teleport/devicetrust/v1/device_collected_data.proto b/api/proto/teleport/devicetrust/v1/device_collected_data.proto index f36aa0db69beb..cd56570010b6e 100644 --- a/api/proto/teleport/devicetrust/v1/device_collected_data.proto +++ b/api/proto/teleport/devicetrust/v1/device_collected_data.proto @@ -41,4 +41,27 @@ message DeviceCollectedData { // Device serial number. // Required for macOS devices. string serial_number = 4; + + // Non-descriptive model identifier. + // Example: "MacBookPro9,2". + string model_identifier = 5; + + // OS version number, without the leading 'v'. + // Example: "13.2.1". + string os_version = 6; + + // OS build identifier. Augments the os_version. + // Example: "22D68". + string os_build = 7; + + // OS username (distinct from the Teleport user). + string os_username = 8; + + // Jamf binary version, without the leading 'v'. + // Example: "9.27" or "10.44.1-t1677509507". + string jamf_binary_version = 9; + + // Unmodified output of `/usr/bin/profiles status -type enrollment`. + // Used to verify the presence of an enrollment profile. + string macos_enrollment_profiles = 10; } diff --git a/api/proto/teleport/devicetrust/v1/device_profile.proto b/api/proto/teleport/devicetrust/v1/device_profile.proto new file mode 100644 index 0000000000000..c49b8eaa64c7d --- /dev/null +++ b/api/proto/teleport/devicetrust/v1/device_profile.proto @@ -0,0 +1,49 @@ +// 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.devicetrust.v1; + +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"; + +// Device profile information acquired from an external source. +// If present, it's used to further validate collected data. +message DeviceProfile { + // Latest profile update time. + // System managed. + google.protobuf.Timestamp update_time = 1; + + // Non-descriptive model identifier. + // Example: "MacBookPro9,2". + string model_identifier = 2; + + // OS version number, without the leading 'v'. + // See the Device's os_type for the general OS category. + // Example: "13.2.1". + string os_version = 3; + + // OS build identifier. Augments the os_version. + // Example: "22D68". + string os_build = 4; + + // Known OS users (distinct from the Teleport user). + repeated string os_usernames = 5; + + // Jamf binary version, without the leading 'v'. + // Example: "9.27" or "10.44.1-t1677509507". + string jamf_binary_version = 6; +} diff --git a/api/proto/teleport/devicetrust/v1/device_source.proto b/api/proto/teleport/devicetrust/v1/device_source.proto new file mode 100644 index 0000000000000..75bf9dea9c1c9 --- /dev/null +++ b/api/proto/teleport/devicetrust/v1/device_source.proto @@ -0,0 +1,47 @@ +// 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.devicetrust.v1; + +option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"; + +// Source of device, for devices that are managed by external systems +// (for example, MDMs). +message DeviceSource { + // Name of the source. + // Matches the name of the corresponding MDM service, if applicable. + // Readonly. + string name = 1; + + // Origin of the source. + // Readonly. + DeviceOrigin origin = 2; +} + +// Origin of a device. +enum DeviceOrigin { + // Unspecified or absent origin. + DEVICE_ORIGIN_UNSPECIFIED = 0; + + // Devices originated from direct API usage. + DEVICE_ORIGIN_API = 1; + + // Devices originated from Jamf sync. + DEVICE_ORIGIN_JAMF = 2; + + // Source originated from Microsoft Intune sync. + DEVICE_ORIGIN_INTUNE = 3; +} diff --git a/api/proto/teleport/devicetrust/v1/devicetrust_service.proto b/api/proto/teleport/devicetrust/v1/devicetrust_service.proto index 974bd924d1cf4..1decaf3d8f958 100644 --- a/api/proto/teleport/devicetrust/v1/devicetrust_service.proto +++ b/api/proto/teleport/devicetrust/v1/devicetrust_service.proto @@ -22,6 +22,7 @@ import "google/rpc/status.proto"; import "teleport/devicetrust/v1/device.proto"; import "teleport/devicetrust/v1/device_collected_data.proto"; import "teleport/devicetrust/v1/device_enroll_token.proto"; +import "teleport/devicetrust/v1/device_source.proto"; import "teleport/devicetrust/v1/user_certificates.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"; @@ -120,6 +121,14 @@ service DeviceTrustService { // // Only registered and enrolled devices may perform device authentication. rpc AuthenticateDevice(stream AuthenticateDeviceRequest) returns (stream AuthenticateDeviceResponse); + + // Syncs device inventory from a source exterior to Teleport, for example an + // MDM. + // Allows both partial and full syncs; for the latter, devices missing from + // the external inventory are handled as specified. + // Authorized either by a valid MDM service certificate or the appropriate + // "device" permissions (create/update/delete). + rpc SyncInventory(stream SyncInventoryRequest) returns (stream SyncInventoryResponse); } // Request for CreateDevice. @@ -257,12 +266,21 @@ message DeviceOrStatus { // ID of the created device. // Only present if the status is OK. string id = 2; + + // If true the action attempted against the device was a delete, instead of a + // create or update. + bool deleted = 3; } // Request for CreateDeviceEnrollToken. message CreateDeviceEnrollTokenRequest { // ID of the device. string device_id = 1; + + // Device collected data. + // Used to authorize issuance of device enrollment tokens for auto-enrollment. + // Not required otherwise. + DeviceCollectedData device_data = 2; } // Request for EnrollDevice. @@ -386,3 +404,107 @@ message AuthenticateDeviceChallengeResponse { // Signature over the challenge, using the device key. bytes signature = 1; } + +// Request for SyncInventory. +// +// A typical message sequence is as follows: +// (-> means client-to-server, <- means server-to-client) +// -> SyncInventoryStart +// <- SyncInventoryAck +// (loop) +// -> SyncInventoryDevices (add/remove devices) +// <- SyncInventoryResult +// (end loop) +// -> SyncInventoryEnd +// <- SyncInventoryResult (missing devices) +message SyncInventoryRequest { + oneof payload { + SyncInventoryStart start = 1; + SyncInventoryEnd end = 2; + SyncInventoryDevices devices_to_upsert = 3; + SyncInventoryDevices devices_to_remove = 4; + } +} + +// Response for SyncInventory. +message SyncInventoryResponse { + oneof payload { + SyncInventoryAck ack = 1; + SyncInventoryResult result = 2; + } +} + +// SyncInventoryStart starts the inventory sync. +message SyncInventoryStart { + // Source of the inventory sync. + // Acquired from the mTLS certificate for MDM services, must be explicitly + // supplied otherwise. + // This source is used for all devices. The `source` field in individual + // devices is ignored by this RPC. + DeviceSource source = 1; + + // Mode of syncing. + // Required. + SyncInventoryMode mode = 2; + + // Action for devices missing from the external inventory, but present in + // Teleport. + // Only executed if mode is FULL and mdm_sync_successful is set to true in the + // SyncInventoryEnd message. + SyncInventoryDeviceAction on_missing_action = 3; +} + +// Mode of sync for SyncInventory. +enum SyncInventoryMode { + SYNC_INVENTORY_MODE_UNSPECIFIED = 0; + + // Partial inventory sync; not all devices in the external inventory are sent + // to the stream. + // Partial sync precludes inventory cleanup. + SYNC_INVENTORY_MODE_PARTIAL = 1; + + // Full inventory sync; all devices in the external inventory must be sent to + // the stream. + // Full sync allows for handling of missing devices. + SYNC_INVENTORY_MODE_FULL = 2; +} + +// Device action for SyncInventory. +// Typically triggered at the end of FULL syncs.. +enum SyncInventoryDeviceAction { + SYNC_INVENTORY_DEVICE_ACTION_UNSPECIFIED = 0; + + // Noop is a no-action. + SYNC_INVENTORY_DEVICE_ACTION_NOOP = 1; + + // Deletes the device if the action condition is met. + SYNC_INVENTORY_DEVICE_ACTION_DELETE = 2; +} + +// SyncInventoryEnd ends the inventory sync, signaling that no more +// SyncInventoryDevices messages will be sent by the client. +message SyncInventoryEnd { + // True if the external sync was fully successful (for example, all reads from + // an MDM API were successful). + // If false the sync's on_missing_action is skipped. + bool external_sync_successful = 1; +} + +// SyncInventoryDevices transports devices to add/update/remove. +// Removals only need identifying fields to be set. +message SyncInventoryDevices { + // Devices to sync. + repeated Device devices = 1; +} + +// SyncInventoryAck is used to confirm successful processing of messages that +// lack a more specific response. +message SyncInventoryAck {} + +// SyncInventoryResult is the response for SyncInventoryDevices or +// SyncInventoryEnd +// It lists all synced/deleted devices. +message SyncInventoryResult { + // Devices modified, in the same order as the input when applicable. + repeated DeviceOrStatus devices = 1; +} diff --git a/api/proto/teleport/usageevents/v1/usageevents.proto b/api/proto/teleport/usageevents/v1/usageevents.proto index 0bb238e7683f8..a5bfb1727052c 100644 --- a/api/proto/teleport/usageevents/v1/usageevents.proto +++ b/api/proto/teleport/usageevents/v1/usageevents.proto @@ -106,6 +106,28 @@ enum DiscoverResource { DISCOVER_RESOURCE_DATABASE_POSTGRES_GCP = 15; DISCOVER_RESOURCE_DATABASE_MYSQL_GCP = 16; DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP = 17; + + DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS = 18; + DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE = 19; + DISCOVER_RESOURCE_DATABASE_DYNAMODB = 20; + DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES = 21; + DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED = 22; // Cassandra & ScyllaDb + DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED = 23; + DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE = 24; // Elasticache & MemoryDb + DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB = 25; + DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE = 26; + DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED = 27; + + DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE = 28; + DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE = 29; + DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT = 30; + DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED = 31; + DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS = 32; + DISCOVER_RESOURCE_DATABASE_SNOWFLAKE = 33; + + DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY = 34; + DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY = 35; + DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION = 36; } // DiscoverResourceMetadata contains common metadata identifying resource type being added. diff --git a/api/types/installers/installer.sh.tmpl b/api/types/installers/installer.sh.tmpl index 3232f69d11132..d2b880410c5e6 100644 --- a/api/types/installers/installer.sh.tmpl +++ b/api/types/installers/installer.sh.tmpl @@ -1,5 +1,7 @@ #!/bin/sh +set -eu + on_ec2() { EC2_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS "http://169.254.169.254/latest/meta-data") # EC2 metadata sometimes requires token access, so a successful hit may diff --git a/api/utils/grpc/stream/stream.go b/api/utils/grpc/stream/stream.go index bd9c40e31c954..54021755d273d 100644 --- a/api/utils/grpc/stream/stream.go +++ b/api/utils/grpc/stream/stream.go @@ -113,7 +113,7 @@ func (c *ReadWriter) Write(b []byte) (int, error) { } if err := c.source.Send(chunk); err != nil { - return sent, trace.ConnectionProblem(trail.FromGRPC(err), "failed to send on source") + return sent, trace.ConnectionProblem(trail.FromGRPC(err), "failed to send on source: %v", err) } sent += len(chunk) diff --git a/build.assets/Makefile b/build.assets/Makefile index f943cf10e0cea..8f96e962d02d6 100644 --- a/build.assets/Makefile +++ b/build.assets/Makefile @@ -19,7 +19,7 @@ TEST_KUBE ?= OS ?= linux ARCH ?= amd64 -GOLANG_VERSION ?= go1.20.2 +GOLANG_VERSION ?= go1.20.3 NODE_VERSION ?= 16.18.1 diff --git a/docs/config.json b/docs/config.json index 6430d558fb379..80f2529dd6cf7 100644 --- a/docs/config.json +++ b/docs/config.json @@ -444,10 +444,6 @@ { "title": "Reference", "slug": "/access-controls/reference/" - }, - { - "title": "FAQ", - "slug": "/access-controls/faq/" } ] }, @@ -1417,8 +1413,8 @@ "version": "12.1.2", "major_version": "12", "sla": { - "monthly_percentage": "99.5%", - "monthly_downtime": "3 hours 40 minutes" + "monthly_percentage": "99.9%", + "monthly_downtime": "44 minutes" } }, "devicetrust": { @@ -2359,6 +2355,11 @@ "source": "/kubernetes-access/guides/", "destination": "/kubernetes-access/introduction/", "permanent": true + }, + { + "source": "/access-controls/faq/", + "destination": "/access-controls/introduction/", + "permanent": true } ] } diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx b/docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx index 7b5d3ac673181..c75b0e30e8166 100644 --- a/docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx +++ b/docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx @@ -16,8 +16,7 @@ compromising productivity. - Admin account on your Discord server. Installing a bot requires at least the "manager server" permission. - Either a Linux host or Kubernetes cluster where you will run the Discord plugin. - -(!/docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/8. Define RBAC resources diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx b/docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx index ffed4cc27d172..8735c2a63f09a 100644 --- a/docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx +++ b/docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx @@ -25,7 +25,7 @@ regularly. -(!/docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/7. Define RBAC resources diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx b/docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx index 134a4c2f3baea..0081e1f07fc84 100644 --- a/docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx +++ b/docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx @@ -14,7 +14,7 @@ Jira tickets. - Jira Server or Jira Cloud installation with an owner privileges, specifically to set up webhooks, issue types, and workflows -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/6. Create a user and role for access diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx b/docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx index d4f4383ac3922..02c75c2944163 100644 --- a/docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx +++ b/docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx @@ -16,8 +16,7 @@ compromising productivity. - A Mattermost account with admin privileges. This plugin has been tested with Mattermost v7.0.1. - Either a Linux host or Kubernetes cluster where you will run the Mattermost plugin. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/8. Define RBAC resources diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx b/docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx index 79cb9e713c177..52e5eb1ccee03 100644 --- a/docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx +++ b/docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx @@ -45,7 +45,7 @@ Teleport Cloud tenant and PagerDuty. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/8. Create services diff --git a/docs/pages/access-controls/access-requests/resource-requests.mdx b/docs/pages/access-controls/access-requests/resource-requests.mdx index 03411d4917832..107053adfe985 100644 --- a/docs/pages/access-controls/access-requests/resource-requests.mdx +++ b/docs/pages/access-controls/access-requests/resource-requests.mdx @@ -1,7 +1,6 @@ --- title: Resource Access Requests description: Teleport allows users to request access to specific resources from the CLI or UI. Requests can be escalated via ChatOps or anywhere else via our flexible Authorization Workflow API. -h1: Teleport Resource Access Requests --- @@ -28,7 +27,7 @@ available in Teleport Enterprise. (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) All `teleport` instances in the cluster must be running Teleport `v10.0.0` or diff --git a/docs/pages/access-controls/access-requests/role-requests.mdx b/docs/pages/access-controls/access-requests/role-requests.mdx index e703b6c7fc649..0a70c664b67b4 100644 --- a/docs/pages/access-controls/access-requests/role-requests.mdx +++ b/docs/pages/access-controls/access-requests/role-requests.mdx @@ -12,7 +12,7 @@ via ChatOps or anywhere else via our flexible Authorization Workflow API. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## RBAC Setup diff --git a/docs/pages/access-controls/faq.mdx b/docs/pages/access-controls/faq.mdx deleted file mode 100644 index 34b05c0ea2fa7..0000000000000 --- a/docs/pages/access-controls/faq.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Access Controls FAQ -description: Frequently asked questions about Teleport RBAC ---- - -## What if a node has multiple labels? - -In this case, the access will be granted only if **all of the labels** defined in the -role are present. This effectively means Teleport uses an "AND" operator when evaluating -node-level access using labels. - -## Can I use node-level RBAC with OpenSSH servers? - -No. OpenSSH servers running `sshd` can't label themselves. This is a factor in deciding -to run the Teleport Node Service instead. - -## Why do I see a UUID instead of a hostname when reviewing access requests? - -Resource Access Requests embed the UUID of requested resources in order to ensure -that extra access isn't mistakenly granted due to overlapping hostnames. - -In order for Access Request reviewers to see the hostname, they must either: - -- Have permissions to access the requested server themselves, or -- Have [`preview_as_roles`](./access-requests/resource-requests.mdx#allow-reviewers-to-see-the-hostnames-of-ssh-nodes) - set with a role that can access the server - \ No newline at end of file diff --git a/docs/pages/access-controls/getting-started.mdx b/docs/pages/access-controls/getting-started.mdx index ab847056040d7..0e308d1cb921e 100644 --- a/docs/pages/access-controls/getting-started.mdx +++ b/docs/pages/access-controls/getting-started.mdx @@ -14,9 +14,9 @@ wrap up with creating your own role. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/permission-warning.mdx!) +- (!docs/pages/includes/tctl.mdx!) -(!docs/pages/includes/tctl.mdx!) +(!docs/pages/includes/permission-warning.mdx!) ## Step 1/3. Add local users with preset roles diff --git a/docs/pages/access-controls/guides/dual-authz.mdx b/docs/pages/access-controls/guides/dual-authz.mdx index 5d85d9d68bfd1..abe12a9444150 100644 --- a/docs/pages/access-controls/guides/dual-authz.mdx +++ b/docs/pages/access-controls/guides/dual-authz.mdx @@ -41,7 +41,7 @@ of two team members for a privileged role `dbadmin`. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Set up a Teleport bot diff --git a/docs/pages/access-controls/guides/hardware-key-support.mdx b/docs/pages/access-controls/guides/hardware-key-support.mdx index 31d3be24d9be8..533ccadb4af6b 100644 --- a/docs/pages/access-controls/guides/hardware-key-support.mdx +++ b/docs/pages/access-controls/guides/hardware-key-support.mdx @@ -62,7 +62,7 @@ Additionally, this feature can be configured to require touch for every Teleport Teleport clients use PIV slot 9a for the `hardware_key` option and 9c for the `hardware_key_touch` option, and will overwrite other keys and certs in these slots as needed. This may interfere with other PIV applications, like `yubikey-agent`, so we recommend only using one PIV application at a time. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/2. Enforce Hardware Key Support diff --git a/docs/pages/access-controls/guides/impersonation.mdx b/docs/pages/access-controls/guides/impersonation.mdx index 2489761616817..9ee8285164a2e 100644 --- a/docs/pages/access-controls/guides/impersonation.mdx +++ b/docs/pages/access-controls/guides/impersonation.mdx @@ -15,7 +15,7 @@ non-interactive CI/CD user Jenkins and a security scanner. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3: Create a CI/CD user and corresponding role diff --git a/docs/pages/access-controls/guides/locking.mdx b/docs/pages/access-controls/guides/locking.mdx index 33658ae9e43df..e203a58630f69 100644 --- a/docs/pages/access-controls/guides/locking.mdx +++ b/docs/pages/access-controls/guides/locking.mdx @@ -39,7 +39,7 @@ A lock can target the following objects or attributes: (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/2. Create a lock diff --git a/docs/pages/access-controls/guides/per-session-mfa.mdx b/docs/pages/access-controls/guides/per-session-mfa.mdx index fa03f4eebe1c0..4f9da7652788a 100644 --- a/docs/pages/access-controls/guides/per-session-mfa.mdx +++ b/docs/pages/access-controls/guides/per-session-mfa.mdx @@ -38,13 +38,11 @@ their on-disk Teleport certificates. Per-session MFA for Desktop Access was introduced in Teleport 9. - ## Prerequisites (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - [WebAuthn configured](webauthn.mdx) on this cluster - Second factor hardware device, such as YubiKey or SoloKey - A Web browser with [WebAuthn support]( @@ -64,6 +62,7 @@ teleport: webauthn: rp_id: teleport.example.com ``` + ## Configure per-session MFA diff --git a/docs/pages/access-controls/guides/role-templates.mdx b/docs/pages/access-controls/guides/role-templates.mdx index 1f3b7f0534ef5..96b26cc21bc5e 100644 --- a/docs/pages/access-controls/guides/role-templates.mdx +++ b/docs/pages/access-controls/guides/role-templates.mdx @@ -20,7 +20,7 @@ other policies. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Local users diff --git a/docs/pages/access-controls/guides/webauthn.mdx b/docs/pages/access-controls/guides/webauthn.mdx index 153d5c7dbeaa8..91eebded132e7 100644 --- a/docs/pages/access-controls/guides/webauthn.mdx +++ b/docs/pages/access-controls/guides/webauthn.mdx @@ -20,8 +20,7 @@ UI). - WebAuthn hardware device, such as YubiKey or SoloKey - A Web browser with [WebAuthn support]( https://developers.yubico.com/WebAuthn/WebAuthn_Browser_Support/) - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Enable WebAuthn support diff --git a/docs/pages/access-controls/idps/saml-grafana.mdx b/docs/pages/access-controls/idps/saml-grafana.mdx index 6a730b6102417..6a1ba82e57700 100644 --- a/docs/pages/access-controls/idps/saml-grafana.mdx +++ b/docs/pages/access-controls/idps/saml-grafana.mdx @@ -17,7 +17,7 @@ not just those running behind the Teleport App Service. (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Configure a Teleport role with access to SAML service provider objects diff --git a/docs/pages/access-controls/idps/saml-guide.mdx b/docs/pages/access-controls/idps/saml-guide.mdx index ffaa46f0e1b0e..71ae4eb53c5d7 100644 --- a/docs/pages/access-controls/idps/saml-guide.mdx +++ b/docs/pages/access-controls/idps/saml-guide.mdx @@ -13,10 +13,10 @@ authenticate to external services. (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - If you're new to SAML, consider reviewing our [SAML Identity Provider Reference](./saml-reference.mdx) before proceeding. + ## Example external application We'll be using [samltest.id](https://samltest.id/) to create a test consumer of diff --git a/docs/pages/access-controls/introduction.mdx b/docs/pages/access-controls/introduction.mdx index 4b2fbf3baae31..50c5802b14a25 100644 --- a/docs/pages/access-controls/introduction.mdx +++ b/docs/pages/access-controls/introduction.mdx @@ -74,7 +74,5 @@ achieve compliance with: ## Find out more -Find out more information on Teleport's RBAC features: - -- [Access Controls Reference](./reference.mdx) -- [Frequently Asked Questions](./faq.mdx) +Find out more information on Teleport's RBAC features by reading the [Access +Controls Reference](./reference.mdx). diff --git a/docs/pages/access-controls/login-rules/guide.mdx b/docs/pages/access-controls/login-rules/guide.mdx index 98f4362d488b1..7c9350ec2aae8 100644 --- a/docs/pages/access-controls/login-rules/guide.mdx +++ b/docs/pages/access-controls/login-rules/guide.mdx @@ -14,7 +14,7 @@ first Login Rule to your Teleport cluster. (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Before you get started you’ll need a running Teleport Enterprise or Cloud cluster on version `11.3.1` or greater. diff --git a/docs/pages/access-controls/sso/adfs.mdx b/docs/pages/access-controls/sso/adfs.mdx index 10178ac810a1c..7d3c75fd3183a 100644 --- a/docs/pages/access-controls/sso/adfs.mdx +++ b/docs/pages/access-controls/sso/adfs.mdx @@ -21,7 +21,7 @@ like: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) (!docs/pages/includes/enterprise/samlauthentication.mdx!) diff --git a/docs/pages/access-controls/sso/azuread.mdx b/docs/pages/access-controls/sso/azuread.mdx index 6bb637aa2faee..767bc741d3537 100644 --- a/docs/pages/access-controls/sso/azuread.mdx +++ b/docs/pages/access-controls/sso/azuread.mdx @@ -23,7 +23,7 @@ Before you get started you’ll need: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) (!docs/pages/includes/enterprise/samlauthentication.mdx!) diff --git a/docs/pages/access-controls/sso/github-sso.mdx b/docs/pages/access-controls/sso/github-sso.mdx index 11ee87b749df9..4f55164492ddd 100644 --- a/docs/pages/access-controls/sso/github-sso.mdx +++ b/docs/pages/access-controls/sso/github-sso.mdx @@ -17,7 +17,7 @@ from either GitHub Cloud or GitHub Enterprise Server. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Create a GitHub OAuth app diff --git a/docs/pages/access-controls/sso/gitlab.mdx b/docs/pages/access-controls/sso/gitlab.mdx index 71ef8261a99e5..160225fa6ef8e 100644 --- a/docs/pages/access-controls/sso/gitlab.mdx +++ b/docs/pages/access-controls/sso/gitlab.mdx @@ -22,7 +22,7 @@ like: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Enable default OIDC authentication diff --git a/docs/pages/access-controls/sso/google-workspace.mdx b/docs/pages/access-controls/sso/google-workspace.mdx index 6eb98480b0824..83d2d4b0ae681 100644 --- a/docs/pages/access-controls/sso/google-workspace.mdx +++ b/docs/pages/access-controls/sso/google-workspace.mdx @@ -24,7 +24,7 @@ Before you get started you’ll need: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Enable default OIDC authentication diff --git a/docs/pages/access-controls/sso/oidc.mdx b/docs/pages/access-controls/sso/oidc.mdx index b39aed6f53268..af4f6e10fdd37 100644 --- a/docs/pages/access-controls/sso/oidc.mdx +++ b/docs/pages/access-controls/sso/oidc.mdx @@ -19,7 +19,7 @@ administrators to define policies like: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Enable default OIDC authentication diff --git a/docs/pages/access-controls/sso/okta.mdx b/docs/pages/access-controls/sso/okta.mdx index 5e454b21289ce..d0a9aa0b16623 100644 --- a/docs/pages/access-controls/sso/okta.mdx +++ b/docs/pages/access-controls/sso/okta.mdx @@ -23,8 +23,7 @@ Teleport administrators to define policies like: - A Teleport role with access to edit and maintain `saml` resources. This is available in the default `editor` role. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) (!docs/pages/includes/enterprise/samlauthentication.mdx!) diff --git a/docs/pages/access-controls/sso/one-login.mdx b/docs/pages/access-controls/sso/one-login.mdx index 37146b9293a75..fe5941589db6c 100644 --- a/docs/pages/access-controls/sso/one-login.mdx +++ b/docs/pages/access-controls/sso/one-login.mdx @@ -20,7 +20,7 @@ like: (!docs/pages/includes/commercial-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) (!docs/pages/includes/enterprise/samlauthentication.mdx!) diff --git a/docs/pages/api/rbac.mdx b/docs/pages/api/rbac.mdx index 7f4f7f7b67dc1..5307468910c44 100644 --- a/docs/pages/api/rbac.mdx +++ b/docs/pages/api/rbac.mdx @@ -59,7 +59,7 @@ Teleport roles based on an external RBAC system. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up your Kubernetes cluster diff --git a/docs/pages/application-access/cloud-apis/azure.mdx b/docs/pages/application-access/cloud-apis/azure.mdx index b81718bfce4db..2e10d39ec88fe 100644 --- a/docs/pages/application-access/cloud-apis/azure.mdx +++ b/docs/pages/application-access/cloud-apis/azure.mdx @@ -48,7 +48,7 @@ prevent unauthorized access to your organization's Azure identities. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Grant an identity to your VM diff --git a/docs/pages/application-access/cloud-apis/google-cloud.mdx b/docs/pages/application-access/cloud-apis/google-cloud.mdx index 74c9340c21f04..627308636184c 100644 --- a/docs/pages/application-access/cloud-apis/google-cloud.mdx +++ b/docs/pages/application-access/cloud-apis/google-cloud.mdx @@ -55,7 +55,7 @@ guide. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Configure Google Cloud diff --git a/docs/pages/application-access/getting-started.mdx b/docs/pages/application-access/getting-started.mdx index 7b844b7c4dad3..102807b06fd49 100644 --- a/docs/pages/application-access/getting-started.mdx +++ b/docs/pages/application-access/getting-started.mdx @@ -21,8 +21,7 @@ Let's connect to Grafana using Teleport Application Access in three steps: (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - A Docker installation, which we will use to launch Grafana in a container. Alternatively, if you have another web application you'd like to protect with Application Access, you can use that instead. - A host where you will run the Teleport Application Service. diff --git a/docs/pages/application-access/guides/dynamodb.mdx b/docs/pages/application-access/guides/dynamodb.mdx index 708e26434efb3..ffcf5b3ef4091 100644 --- a/docs/pages/application-access/guides/dynamodb.mdx +++ b/docs/pages/application-access/guides/dynamodb.mdx @@ -37,7 +37,7 @@ This guide will help you to: (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) If you have not yet deployed the Auth Service and Proxy Service, you should follow one of our [getting started guides](../getting-started.mdx) or try our Teleport Application Access [interactive learning track](https://play.instruqt.com/teleport/invite/rgvuva4gzkon). diff --git a/docs/pages/application-access/guides/tcp.mdx b/docs/pages/application-access/guides/tcp.mdx index 93546c082650b..c6853dd4a3fbb 100644 --- a/docs/pages/application-access/guides/tcp.mdx +++ b/docs/pages/application-access/guides/tcp.mdx @@ -11,8 +11,7 @@ servers or databases not yet natively supported in Database Access. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - TCP application to connect to. In this guide we'll use a PostgreSQL running in Docker as an example. You can also use any TCP-based application you may already have. diff --git a/docs/pages/contributing/documentation/style-guide.mdx b/docs/pages/contributing/documentation/style-guide.mdx index 67b91461a75eb..21992871272e6 100644 --- a/docs/pages/contributing/documentation/style-guide.mdx +++ b/docs/pages/contributing/documentation/style-guide.mdx @@ -111,7 +111,6 @@ Specific kinds of architecture guides include: - Should be easy to navigate with a browser's search functionality. List all content on the same page. - Should avoid prose and be expressed with brevity. - ## General style rules Please refer to this style guide when determining how address questions about @@ -119,6 +118,14 @@ English grammar, usage, and so on. Since many of these rules have equally justifiable alternatives, a style guide prevents debates over these questions from getting in the way of documenting Teleport. + + +If a commonly debated style question does not have a resolution in this guide +(e.g., the Oxford comma), all we ask is that you keep your style consistent +within a particular page to maintain a professional polish. + + + ### Voice The documentation should be technically precise and directed toward a technical diff --git a/docs/pages/database-access/getting-started.mdx b/docs/pages/database-access/getting-started.mdx index abe9ec1913206..97fc01248e671 100644 --- a/docs/pages/database-access/getting-started.mdx +++ b/docs/pages/database-access/getting-started.mdx @@ -35,7 +35,7 @@ release. - A host, e.g., an EC2 instance, where you will run the Teleport Database Service. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up Aurora diff --git a/docs/pages/database-access/guides/aws-cassandra-keyspaces.mdx b/docs/pages/database-access/guides/aws-cassandra-keyspaces.mdx index c14a5a0dd0564..77d22104ebce6 100644 --- a/docs/pages/database-access/guides/aws-cassandra-keyspaces.mdx +++ b/docs/pages/database-access/guides/aws-cassandra-keyspaces.mdx @@ -35,7 +35,7 @@ This guide will help you to: - The `cqlsh` Cassandra client installed and added to your system's `PATH` environment variable. - A host, e.g., an Amazon EC2 instance, where you will run the Teleport Database Service. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/aws-dynamodb.mdx b/docs/pages/database-access/guides/aws-dynamodb.mdx index 9309b50b56067..8a18bcf50b7de 100644 --- a/docs/pages/database-access/guides/aws-dynamodb.mdx +++ b/docs/pages/database-access/guides/aws-dynamodb.mdx @@ -32,7 +32,7 @@ This guide will help you to: This guide assumes an EC2 instance when creating and applying IAM roles, and must be adjusted accordingly for custom configurations. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) This guide provides an example configuration of IAM access roles as a model, diff --git a/docs/pages/database-access/guides/azure-redis.mdx b/docs/pages/database-access/guides/azure-redis.mdx index d0b8f51402bff..4f7bcdbbdb2ae 100644 --- a/docs/pages/database-access/guides/azure-redis.mdx +++ b/docs/pages/database-access/guides/azure-redis.mdx @@ -28,8 +28,7 @@ This guide will help you to: Service. - `redis-cli` version `6.2` or newer installed and added to your system's `PATH` environment variable. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Create a Teleport user diff --git a/docs/pages/database-access/guides/azure-sql-server-ad.mdx b/docs/pages/database-access/guides/azure-sql-server-ad.mdx index 7210ee700c5d4..467151ad7f34f 100644 --- a/docs/pages/database-access/guides/azure-sql-server-ad.mdx +++ b/docs/pages/database-access/guides/azure-sql-server-ad.mdx @@ -38,8 +38,7 @@ This guide will help you to: - SQL Server running on Azure. - The Teleport Database Service running on an Azure virtual instance. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/8. Create a Teleport user diff --git a/docs/pages/database-access/guides/cassandra-self-hosted.mdx b/docs/pages/database-access/guides/cassandra-self-hosted.mdx index 7edc53d919258..2613ab7fe6c09 100644 --- a/docs/pages/database-access/guides/cassandra-self-hosted.mdx +++ b/docs/pages/database-access/guides/cassandra-self-hosted.mdx @@ -33,8 +33,7 @@ This guide will help you to: - Self-hosted Cassandra or ScyllaDB instance. - The `cqlsh` Cassandra client installed and added to your system's `PATH` environment variable. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/cockroachdb-self-hosted.mdx b/docs/pages/database-access/guides/cockroachdb-self-hosted.mdx index 26fdc0032aa78..739134650910b 100644 --- a/docs/pages/database-access/guides/cockroachdb-self-hosted.mdx +++ b/docs/pages/database-access/guides/cockroachdb-self-hosted.mdx @@ -19,7 +19,6 @@ This guide will help you to: 2. Configure mutual TLS authentication between Teleport and your CockroachDB cluster. 3. Connect to your CockroachDB cluster via Teleport. - ![Teleport Database Access CockroachDB Self-Hosted](../../../img/database-access/guides/cockroachdb_selfhosted.png) @@ -34,8 +33,7 @@ This guide will help you to: - CockroachDB cluster. - A host, e.g., an Amazon EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/dynamic-registration.mdx b/docs/pages/database-access/guides/dynamic-registration.mdx index 8da5713d3548d..3373419c38f00 100644 --- a/docs/pages/database-access/guides/dynamic-registration.mdx +++ b/docs/pages/database-access/guides/dynamic-registration.mdx @@ -91,7 +91,7 @@ To create a database resource, run: $ tctl create database.yaml ``` -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) After the resource has been created, it will appear among the list of available databases (in `tsh db ls` or UI) as long as at least one Database Service diff --git a/docs/pages/database-access/guides/elastic.mdx b/docs/pages/database-access/guides/elastic.mdx index 437dc96af3b82..44113c78f4785 100644 --- a/docs/pages/database-access/guides/elastic.mdx +++ b/docs/pages/database-access/guides/elastic.mdx @@ -28,7 +28,7 @@ This guide will help you to configure secured access to an Elasticsearch databas See [Installation](../../installation.mdx) for details. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/mongodb-atlas.mdx b/docs/pages/database-access/guides/mongodb-atlas.mdx index e2d4e46f4a8fd..febab1dd80657 100644 --- a/docs/pages/database-access/guides/mongodb-atlas.mdx +++ b/docs/pages/database-access/guides/mongodb-atlas.mdx @@ -24,8 +24,7 @@ In this guide you will: - [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) cluster. - A host, e.g., an Amazon EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up the Teleport Database Service @@ -85,7 +84,8 @@ db_service: - name: "mongodb-atlas" protocol: "mongodb" uri: "mongodb+srv://cluster0.abcde.mongodb.net" - ca_cert_file: "/path/to/letsencrypt/isrgrootx1.pem" + tls: + ca_cert_file: "/path/to/letsencrypt/isrgrootx1.pem" static_labels: env: "dev" @@ -133,7 +133,8 @@ db_service: - name: "mongodb-atlas" protocol: "mongodb" uri: "mongodb+srv://cluster0.abcde.mongodb.net" - ca_cert_file: "/path/to/letsencrypt/isrgrootx1.pem" + tls: + ca_cert_file: "/path/to/letsencrypt/isrgrootx1.pem" static_labels: env: "dev" ``` diff --git a/docs/pages/database-access/guides/mongodb-self-hosted.mdx b/docs/pages/database-access/guides/mongodb-self-hosted.mdx index 87dc20a7f7ebb..7141e4875ac63 100644 --- a/docs/pages/database-access/guides/mongodb-self-hosted.mdx +++ b/docs/pages/database-access/guides/mongodb-self-hosted.mdx @@ -31,7 +31,7 @@ In this guide you will: April 2021 so if you're still using an older version, consider upgrading. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Install and configure Teleport diff --git a/docs/pages/database-access/guides/mysql-cloudsql.mdx b/docs/pages/database-access/guides/mysql-cloudsql.mdx index 977b750e581dd..93afe819803f2 100644 --- a/docs/pages/database-access/guides/mysql-cloudsql.mdx +++ b/docs/pages/database-access/guides/mysql-cloudsql.mdx @@ -30,8 +30,7 @@ Teleport Database Access for Cloud SQL MySQL is available starting from the - Google Cloud account - A host, e.g., a Compute Engine instance, where you will run the Teleport Database Service - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Create a service account for the Teleport Database Service diff --git a/docs/pages/database-access/guides/mysql-self-hosted.mdx b/docs/pages/database-access/guides/mysql-self-hosted.mdx index e71f4f72afe4d..3ac02ef68606e 100644 --- a/docs/pages/database-access/guides/mysql-self-hosted.mdx +++ b/docs/pages/database-access/guides/mysql-self-hosted.mdx @@ -23,8 +23,7 @@ This guide will help you to: - A self-hosted MySQL or MariaDB instance. - A host, e.g., an Amazon EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/postgres-cloudsql.mdx b/docs/pages/database-access/guides/postgres-cloudsql.mdx index b2855b29b5e41..6ae1868421b9e 100644 --- a/docs/pages/database-access/guides/postgres-cloudsql.mdx +++ b/docs/pages/database-access/guides/postgres-cloudsql.mdx @@ -25,8 +25,7 @@ This guide will help you to: - Command-line client `psql` installed and added to your system's `PATH` environment variable. - A host, e.g., a Compute Engine instance, where you will run the Teleport Database Service - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/7. Enable Cloud SQL IAM authentication diff --git a/docs/pages/database-access/guides/postgres-redshift.mdx b/docs/pages/database-access/guides/postgres-redshift.mdx index 8f8ec94754325..520758c197b94 100644 --- a/docs/pages/database-access/guides/postgres-redshift.mdx +++ b/docs/pages/database-access/guides/postgres-redshift.mdx @@ -26,8 +26,7 @@ This guide will help you to: - Command-line client `psql` installed and added to your system's `PATH` environment variable. - A host, e.g., an EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Create a Teleport user diff --git a/docs/pages/database-access/guides/postgres-self-hosted.mdx b/docs/pages/database-access/guides/postgres-self-hosted.mdx index 795a3d5080abc..c40f278203a2e 100644 --- a/docs/pages/database-access/guides/postgres-self-hosted.mdx +++ b/docs/pages/database-access/guides/postgres-self-hosted.mdx @@ -24,8 +24,7 @@ This guide will help you to: - Command-line client `psql` installed and added to your system's `PATH` environment variable. - A host, e.g., an Amazon EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/rds-proxy.mdx b/docs/pages/database-access/guides/rds-proxy.mdx index c03f3715f57df..b6ade327a6de9 100644 --- a/docs/pages/database-access/guides/rds-proxy.mdx +++ b/docs/pages/database-access/guides/rds-proxy.mdx @@ -25,8 +25,7 @@ Teleport currently supports RDS Proxy instances with engine family - Any RDS Proxy instances intended for connection through Teleport must have TLS enabled. - A host, e.g., an EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Create a Database Service configuration diff --git a/docs/pages/database-access/guides/rds.mdx b/docs/pages/database-access/guides/rds.mdx index dbb9ac6204952..608577f327530 100644 --- a/docs/pages/database-access/guides/rds.mdx +++ b/docs/pages/database-access/guides/rds.mdx @@ -35,8 +35,7 @@ This guide will help you to: IAM policies. - A host, e.g., an EC2 instance, where you will run the Teleport Database Service. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/6. Create a Teleport user diff --git a/docs/pages/database-access/guides/redis-aws.mdx b/docs/pages/database-access/guides/redis-aws.mdx index 69f755c536e9d..b0b8d935cd6de 100644 --- a/docs/pages/database-access/guides/redis-aws.mdx +++ b/docs/pages/database-access/guides/redis-aws.mdx @@ -27,7 +27,7 @@ This guide will help you to: - A host, e.g., an EC2 instance, where you will run the Teleport Database Service. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/6. Create a Teleport user diff --git a/docs/pages/database-access/guides/redis-cluster.mdx b/docs/pages/database-access/guides/redis-cluster.mdx index 078f573b51b97..c9ca66e3332ff 100644 --- a/docs/pages/database-access/guides/redis-cluster.mdx +++ b/docs/pages/database-access/guides/redis-cluster.mdx @@ -40,7 +40,7 @@ This guide will help you to: Redis `7.0` and RESP3 (REdis Serialization Protocol) are currently not supported. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/6. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/redis.mdx b/docs/pages/database-access/guides/redis.mdx index 2f6a8d96e346d..3bebb8c7dc41b 100644 --- a/docs/pages/database-access/guides/redis.mdx +++ b/docs/pages/database-access/guides/redis.mdx @@ -45,7 +45,7 @@ This guide will help you to: Redis `7.0` and RESP3 (REdis Serialization Protocol) are currently not supported. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/snowflake.mdx b/docs/pages/database-access/guides/snowflake.mdx index 8825a60d8dee4..f0b542ca787b1 100644 --- a/docs/pages/database-access/guides/snowflake.mdx +++ b/docs/pages/database-access/guides/snowflake.mdx @@ -38,7 +38,7 @@ This guide will help you to: See [Installation](../../installation.mdx) for details. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Set up the Teleport Database Service diff --git a/docs/pages/database-access/guides/sql-server-ad.mdx b/docs/pages/database-access/guides/sql-server-ad.mdx index 92743aff71ed9..bafe5d90e66ed 100644 --- a/docs/pages/database-access/guides/sql-server-ad.mdx +++ b/docs/pages/database-access/guides/sql-server-ad.mdx @@ -44,8 +44,7 @@ Directory authentication. - A Windows machine joined to the same Active Directory domain as the database. - A Linux node joined to the same Active Directory domain as the database. This guide will walk you through the joining steps if you don't have one. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/7. Create a Teleport user diff --git a/docs/pages/desktop-access/directory-sharing.mdx b/docs/pages/desktop-access/directory-sharing.mdx index 8200ed73c2539..d3a87e50e8f6b 100644 --- a/docs/pages/desktop-access/directory-sharing.mdx +++ b/docs/pages/desktop-access/directory-sharing.mdx @@ -36,7 +36,7 @@ after the session ends. You can see a full compatibility table in the [Mozilla Developer Network documentation](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API#browser_compatibility). -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Share a directory diff --git a/docs/pages/desktop-access/getting-started.mdx b/docs/pages/desktop-access/getting-started.mdx index 0257dfa4c6e1f..de5e8f388ea26 100644 --- a/docs/pages/desktop-access/getting-started.mdx +++ b/docs/pages/desktop-access/getting-started.mdx @@ -37,8 +37,7 @@ an [Active Directory domain](./active-directory.mdx). You can reuse an existing server running any other Teleport instance. - A server or virtual machine running a Windows operating system with Remote Desktop enabled and the RDP port available to the Linux server. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Prepare Windows diff --git a/docs/pages/desktop-access/reference/cli.mdx b/docs/pages/desktop-access/reference/cli.mdx index a244da40c8353..474c0da1e16ff 100644 --- a/docs/pages/desktop-access/reference/cli.mdx +++ b/docs/pages/desktop-access/reference/cli.mdx @@ -5,7 +5,7 @@ description: CLI reference for Teleport Desktop Access. The following `tctl` commands are used to manage Teleport Desktop Access. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Generate a join token for a Windows Desktop Service: diff --git a/docs/pages/includes/tctl.mdx b/docs/pages/includes/tctl.mdx index 4b697ba811210..d759381f6a341 100644 --- a/docs/pages/includes/tctl.mdx +++ b/docs/pages/includes/tctl.mdx @@ -1,9 +1,4 @@ -
+ To connect to Teleport, log in to your cluster using `tsh`, then use `tctl` remotely: @@ -20,13 +15,8 @@ You can run subsequent `tctl` commands in this guide on your local machine. For full privileges, you can also run `tctl` commands on your Auth Service host. -
-
+ + To connect to Teleport, log in to your cluster using `tsh`, then use `tctl` remotely: @@ -41,4 +31,4 @@ $ tctl status You must run subsequent `tctl` commands in this guide on your local machine. -
+ diff --git a/docs/pages/kubernetes-access/discovery/google-cloud.mdx b/docs/pages/kubernetes-access/discovery/google-cloud.mdx index 73c379521e506..1befef790c74f 100644 --- a/docs/pages/kubernetes-access/discovery/google-cloud.mdx +++ b/docs/pages/kubernetes-access/discovery/google-cloud.mdx @@ -31,7 +31,7 @@ Auto-Discovery for GKE. services. You can run this host on any cloud provider or even use a local machine. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Obtain Google Cloud credentials diff --git a/docs/pages/kubernetes-access/getting-started.mdx b/docs/pages/kubernetes-access/getting-started.mdx index 39b311bb2bd92..b981dc3d0d0f9 100644 --- a/docs/pages/kubernetes-access/getting-started.mdx +++ b/docs/pages/kubernetes-access/getting-started.mdx @@ -27,7 +27,7 @@ Standalone Teleport Cluster](./register-clusters/static-kubeconfig.mdx). (!docs/pages/includes/kubernetes-access/helm-k8s.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Deployment overview diff --git a/docs/pages/kubernetes-access/manage-access/rbac.mdx b/docs/pages/kubernetes-access/manage-access/rbac.mdx index fe69c5d6b4416..8326f84ea7fb3 100644 --- a/docs/pages/kubernetes-access/manage-access/rbac.mdx +++ b/docs/pages/kubernetes-access/manage-access/rbac.mdx @@ -20,7 +20,7 @@ Kubernetes clusers, groups, users, and resources. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) To run the local demo environment, ensure that you have the following tools installed on your workstation: diff --git a/docs/pages/kubernetes-access/register-clusters/dynamic-registration.mdx b/docs/pages/kubernetes-access/register-clusters/dynamic-registration.mdx index 391abe1b304c0..0833f82c45fe3 100644 --- a/docs/pages/kubernetes-access/register-clusters/dynamic-registration.mdx +++ b/docs/pages/kubernetes-access/register-clusters/dynamic-registration.mdx @@ -32,7 +32,7 @@ registration, then create, list, update, and delete Kubernetes clusters via permissions to create namespaces, secrets, service accounts, cluster roles, and cluster role bindings in the cluster. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Set up the Teleport Kubernetes Service diff --git a/docs/pages/kubernetes-access/register-clusters/iam-joining.mdx b/docs/pages/kubernetes-access/register-clusters/iam-joining.mdx index 1546b70f94e38..dccfea7922e9d 100644 --- a/docs/pages/kubernetes-access/register-clusters/iam-joining.mdx +++ b/docs/pages/kubernetes-access/register-clusters/iam-joining.mdx @@ -27,7 +27,7 @@ to automatically join the cluster on subsequent restarts. (!docs/pages/includes/helm.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Create a Kubernetes service account with an IAM identity diff --git a/docs/pages/kubernetes-access/register-clusters/register-via-deployment.mdx b/docs/pages/kubernetes-access/register-clusters/register-via-deployment.mdx index d5a974e821d17..f936b94eeefcf 100644 --- a/docs/pages/kubernetes-access/register-clusters/register-via-deployment.mdx +++ b/docs/pages/kubernetes-access/register-clusters/register-via-deployment.mdx @@ -27,7 +27,7 @@ Teleport Kubernetes Service on each cluster you want to register. (!docs/pages/includes/helm.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Connecting clusters diff --git a/docs/pages/kubernetes-access/register-clusters/static-kubeconfig.mdx b/docs/pages/kubernetes-access/register-clusters/static-kubeconfig.mdx index 66860352c8056..aced14c6e5fef 100644 --- a/docs/pages/kubernetes-access/register-clusters/static-kubeconfig.mdx +++ b/docs/pages/kubernetes-access/register-clusters/static-kubeconfig.mdx @@ -23,8 +23,7 @@ authenticate to the API server of your chosen Kubernetes cluster. Kubernetes Service. This can run outside of your Kubernetes cluster. - The [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) command line tool installed on your workstation. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Generate a kubeconfig file diff --git a/docs/pages/machine-id/guides/applications.mdx b/docs/pages/machine-id/guides/applications.mdx index abfe5908fc5ae..ac760e0cbab32 100644 --- a/docs/pages/machine-id/guides/applications.mdx +++ b/docs/pages/machine-id/guides/applications.mdx @@ -26,7 +26,7 @@ If you're not already familiar with Machine ID, follow the [Getting Started Guide](../getting-started.mdx) to familiarize yourself with Machine ID. You'll also need `tctl` access to initially configure the bot. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Lastly, ensure the `tbot` binary is installed on your Machine ID client system. The client system is any system from which you want to access your Teleport diff --git a/docs/pages/machine-id/guides/circleci.mdx b/docs/pages/machine-id/guides/circleci.mdx index 81ee923c56cd8..62a93877b454e 100644 --- a/docs/pages/machine-id/guides/circleci.mdx +++ b/docs/pages/machine-id/guides/circleci.mdx @@ -25,8 +25,7 @@ control. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - A running instance of the Teleport SSH Service that you have registered with your Teleport cluster. For instructions on setting this up, see the [Getting Started Guide](../../server-access/introduction.mdx). The SSH node must diff --git a/docs/pages/machine-id/guides/databases.mdx b/docs/pages/machine-id/guides/databases.mdx index 97741e505e5fe..361e6aa7eafbb 100644 --- a/docs/pages/machine-id/guides/databases.mdx +++ b/docs/pages/machine-id/guides/databases.mdx @@ -34,7 +34,7 @@ If you have not already set up Machine ID, follow the [Machine ID Getting Started Guide](../getting-started.mdx) to familiarize yourself with Machine ID. You'll need `tctl` access to initially configure the bot. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Lastly, ensure both the `tbot` and `tsh` executables are available on your application host. See [Installation](../../installation.mdx) for details. diff --git a/docs/pages/machine-id/guides/github-actions-kubernetes.mdx b/docs/pages/machine-id/guides/github-actions-kubernetes.mdx index ed2a1e1e43ca3..5af4508bd67e8 100644 --- a/docs/pages/machine-id/guides/github-actions-kubernetes.mdx +++ b/docs/pages/machine-id/guides/github-actions-kubernetes.mdx @@ -27,7 +27,7 @@ Actions runners as well as GitHub Enterprise Server. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) - A Kubernetes cluster connected to your Teleport cluster. If you do not already have one configured, try our diff --git a/docs/pages/machine-id/guides/github-actions.mdx b/docs/pages/machine-id/guides/github-actions.mdx index cb6dcd353b61f..c6a1cbdbd1dc7 100644 --- a/docs/pages/machine-id/guides/github-actions.mdx +++ b/docs/pages/machine-id/guides/github-actions.mdx @@ -27,8 +27,7 @@ Actions runners as well as GitHub Enterprise Server. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - A node that is a part of the Teleport cluster with [Server Access](https://goteleport.com/docs/server-access/introduction/). - Your user should have the privileges to create token resources. - A GitHub repository with GitHub Actions enabled. This guide uses the example `gravitational/example` diff --git a/docs/pages/machine-id/guides/gitlab.mdx b/docs/pages/machine-id/guides/gitlab.mdx index 3042ae2608bba..09254c5964ce3 100644 --- a/docs/pages/machine-id/guides/gitlab.mdx +++ b/docs/pages/machine-id/guides/gitlab.mdx @@ -29,8 +29,7 @@ control. (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) - +- (!docs/pages/includes/tctl.mdx!) - A running instance of the Teleport SSH Service registered with your Teleport cluster. For instructions on setting this up, see the [Getting Started Guide](../../server-access/introduction.mdx). The SSH node must include a user diff --git a/docs/pages/machine-id/guides/jenkins.mdx b/docs/pages/machine-id/guides/jenkins.mdx index a1ad3bd3342ab..35d2ad6c3f3cc 100644 --- a/docs/pages/machine-id/guides/jenkins.mdx +++ b/docs/pages/machine-id/guides/jenkins.mdx @@ -34,7 +34,7 @@ proxy_service: ``` -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Architecture diff --git a/docs/pages/machine-id/guides/kubernetes.mdx b/docs/pages/machine-id/guides/kubernetes.mdx index d3087cec5ac2a..09033798e1ad9 100644 --- a/docs/pages/machine-id/guides/kubernetes.mdx +++ b/docs/pages/machine-id/guides/kubernetes.mdx @@ -28,7 +28,7 @@ If you're not already familiar with Machine ID, follow the [Getting Started Guide](../getting-started.mdx) to familiarize yourself with Machine ID. You'll also need `tctl` access to initially configure the bot. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Next, ensure the `tbot` binary is installed on your Machine ID client system. The client system is any system from which you want to access your Teleport diff --git a/docs/pages/management/admin/adding-nodes.mdx b/docs/pages/management/admin/adding-nodes.mdx index 090b2797425d7..43fce9d49a953 100644 --- a/docs/pages/management/admin/adding-nodes.mdx +++ b/docs/pages/management/admin/adding-nodes.mdx @@ -10,8 +10,7 @@ This guide explains how to add Teleport Nodes to your cluster. (!docs/pages/includes/edition-prereqs-tabs.mdx!) - A Linux server that you will use to host your Teleport Node. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Install Teleport on your Node @@ -45,7 +44,7 @@ print a warning message. The CA pin becomes invalid if a Teleport administrator performs the CA rotation by executing [`tctl auth rotate`](../../reference/cli.mdx#tctl-auth-rotate). - + On you local machine, retrieve the CA pin of the Auth Service -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/3. Enable verbose logging diff --git a/docs/pages/management/admin/users.mdx b/docs/pages/management/admin/users.mdx index 97008bc2c0614..7bb98f1115798 100644 --- a/docs/pages/management/admin/users.mdx +++ b/docs/pages/management/admin/users.mdx @@ -20,7 +20,7 @@ This guide shows you how to: (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Adding local users diff --git a/docs/pages/management/export-audit-events/datadog.mdx b/docs/pages/management/export-audit-events/datadog.mdx index 589c7e857242b..cf4a7a6246799 100644 --- a/docs/pages/management/export-audit-events/datadog.mdx +++ b/docs/pages/management/export-audit-events/datadog.mdx @@ -63,7 +63,7 @@ d-->h(Datadog) The instructions below demonstrate a local test of the Event Handler plugin on your workstation. You will need to adjust paths, ports, and domains for other environments. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/6. Install the Event Handler plugin diff --git a/docs/pages/management/export-audit-events/fluentd.mdx b/docs/pages/management/export-audit-events/fluentd.mdx index 95320910136b3..c281bfd56f432 100644 --- a/docs/pages/management/export-audit-events/fluentd.mdx +++ b/docs/pages/management/export-audit-events/fluentd.mdx @@ -44,9 +44,10 @@ to integrate with your infrastructure. (!docs/pages/includes/edition-prereqs-tabs.mdx!) - Fluentd version v(=fluentd.version=) or greater. + - Docker version v(=docker.version=). -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) - On your workstation, create a folder called `event-handler`, to hold configuration files and plugin state: diff --git a/docs/pages/management/export-audit-events/splunk.mdx b/docs/pages/management/export-audit-events/splunk.mdx index d2c4feb382a91..567ddd13d969c 100644 --- a/docs/pages/management/export-audit-events/splunk.mdx +++ b/docs/pages/management/export-audit-events/splunk.mdx @@ -38,7 +38,7 @@ visualization and alerting. - On Splunk Enterprise, port `8088` should be open to traffic from the host running the Teleport Event Handler and Universal Forwarder. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up the Teleport Event Handler plugin diff --git a/docs/pages/management/guides/joining-nodes-aws-ec2.mdx b/docs/pages/management/guides/joining-nodes-aws-ec2.mdx index 3de48af8da2b7..9fb5c447813ec 100644 --- a/docs/pages/management/guides/joining-nodes-aws-ec2.mdx +++ b/docs/pages/management/guides/joining-nodes-aws-ec2.mdx @@ -89,7 +89,7 @@ more in the following guide: -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up AWS IAM credentials diff --git a/docs/pages/management/guides/joining-nodes-aws-iam.mdx b/docs/pages/management/guides/joining-nodes-aws-iam.mdx index 8ff4c11a32d1d..35deae6191c9b 100644 --- a/docs/pages/management/guides/joining-nodes-aws-iam.mdx +++ b/docs/pages/management/guides/joining-nodes-aws-iam.mdx @@ -64,10 +64,10 @@ connecting directly to the Auth Service. - An AWS EC2 instance to act as a Teleport Node, with the Teleport binary installed. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up AWS IAM credentials + Every Node or Proxy using the IAM method to join your Teleport cluster needs AWS IAM credentials in order to call the `sts:GetCallerIdentity` API. No specific IAM policy or permissions are needed. Any IAM user or role can call this API. diff --git a/docs/pages/management/guides/joining-nodes-azure.mdx b/docs/pages/management/guides/joining-nodes-azure.mdx index 77a676f524e65..37195e9c2a238 100644 --- a/docs/pages/management/guides/joining-nodes-azure.mdx +++ b/docs/pages/management/guides/joining-nodes-azure.mdx @@ -40,10 +40,10 @@ connecting directly to the Auth Service. installed. The Virtual Machine must have a [Managed Identity](https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) assigned to it with permission to read virtual machine info. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Set up a Managed Identity + Every virtual machine hosting a Node or Proxy using the Azure method to join your Teleport cluster needs a Managed Identity assigned to it. The identity requires the `Microsoft.Compute/virtualMachines/read` permission so Teleport diff --git a/docs/pages/management/guides/terraform-provider.mdx b/docs/pages/management/guides/terraform-provider.mdx index 6340ae44991cf..6573397552e4c 100644 --- a/docs/pages/management/guides/terraform-provider.mdx +++ b/docs/pages/management/guides/terraform-provider.mdx @@ -20,7 +20,7 @@ This guide will explain how to: # Terraform v(=terraform.version=) ``` -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) Create a folder called `teleport-terraform` to hold some temporary files: diff --git a/docs/pages/management/operations/ca-rotation.mdx b/docs/pages/management/operations/ca-rotation.mdx index 00a3feefe1981..4a4e51830972d 100644 --- a/docs/pages/management/operations/ca-rotation.mdx +++ b/docs/pages/management/operations/ca-rotation.mdx @@ -7,7 +7,7 @@ description: How to rotate Teleport's certificate authority (!docs/pages/includes/edition-prereqs-tabs.mdx!) -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Certificate Authority rotation diff --git a/docs/pages/management/security/reduce-blast-radius.mdx b/docs/pages/management/security/reduce-blast-radius.mdx index 9ef1b896b3b83..357e36bde2fbb 100644 --- a/docs/pages/management/security/reduce-blast-radius.mdx +++ b/docs/pages/management/security/reduce-blast-radius.mdx @@ -11,10 +11,10 @@ Teleport encourages users to practice defense in depth so that every component o - [Automatically prevent some roles from requesting others](#automatically-prevent-some-roles-from-requesting-others) - [Restrict role requests based on user traits](#restrict-role-requests-based-on-user-traits) - [Set up your RBAC without admin roles](#set-up-your-rbac-without-admin-roles) - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Make MFA mandatory for `tsh login` + If a user sets up an account to authenticate to their Teleport cluster with only a password, an adversary can gain access to the password using brute-force attacks, person-in-the-middle attacks, or phishing. But even if a user's password is compromised, you can stop an attacker from authenticating with it when they run `tsh login`. Teleport lets you make it mandatory for a user to enroll an MFA device when they create an account, and to authenticate using that device when they begin a new Teleport session. diff --git a/docs/pages/reference/resources.mdx b/docs/pages/reference/resources.mdx index d9524a0896d77..7fa009bbdf959 100644 --- a/docs/pages/reference/resources.mdx +++ b/docs/pages/reference/resources.mdx @@ -26,7 +26,7 @@ users. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ### `tctl` concepts diff --git a/docs/pages/server-access/getting-started.mdx b/docs/pages/server-access/getting-started.mdx index 4cc16d9057a42..e899e6c91decf 100644 --- a/docs/pages/server-access/getting-started.mdx +++ b/docs/pages/server-access/getting-started.mdx @@ -32,8 +32,7 @@ pattern** so that only a single Node can be accessed publicly. - One host running a Linux environment (such as Ubuntu 20.04, CentOS 8.0, or Debian 10). This will serve as a Teleport Node. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) (!docs/pages/includes/permission-warning.mdx!) diff --git a/docs/pages/server-access/guides/auditd.mdx b/docs/pages/server-access/guides/auditd.mdx index 7f8dd3c79d428..a3c46559bb158 100644 --- a/docs/pages/server-access/guides/auditd.mdx +++ b/docs/pages/server-access/guides/auditd.mdx @@ -13,8 +13,7 @@ You can configure Teleport's SSH Service to integrate with the Linux Auditing Sy - A running Teleport Node. See the [Server Access Getting Started Guide](../getting-started.mdx) for how to add a Node to your Teleport cluster. On the Node, `teleport` must be running as a systemd service with root permissions. - Linux kernel 2.6.6+ compiled with `CONFIG_AUDIT`. Most Linux distributions have this option enabled by default. - `auditctl` to check auditd status (optional). - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Check system configuration @@ -36,6 +35,7 @@ backlog_wait_time 60000 backlog_wait_time_actual 0 loginuid_immutable 0 unlocked ``` + The first line `enabled 1` indicates that auditd is enabled, and Teleport will send events. All events are generated on a Teleport Node. diff --git a/docs/pages/server-access/guides/azure-discovery.mdx b/docs/pages/server-access/guides/azure-discovery.mdx index e4fa7225bf0d5..022136deb4ad1 100644 --- a/docs/pages/server-access/guides/azure-discovery.mdx +++ b/docs/pages/server-access/guides/azure-discovery.mdx @@ -17,8 +17,7 @@ managed identities. - Azure virtual machines to join the Teleport cluster, running Ubuntu/Debian/RHEL if making use of the default Teleport install script. (For other Linux distributions, you can install Teleport manually.) - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/5. Create an Azure invite token diff --git a/docs/pages/server-access/guides/bpf-session-recording.mdx b/docs/pages/server-access/guides/bpf-session-recording.mdx index 3bf0a5b028177..60206b5f490df 100644 --- a/docs/pages/server-access/guides/bpf-session-recording.mdx +++ b/docs/pages/server-access/guides/bpf-session-recording.mdx @@ -93,7 +93,7 @@ library preloading, and environment variables may not be captured in session rec -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/2. Configure a Teleport Node diff --git a/docs/pages/server-access/guides/ec2-discovery.mdx b/docs/pages/server-access/guides/ec2-discovery.mdx index 6819849dad1c7..c6b600ee97e67 100644 --- a/docs/pages/server-access/guides/ec2-discovery.mdx +++ b/docs/pages/server-access/guides/ec2-discovery.mdx @@ -21,8 +21,7 @@ policies. - EC2 instances running Ubuntu/Debian/RHEL/Amazon Linux 2 and SSM agent version 3.1 or greater if making use of the default Teleport install script. (For other Linux distributions, you can install Teleport manually.) - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/7. Create an EC2 invite token diff --git a/docs/pages/server-access/guides/host-user-creation.mdx b/docs/pages/server-access/guides/host-user-creation.mdx index b54399f399c9c..e8dfb9242bf3d 100644 --- a/docs/pages/server-access/guides/host-user-creation.mdx +++ b/docs/pages/server-access/guides/host-user-creation.mdx @@ -29,7 +29,7 @@ since it must execute these commands in order to create transient users: - `getent` - `visudo` -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/2. Configure a role diff --git a/docs/pages/server-access/guides/openssh.mdx b/docs/pages/server-access/guides/openssh.mdx index 1a1a30044e1f4..01bc5d32e13ee 100644 --- a/docs/pages/server-access/guides/openssh.mdx +++ b/docs/pages/server-access/guides/openssh.mdx @@ -26,8 +26,7 @@ We've outlined these reasons in [OpenSSH vs Teleport SSH for Servers?](https://g - A Linux host with the OpenSSH server `sshd` installed, but not Teleport. The SSH port on this host must be open to traffic from the Teleport Proxy Service host. - -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Step 1/4. Configure `sshd` to trust the Teleport CA diff --git a/docs/pages/server-access/guides/restricted-session.mdx b/docs/pages/server-access/guides/restricted-session.mdx index 1f4b707c6ebd9..dd5227b28364f 100644 --- a/docs/pages/server-access/guides/restricted-session.mdx +++ b/docs/pages/server-access/guides/restricted-session.mdx @@ -37,7 +37,7 @@ Teleport supports network restrictions with more types coming in the future. -(!docs/pages/includes/tctl.mdx!) +- (!docs/pages/includes/tctl.mdx!) ## Network Restrictions diff --git a/docs/pages/server-access/rbac.mdx b/docs/pages/server-access/rbac.mdx index 58f361b51c399..d3706ef264a3d 100644 --- a/docs/pages/server-access/rbac.mdx +++ b/docs/pages/server-access/rbac.mdx @@ -3,7 +3,6 @@ title: Access Controls for Servers description: Role-based access control (RBAC) for Teleport Server Access. --- - You can use Teleport's role-based access control (RBAC) system to set up granular permissions for authenticating to Linux servers connected to Teleport. @@ -15,6 +14,14 @@ emergency.* For a more general description of Teleport roles and examples see our [Access Controls guides](../access-controls/introduction.mdx), as this section focuses on configuring RBAC for servers connected to Teleport. + + +Teleport's RBAC system does not extend to OpenSSH servers, as it is not possible +to apply labels to servers running `sshd`. You must enable access to these +servers via the Teleport SSH Service instead. + + + ## Role configuration Teleport's "role" resource provides the following instruments for restricting diff --git a/e b/e index d5774ee0f62d6..8bf04c9df27ec 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit d5774ee0f62d66499226ca1817d6c24fe607b778 +Subproject commit 8bf04c9df27ecb88230fb32909e54eade0d8b7af diff --git a/examples/bench/example.go b/examples/bench/example.go index 1d925c014f027..00b0c5b699cb9 100644 --- a/examples/bench/example.go +++ b/examples/bench/example.go @@ -19,6 +19,7 @@ import ( "context" "fmt" "os" + "strings" "time" "github.com/gravitational/teleport/lib/benchmark" @@ -34,7 +35,16 @@ func main() { } // Run Linear generator - results, err := benchmark.Run(context.TODO(), linear, "ls -l /", "host", "username", "teleport.example.com") + results, err := benchmark.Run( + context.TODO(), + linear, + "host", + "username", + "teleport.example.com", + benchmark.SSHBenchmark{ + Command: strings.Split("ls -l /", " "), + }, + ) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/examples/chart/teleport-cluster/templates/auth/predeploy_config.yaml b/examples/chart/teleport-cluster/templates/auth/predeploy_config.yaml index fcbcab24572c7..14194400dd22f 100644 --- a/examples/chart/teleport-cluster/templates/auth/predeploy_config.yaml +++ b/examples/chart/teleport-cluster/templates/auth/predeploy_config.yaml @@ -10,7 +10,7 @@ metadata: annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "4" - "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded data: {{- if $auth.createProxyToken }} apply-on-startup.yaml: |2 diff --git a/examples/chart/teleport-cluster/templates/auth/predeploy_job.yaml b/examples/chart/teleport-cluster/templates/auth/predeploy_job.yaml index 495d2d781d7a3..0b2357249b009 100644 --- a/examples/chart/teleport-cluster/templates/auth/predeploy_job.yaml +++ b/examples/chart/teleport-cluster/templates/auth/predeploy_job.yaml @@ -9,7 +9,7 @@ metadata: annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "5" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: backoffLimit: 1 template: diff --git a/examples/chart/teleport-cluster/templates/proxy/predeploy_config.yaml b/examples/chart/teleport-cluster/templates/proxy/predeploy_config.yaml index c72214efdbf72..6e2d374bec292 100644 --- a/examples/chart/teleport-cluster/templates/proxy/predeploy_config.yaml +++ b/examples/chart/teleport-cluster/templates/proxy/predeploy_config.yaml @@ -9,7 +9,7 @@ metadata: annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "4" - "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded data: teleport.yaml: |2 {{- mustMergeOverwrite (include $configTemplate . | fromYaml) $proxy.teleportConfig | toYaml | nindent 4 -}} diff --git a/examples/chart/teleport-cluster/templates/proxy/predeploy_job.yaml b/examples/chart/teleport-cluster/templates/proxy/predeploy_job.yaml index eeed525383296..2efc7eaab84b9 100644 --- a/examples/chart/teleport-cluster/templates/proxy/predeploy_job.yaml +++ b/examples/chart/teleport-cluster/templates/proxy/predeploy_job.yaml @@ -9,7 +9,7 @@ metadata: annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "5" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: backoffLimit: 1 template: diff --git a/examples/chart/teleport-kube-agent/templates/delete_hook.yaml b/examples/chart/teleport-kube-agent/templates/delete_hook.yaml index fe0d78d990893..024b15a2b4871 100644 --- a/examples/chart/teleport-kube-agent/templates/delete_hook.yaml +++ b/examples/chart/teleport-kube-agent/templates/delete_hook.yaml @@ -6,7 +6,7 @@ metadata: annotations: "helm.sh/hook": post-delete "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -16,7 +16,7 @@ metadata: annotations: "helm.sh/hook": post-delete "helm.sh/hook-weight": "-3" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded rules: - apiGroups: [""] resources: ["secrets",] @@ -30,7 +30,7 @@ metadata: annotations: "helm.sh/hook": post-delete "helm.sh/hook-weight": "-2" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -48,7 +48,7 @@ metadata: annotations: "helm.sh/hook": post-delete "helm.sh/hook-weight": "-1" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: template: metadata: @@ -81,4 +81,4 @@ spec: imagePullPolicy: {{ toYaml .Values.imagePullPolicy }} {{- end }} command: ["teleport"] - args: ["kube-state", "delete"] \ No newline at end of file + args: ["kube-state", "delete"] diff --git a/examples/chart/teleport-kube-agent/templates/hook.yaml b/examples/chart/teleport-kube-agent/templates/hook.yaml index 5b7bd719c9baa..67ff9fe991618 100644 --- a/examples/chart/teleport-kube-agent/templates/hook.yaml +++ b/examples/chart/teleport-kube-agent/templates/hook.yaml @@ -8,7 +8,7 @@ metadata: annotations: "helm.sh/hook": post-upgrade "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -18,7 +18,7 @@ metadata: annotations: "helm.sh/hook": post-upgrade "helm.sh/hook-weight": "-3" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded rules: - apiGroups: ["apps"] resources: ["statefulsets"] @@ -40,7 +40,7 @@ metadata: annotations: "helm.sh/hook": post-upgrade "helm.sh/hook-weight": "-2" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -58,7 +58,7 @@ metadata: annotations: "helm.sh/hook": post-upgrade "helm.sh/hook-weight": "-1" - "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: template: metadata: @@ -87,4 +87,4 @@ spec: # delete deployment kubectl delete deployment/{{ .Release.Name }} EOF -{{- end}} \ No newline at end of file +{{- end}} diff --git a/gen/proto/go/prehog/v1alpha/connect.pb.go b/gen/proto/go/prehog/v1alpha/connect.pb.go index a398438e9bb56..d00f0cc12d7a3 100644 --- a/gen/proto/go/prehog/v1alpha/connect.pb.go +++ b/gen/proto/go/prehog/v1alpha/connect.pb.go @@ -144,6 +144,8 @@ type ConnectProtocolUseEvent struct { UserName string `protobuf:"bytes,2,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` // one of ssh/db/kube Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` + // one of resource_table/search_bar/connection_list/reopened_session (optional) + Origin string `protobuf:"bytes,4,opt,name=origin,proto3" json:"origin,omitempty"` } func (x *ConnectProtocolUseEvent) Reset() { @@ -199,6 +201,13 @@ func (x *ConnectProtocolUseEvent) GetProtocol() string { return "" } +func (x *ConnectProtocolUseEvent) GetOrigin() string { + if x != nil { + return x.Origin + } + return "" +} + type ConnectAccessRequestCreateEvent struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -721,112 +730,113 @@ var file_prehog_v1alpha_connect_proto_rawDesc = []byte{ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x70, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x75, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x73, 0x65, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x75, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8d, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x73, 0x65, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x22, 0x75, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x61, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x61, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, + 0x65, 0x22, 0x65, 0x0a, 0x23, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, + 0x6f, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x23, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0x7a, 0x0a, 0x1b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x69, 0x73, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x3a, 0x0a, 0x1d, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, - 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, 0x22, 0x9d, 0x06, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x74, - 0x69, 0x6e, 0x63, 0x74, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 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, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x12, 0x4f, 0x0a, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, + 0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x7a, 0x0a, 0x1b, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, + 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x3a, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x6f, 0x6c, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, + 0x22, 0x9d, 0x06, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, + 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x49, 0x64, 0x12, + 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x0d, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x28, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x55, 0x73, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x75, 0x73, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x73, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x73, 0x65, 0x12, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x65, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, + 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x65, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x72, 0x0a, - 0x1a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x61, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x17, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, - 0x65, 0x12, 0x59, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, - 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x66, 0x69, 0x6c, - 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x12, 0x60, 0x0a, 0x14, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x65, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x72, 0x0a, 0x1a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x5f, + 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x75, 0x73, 0x65, - 0x72, 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x07, - 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x88, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, - 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x63, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x17, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x59, 0x0a, 0x11, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x52, 0x75, 0x6e, 0x12, 0x60, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6a, 0x6f, + 0x62, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x52, 0x6f, 0x6c, + 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x88, + 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, 0x12, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x29, 0x2e, 0x70, 0x72, 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x72, + 0x65, 0x68, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2e, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, + 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x68, + 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x3b, 0x70, 0x72, 0x65, 0x68, 0x6f, + 0x67, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/proto/js/prehog/v1alpha/connect_pb.d.ts b/gen/proto/js/prehog/v1alpha/connect_pb.d.ts index b3995986e7f95..ff5eda22009a8 100644 --- a/gen/proto/js/prehog/v1alpha/connect_pb.d.ts +++ b/gen/proto/js/prehog/v1alpha/connect_pb.d.ts @@ -62,6 +62,9 @@ export class ConnectProtocolUseEvent extends jspb.Message { getProtocol(): string; setProtocol(value: string): ConnectProtocolUseEvent; + getOrigin(): string; + setOrigin(value: string): ConnectProtocolUseEvent; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ConnectProtocolUseEvent.AsObject; @@ -78,6 +81,7 @@ export namespace ConnectProtocolUseEvent { clusterName: string, userName: string, protocol: string, + origin: string, } } diff --git a/gen/proto/js/prehog/v1alpha/connect_pb.js b/gen/proto/js/prehog/v1alpha/connect_pb.js index 31114b8c9c074..7e365b0ad48cc 100644 --- a/gen/proto/js/prehog/v1alpha/connect_pb.js +++ b/gen/proto/js/prehog/v1alpha/connect_pb.js @@ -560,7 +560,8 @@ proto.prehog.v1alpha.ConnectProtocolUseEvent.toObject = function(includeInstance var f, obj = { clusterName: jspb.Message.getFieldWithDefault(msg, 1, ""), userName: jspb.Message.getFieldWithDefault(msg, 2, ""), - protocol: jspb.Message.getFieldWithDefault(msg, 3, "") + protocol: jspb.Message.getFieldWithDefault(msg, 3, ""), + origin: jspb.Message.getFieldWithDefault(msg, 4, "") }; if (includeInstance) { @@ -609,6 +610,10 @@ proto.prehog.v1alpha.ConnectProtocolUseEvent.deserializeBinaryFromReader = funct var value = /** @type {string} */ (reader.readString()); msg.setProtocol(value); break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setOrigin(value); + break; default: reader.skipField(); break; @@ -659,6 +664,13 @@ proto.prehog.v1alpha.ConnectProtocolUseEvent.serializeBinaryToWriter = function( f ); } + f = message.getOrigin(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } }; @@ -716,6 +728,24 @@ proto.prehog.v1alpha.ConnectProtocolUseEvent.prototype.setProtocol = function(va }; +/** + * optional string origin = 4; + * @return {string} + */ +proto.prehog.v1alpha.ConnectProtocolUseEvent.prototype.getOrigin = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * @param {string} value + * @return {!proto.prehog.v1alpha.ConnectProtocolUseEvent} returns this + */ +proto.prehog.v1alpha.ConnectProtocolUseEvent.prototype.setOrigin = function(value) { + return jspb.Message.setProto3StringField(this, 4, value); +}; + + diff --git a/go.mod b/go.mod index ebe72b4a4cd6e..2a25727060c41 100644 --- a/go.mod +++ b/go.mod @@ -131,7 +131,7 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 go.opentelemetry.io/proto/otlp v0.19.0 golang.org/x/crypto v0.7.0 // replaced - golang.org/x/exp v0.0.0-20230116083435-1de6713980de + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb golang.org/x/mod v0.9.0 golang.org/x/net v0.8.0 golang.org/x/oauth2 v0.6.0 @@ -333,7 +333,7 @@ require ( github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/thales-e-security/pool v0.0.2 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.1 // indirect @@ -377,7 +377,7 @@ replace ( github.com/gravitational/teleport/api => ./api github.com/julienschmidt/httprouter => github.com/gravitational/httprouter v1.3.1-0.20220408074523-c876c5e705a5 github.com/keys-pub/go-libfido2 => github.com/gravitational/go-libfido2 v1.5.3-0.20230202181331-c71192ef1c8a - github.com/microsoft/go-mssqldb => github.com/gravitational/go-mssqldb v0.11.1-0.20230209180904-0f00ae61fb60 + github.com/microsoft/go-mssqldb => github.com/gravitational/go-mssqldb v0.11.1-0.20230331180905-0f76f1751cd3 github.com/pkg/sftp => github.com/gravitational/sftp v1.13.6-0.20230328150159-dfe4e0d94419 github.com/sirupsen/logrus => github.com/gravitational/logrus v1.4.4-0.20210817004754-047e20245621 github.com/vulcand/predicate => github.com/gravitational/predicate v1.3.0 diff --git a/go.sum b/go.sum index 9ee5233f8e9c4..e7a80fa5d5ee1 100644 --- a/go.sum +++ b/go.sum @@ -622,8 +622,8 @@ github.com/gravitational/go-cassandra-native-protocol v0.0.0-20221005103706-b9e6 github.com/gravitational/go-cassandra-native-protocol v0.0.0-20221005103706-b9e66c056e90/go.mod h1:6FzirJfdffakAVqmHjwVfFkpru/gNbIazUOK5rIhndc= github.com/gravitational/go-libfido2 v1.5.3-0.20230202181331-c71192ef1c8a h1:Wudq53GAiVk1Z+4A1tFzyvidhA6X7rGDb5JKGU9NV0c= github.com/gravitational/go-libfido2 v1.5.3-0.20230202181331-c71192ef1c8a/go.mod h1:92J9LtSBl0UyUWljElJpTbMMNhC6VeY8dshsu40qjjo= -github.com/gravitational/go-mssqldb v0.11.1-0.20230209180904-0f00ae61fb60 h1:scheLo0b1jteZf9mfgjCgi+0VFGK+3GVENBQQ1DDmfE= -github.com/gravitational/go-mssqldb v0.11.1-0.20230209180904-0f00ae61fb60/go.mod h1:+8BKOZF/H/PTp3h+jsAcA4z5LVmG4vATYcuOs3CfY6k= +github.com/gravitational/go-mssqldb v0.11.1-0.20230331180905-0f76f1751cd3 h1:3JGTacvAeV5tIC4/9XsdLC2K5K7FWaXxIwpW4t+dGH0= +github.com/gravitational/go-mssqldb v0.11.1-0.20230331180905-0f76f1751cd3/go.mod h1:LbRWqr72fXehxAGLXO8nDNQAi4gthRz4j7f8L2LS0XM= github.com/gravitational/go-mysql v1.5.0-teleport.1 h1:EyFryeiTYyP6KslLVp0Q5QTKwtUG5RioVrTIoP4pOuI= github.com/gravitational/go-mysql v1.5.0-teleport.1/go.mod h1:GX0clmylJLdZEYAojPCDTCvwZxbTBrke93dV55715u0= github.com/gravitational/go-oidc v0.1.0 h1:qZkEZCuD7DfPbpFfewvRmDvq82XoDsmZtqgpQ0Ep2i4= @@ -1192,8 +1192,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1320,8 +1320,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20230116083435-1de6713980de h1:DBWn//IJw30uYCgERoxCg84hWtA97F4wMiKOIh00Uf0= -golang.org/x/exp v0.0.0-20230116083435-1de6713980de/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1487,7 +1487,6 @@ golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/integration/integration_test.go b/integration/integration_test.go index b33ab0d56bb16..06a2330311130 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -212,25 +212,30 @@ func testDifferentPinnedIP(t *testing.T, suite *integrationTestSuite) { site := teleInstance.GetSiteAPI(helpers.Site) require.NotNil(t, site) + accessDenied := func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err, i...) + require.True(t, trace.IsAccessDenied(err), "expected an access denied error, got: %v", err) + } + testCases := []struct { - desc string - ip string - wantErr string + desc string + ip string + errAssertion require.ErrorAssertionFunc }{ { - desc: "Correct connecting IP", - ip: "127.0.0.1", - wantErr: "", + desc: "Correct connecting IP", + ip: "127.0.0.1", + errAssertion: require.NoError, }, { - desc: "Wrong connecting IPv4", - ip: "1.2.3.4", - wantErr: "ssh: unable to authenticate", + desc: "Wrong connecting IPv4", + ip: "1.2.3.4", + errAssertion: accessDenied, }, { - desc: "Wrong connecting IPv6", - ip: "1843:4545::12", - wantErr: "ssh: unable to authenticate", + desc: "Wrong connecting IPv6", + ip: "1843:4545::12", + errAssertion: accessDenied, }, } @@ -243,15 +248,9 @@ func testDifferentPinnedIP(t *testing.T, suite *integrationTestSuite) { SourceIP: test.ip, }) require.NoError(t, err) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - err = cl.SSH(ctx, []string{"echo hi"}, false) - if test.wantErr != "" { - require.Error(t, err) - require.Contains(t, err.Error(), "ssh: unable to authenticate") - } else { - require.NoError(t, err) - } + test.errAssertion(t, cl.SSH(ctx, []string{"echo hi"}, false)) }) } } @@ -1279,6 +1278,29 @@ func testEscapeSequenceNoTrigger(t *testing.T, terminal *Terminal, sess <-chan e } } +type localAddr struct { + mu sync.Mutex + addr net.Addr +} + +func (a *localAddr) set(addr net.Addr) { + a.mu.Lock() + defer a.mu.Unlock() + + a.addr = addr +} + +func (a *localAddr) get() net.Addr { + a.mu.Lock() + defer a.mu.Unlock() + + if a.addr == nil { + return &utils.NetAddr{} + } + + return a.addr +} + // testIPPropagation makes sure that we can correctly propagate initial client IP observed by proxy. func testIPPropagation(t *testing.T, suite *integrationTestSuite) { tr := utils.NewTracer(utils.ThisFunction()).Start() @@ -1341,7 +1363,7 @@ func testIPPropagation(t *testing.T, suite *integrationTestSuite) { wg.Wait() } - testNodeConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName, nodeName string) { + testSSHNodeConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName, nodeName string) { person := NewTerminal(250) ctx := context.Background() @@ -1355,23 +1377,112 @@ func testIPPropagation(t *testing.T, suite *integrationTestSuite) { tc.Stdout = person tc.Stdin = person - pc, err := tc.ConnectToProxy(ctx) + clt, err := tc.ConnectToProxy(ctx) + require.NoError(t, err) + defer clt.Close() + + nodeClient, err := clt.ConnectToNode( + ctx, + client.NodeDetails{Addr: nodeName, Namespace: tc.Namespace, Cluster: tc.SiteName}, + tc.Config.HostLogin, + sshutils.ClusterDetails{}, + ) + require.NoError(t, err) + defer nodeClient.Close() + + err = nodeClient.RunCommand(ctx, []string{"echo $SSH_CLIENT"}) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return getRemoteAddrString(person.Output(1000)) == clt.Client.LocalAddr().String() + }, time.Millisecond*100, time.Millisecond*10, "client IP:port that node sees doesn't match to real one") + } + + testGRPCNodeConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName, nodeName string) { + person := NewTerminal(250) + ctx := context.Background() + + tc, err := instance.NewClient(helpers.ClientConfig{ + Login: suite.Me.Username, + Cluster: clusterName, + Host: nodeName, + }) require.NoError(t, err) - defer pc.Close() - tc.Config.MockConnectToProxy = func(ctx context.Context) (*client.ProxyClient, error) { - return pc, nil + tc.Stdout = person + tc.Stdin = person + + local := &localAddr{} + + tc.Config.DialOpts = []grpc.DialOption{ + grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) { + d := net.Dialer{Timeout: defaults.DefaultIOTimeout} + conn, err := d.DialContext(ctx, "tcp", s) + if err != nil { + return nil, trace.Wrap(err) + } + + local.set(conn.LocalAddr()) + return conn, nil + }), } - err = tc.SSH(ctx, []string{"echo $SSH_CLIENT"}, false) + clt, err := tc.ConnectToCluster(ctx) + require.NoError(t, err) + defer clt.Close() + + nodeClient, err := tc.ConnectToNode( + ctx, + clt, + client.NodeDetails{Addr: nodeName, Namespace: tc.Namespace, Cluster: clt.ClusterName()}, + tc.Config.HostLogin, + ) + require.NoError(t, err) + defer nodeClient.Close() + + err = nodeClient.RunCommand(ctx, []string{"echo $SSH_CLIENT"}) require.NoError(t, err) require.Eventually(t, func() bool { - return pc.Client.LocalAddr().String() == getRemoteAddrString(person.Output(1000)) + return getRemoteAddrString(person.Output(1000)) == local.get().String() }, time.Millisecond*100, time.Millisecond*10, "client IP:port that node sees doesn't match to real one") } - testAuthConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName string) { + testGRPCAuthConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName string) { + ctx := context.Background() + + tc, err := instance.NewClient(helpers.ClientConfig{ + Login: suite.Me.Username, + Cluster: clusterName, + Host: Host, + }) + require.NoError(t, err) + + local := &localAddr{} + + tc.Config.DialOpts = []grpc.DialOption{ + grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) { + d := net.Dialer{Timeout: defaults.DefaultIOTimeout} + conn, err := d.DialContext(ctx, "tcp", s) + if err != nil { + return nil, trace.Wrap(err) + } + + local.set(conn.LocalAddr()) + return conn, nil + }), + } + + clt, err := tc.ConnectToCluster(ctx) + require.NoError(t, err) + defer clt.Close() + + pingResp, err := clt.AuthClient.Ping(ctx) + require.NoError(t, err) + require.Equal(t, local.get().String(), pingResp.RemoteAddr, "client IP:port that auth server sees doesn't match the real one") + } + + testSSHAuthConnection := func(t *testing.T, instance *helpers.TeleInstance, clusterName string) { ctx := context.Background() tc, err := instance.NewClient(helpers.ClientConfig{ @@ -1381,17 +1492,17 @@ func testIPPropagation(t *testing.T, suite *integrationTestSuite) { }) require.NoError(t, err) - pc, err := tc.ConnectToProxy(ctx) + clt, err := tc.ConnectToProxy(ctx) require.NoError(t, err) - defer pc.Close() + defer clt.Close() - site, err := pc.ConnectToCluster(ctx, clusterName) + site, err := clt.ConnectToCluster(ctx, clusterName) require.NoError(t, err) pingResp, err := site.Ping(ctx) require.NoError(t, err) - expected := pc.Client.LocalAddr().String() + expected := clt.Client.LocalAddr().String() require.Equal(t, expected, pingResp.RemoteAddr, "client IP:port that auth server sees doesn't match the real one") } _, root, leaf := createTrustedClusterPair(t, suite, startNodes) @@ -1407,32 +1518,49 @@ func testIPPropagation(t *testing.T, suite *integrationTestSuite) { testNodeCases := []struct { instance *helpers.TeleInstance clusterName string - nodeNme string + nodeAddr string }{ - {instance: root, clusterName: "root-test", nodeNme: "root-zero"}, - {instance: root, clusterName: "root-test", nodeNme: "root-one"}, - {instance: root, clusterName: "root-test", nodeNme: "root-two"}, - {instance: root, clusterName: "leaf-test", nodeNme: "leaf-zero"}, - {instance: root, clusterName: "leaf-test", nodeNme: "leaf-one"}, - {instance: root, clusterName: "leaf-test", nodeNme: "leaf-two"}, - {instance: leaf, clusterName: "leaf-test", nodeNme: "leaf-zero"}, - {instance: leaf, clusterName: "leaf-test", nodeNme: "leaf-one"}, - {instance: leaf, clusterName: "leaf-test", nodeNme: "leaf-two"}, - } - - for _, test := range testAuthCases { - t.Run(fmt.Sprintf("Auth test source cluster %q -> target cluster %q", - test.instance.Secrets.SiteName, test.clusterName), func(t *testing.T) { - testAuthConnection(t, test.instance, test.clusterName) - }) - } - for _, test := range testNodeCases { - test := test - t.Run(fmt.Sprintf("Node test, node name %q source cluster %q -> target cluster %q", - test.nodeNme, test.instance.Secrets.SiteName, test.clusterName), func(t *testing.T) { - testNodeConnection(t, test.instance, test.clusterName, test.nodeNme) - }) - } + {instance: root, clusterName: "root-test", nodeAddr: "root-zero:0"}, + {instance: root, clusterName: "root-test", nodeAddr: "root-one:0"}, + {instance: root, clusterName: "root-test", nodeAddr: "root-two:0"}, + {instance: root, clusterName: "leaf-test", nodeAddr: "leaf-zero:0"}, + {instance: root, clusterName: "leaf-test", nodeAddr: "leaf-one:0"}, + {instance: root, clusterName: "leaf-test", nodeAddr: "leaf-two:0"}, + {instance: leaf, clusterName: "leaf-test", nodeAddr: "leaf-zero:0"}, + {instance: leaf, clusterName: "leaf-test", nodeAddr: "leaf-one:0"}, + {instance: leaf, clusterName: "leaf-test", nodeAddr: "leaf-two:0"}, + } + + t.Run("Auth Connections", func(t *testing.T) { + for _, test := range testAuthCases { + t.Run(fmt.Sprintf("source cluster=%q target cluster=%q", + test.instance.Secrets.SiteName, test.clusterName), func(t *testing.T) { + t.Run("ssh connection", func(t *testing.T) { + testSSHAuthConnection(t, test.instance, test.clusterName) + }) + + t.Run("grpc connection", func(t *testing.T) { + testGRPCAuthConnection(t, test.instance, test.clusterName) + }) + }) + } + }) + + t.Run("Host Connections", func(t *testing.T) { + for _, test := range testNodeCases { + test := test + t.Run(fmt.Sprintf("target=%q source cluster=%q target cluster=%q", + test.nodeAddr, test.instance.Secrets.SiteName, test.clusterName), func(t *testing.T) { + t.Run("ssh connection", func(t *testing.T) { + testSSHNodeConnection(t, test.instance, test.clusterName, test.nodeAddr) + }) + + t.Run("grpc connection", func(t *testing.T) { + testGRPCNodeConnection(t, test.instance, test.clusterName, test.nodeAddr) + }) + }) + } + }) } // verifySessionJoin covers SSH into shell and joining the same session from another client @@ -1670,6 +1798,7 @@ func errorContains(text string) errorVerifier { } type disconnectTestCase struct { + name string recordingMode string options types.RoleOptions disconnectTimeout time.Duration @@ -1692,6 +1821,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { testCases := []disconnectTestCase{ { + name: "client idle timeout node recoding", recordingMode: types.RecordAtNode, options: types.RoleOptions{ ClientIdleTimeout: types.NewDuration(500 * time.Millisecond), @@ -1699,6 +1829,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { disconnectTimeout: time.Second, }, { + name: "client idle timeout proxy recording", recordingMode: types.RecordAtProxy, options: types.RoleOptions{ ForwardAgent: types.NewBool(true), @@ -1707,6 +1838,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { disconnectTimeout: time.Second, }, { + name: "expired cert node recording", recordingMode: types.RecordAtNode, options: types.RoleOptions{ DisconnectExpiredCert: types.NewBool(true), @@ -1715,6 +1847,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { disconnectTimeout: 4 * time.Second, }, { + name: "expired cert proxy recording", recordingMode: types.RecordAtProxy, options: types.RoleOptions{ ForwardAgent: types.NewBool(true), @@ -1724,7 +1857,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { disconnectTimeout: 4 * time.Second, }, { - // "verify that concurrent connection limits are applied when recording at node", + name: "concurrent connection limits exceeded node recording", recordingMode: types.RecordAtNode, options: types.RoleOptions{ MaxConnections: 1, @@ -1734,7 +1867,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { verifyError: errorContains("administratively prohibited"), }, { - // "verify that concurrent connection limits are applied when recording at proxy", + name: "concurrent connection limits exceeded proxy recording", recordingMode: types.RecordAtProxy, options: types.RoleOptions{ ForwardAgent: types.NewBool(true), @@ -1745,7 +1878,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { verifyError: errorContains("administratively prohibited"), }, { - // "verify that lost connections to auth server terminate controlled conns", + name: "verify that lost connections to auth server terminate controlled connections", recordingMode: types.RecordAtNode, options: types.RoleOptions{ MaxConnections: 1, @@ -1774,7 +1907,7 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { require.NoError(t, err) require.Len(t, sems, 1) - timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Second) + timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() ss, err := waitForSessionToBeEstablished(timeoutCtx, defaults.Namespace, site) @@ -1785,8 +1918,8 @@ func testDisconnectScenarios(t *testing.T, suite *integrationTestSuite) { }, } - for i, tc := range testCases { - t.Run(fmt.Sprintf("Test %d", i), func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { runDisconnectTest(t, suite, tc) }) } @@ -2005,8 +2138,8 @@ func testInvalidLogins(t *testing.T, suite *integrationTestSuite) { require.NoError(t, err) err = tc.SSH(context.Background(), cmd, false) - require.True(t, trace.IsConnectionProblem(err)) - require.Contains(t, err.Error(), `unknown cluster "wrong-site"`) + require.True(t, trace.IsNotFound(err)) + require.Contains(t, err.Error(), `cluster "wrong-site" is not found`) } // TestTwoClustersTunnel creates two teleport clusters: "a" and "b" and creates a @@ -7240,13 +7373,6 @@ func testAgentlessConnection(t *testing.T, suite *integrationTestSuite) { require.NoError(t, teleInst.StopAll()) }) - tc, err := teleInst.NewClient(helpers.ClientConfig{ - Login: suite.Me.Username, - Cluster: helpers.Site, - Host: Host, - }) - require.NoError(t, err) - // get OpenSSH CA public key and create host certs ctx := context.Background() authClient := teleInst.Process.GetAuthServer() @@ -7325,18 +7451,32 @@ func testAgentlessConnection(t *testing.T, suite *integrationTestSuite) { } require.NoError(t, w.Close()) - // connect to node - proxyClient, err := tc.ConnectToProxy(ctx) + // create client + tc, err := teleInst.NewClient(helpers.ClientConfig{ + Login: suite.Me.Username, + Cluster: helpers.Site, + Host: Host, + }) + require.NoError(t, err) + + // connect to cluster + clt, err := tc.ConnectToCluster(ctx) require.NoError(t, err) t.Cleanup(func() { - require.NoError(t, proxyClient.Close()) + require.NoError(t, clt.Close()) }) - nodeClient, err := tc.ConnectToNode(ctx, proxyClient, client.NodeDetails{ - Addr: sshAddr, - Namespace: tc.Namespace, - Cluster: helpers.Site, - }, tc.Username) + // connect to node + nodeClient, err := tc.ConnectToNode( + ctx, + clt, + client.NodeDetails{ + Addr: sshAddr, + Namespace: tc.Namespace, + Cluster: helpers.Site, + }, + tc.Username, + ) require.NoError(t, err) // forward SSH agent diff --git a/integration/kube_integration_test.go b/integration/kube_integration_test.go index d36326729cdd6..442db7f33e89c 100644 --- a/integration/kube_integration_test.go +++ b/integration/kube_integration_test.go @@ -1053,12 +1053,14 @@ loop: func testKubeDisconnect(t *testing.T, suite *KubeSuite) { testCases := []disconnectTestCase{ { + name: "idle timeout", options: types.RoleOptions{ ClientIdleTimeout: types.NewDuration(500 * time.Millisecond), }, disconnectTimeout: 2 * time.Second, }, { + name: "expired cert", options: types.RoleOptions{ DisconnectExpiredCert: types.NewBool(true), MaxSessionTTL: types.NewDuration(3 * time.Second), @@ -1066,12 +1068,15 @@ func testKubeDisconnect(t *testing.T, suite *KubeSuite) { disconnectTimeout: 6 * time.Second, }, } + for i := 0; i < utils.GetIterations(); i++ { - for j, tc := range testCases { - t.Run(fmt.Sprintf("#%02d_iter_%d", j, i), func(t *testing.T) { - runKubeDisconnectTest(t, suite, tc) - }) - } + t.Run(fmt.Sprintf("Iteration=%d", i), func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + runKubeDisconnectTest(t, suite, tc) + }) + } + }) } } diff --git a/lib/agentless/agentless.go b/lib/agentless/agentless.go index baf099121caf8..cb2037027823c 100644 --- a/lib/agentless/agentless.go +++ b/lib/agentless/agentless.go @@ -27,18 +27,48 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/utils/sshutils" - "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/utils" ) -// SiteClientGetter returns an auth client to a given cluster. -type SiteClientGetter interface { - GetSiteClient(ctx context.Context, clusterName string) (auth.ClientI, error) +// CertGenerator generates certificates from a certificate request. +type CertGenerator interface { + GenerateOpenSSHCert(ctx context.Context, req *proto.OpenSSHCertRequest) (*proto.OpenSSHCert, error) } -// CreateAuthSigner attempts to create a [ssh.Signer] that is signed with +// SignerFromSSHCertificate returns a function that attempts to +// create a [ssh.Signer] for the Identity in the provided [ssh.Certificate] +// that is signed with the OpenSSH CA and can be used to authenticate to agentless nodes. +func SignerFromSSHCertificate(certificate *ssh.Certificate, generator CertGenerator) func(context.Context) (ssh.Signer, error) { + return func(ctx context.Context) (ssh.Signer, error) { + validBefore := time.Unix(int64(certificate.ValidBefore), 0) + ttl := time.Until(validBefore) + + clusterName := certificate.Permissions.Extensions[utils.CertExtensionAuthority] + user := certificate.Permissions.Extensions[utils.CertTeleportUser] + + signer, err := createAuthSigner(ctx, user, clusterName, ttl, generator) + return signer, trace.Wrap(err) + } +} + +// SignerFromAuthzContext returns a function that attempts to +// create a [ssh.Signer] for the [tlsca.Identity] in the provided [authz.Context] +// that is signed with the OpenSSH CA and can be used to authenticate to agentless nodes. +func SignerFromAuthzContext(authzCtx *authz.Context, generator CertGenerator) func(context.Context) (ssh.Signer, error) { + return func(ctx context.Context) (ssh.Signer, error) { + identity := authzCtx.Identity.GetIdentity() + ttl := time.Until(identity.Expires) + + signer, err := createAuthSigner(ctx, authzCtx.User.GetName(), identity.TeleportCluster, ttl, generator) + return signer, trace.Wrap(err) + } +} + +// createAuthSigner creates a [ssh.Signer] that is signed with // OpenSSH CA and can be used to authenticate to agentless nodes. -func CreateAuthSigner(ctx context.Context, username, clusterName string, ttl time.Duration, clientGetter SiteClientGetter) (ssh.Signer, error) { +func createAuthSigner(ctx context.Context, username, clusterName string, ttl time.Duration, generator CertGenerator) (ssh.Signer, error) { // generate a new key pair priv, err := native.GeneratePrivateKey() if err != nil { @@ -46,11 +76,7 @@ func CreateAuthSigner(ctx context.Context, username, clusterName string, ttl tim } // sign new public key with OpenSSH CA - client, err := clientGetter.GetSiteClient(ctx, clusterName) - if err != nil { - return nil, trace.Wrap(err) - } - reply, err := client.GenerateOpenSSHCert(ctx, &proto.OpenSSHCertRequest{ + reply, err := generator.GenerateOpenSSHCert(ctx, &proto.OpenSSHCertRequest{ Username: username, PublicKey: priv.MarshalSSHPublicKey(), TTL: proto.Duration(ttl), diff --git a/lib/benchmark/benchmark.go b/lib/benchmark/benchmark.go index 565d9dc86606f..c52c52a52c526 100644 --- a/lib/benchmark/benchmark.go +++ b/lib/benchmark/benchmark.go @@ -24,7 +24,6 @@ import ( "os" "os/signal" "path/filepath" - "strings" "syscall" "time" @@ -34,7 +33,6 @@ import ( "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/observability/tracing" - "github.com/gravitational/teleport/lib/utils" ) const ( @@ -48,14 +46,20 @@ const ( pauseTimeBetweenBenchmarks = time.Second * 5 ) +// Service is a the Teleport service to benchmark. +type Service string + +const ( + // SSHService is the SSH service + SSHService Service = "ssh" + // KubernetesService is the Kubernetes service + KubernetesService Service = "kube" +) + // Config specifies benchmark requests to run type Config struct { // Rate is requests per second origination rate Rate int - // Command is a command to run - Command []string - // Interactive turns on interactive sessions - Interactive bool // MinimumWindow is the min duration MinimumWindow time.Duration // MinimumMeasurments is the min amount of requests @@ -79,9 +83,8 @@ type Result struct { // Run is used to run the benchmarks, it is given a generator, command to run, // a host, host login, and proxy. If host login or proxy is an empty string, it will // use the default login -func Run(ctx context.Context, lg *Linear, cmd, host, login, proxy string) ([]Result, error) { - c := strings.Split(cmd, " ") - lg.config = &Config{Command: c} +func Run(ctx context.Context, lg *Linear, host, login, proxy string, suite BenchmarkSuite) ([]Result, error) { + lg.config = &Config{} if err := validateConfig(lg); err != nil { return nil, trace.Wrap(err) } @@ -112,7 +115,7 @@ func Run(ctx context.Context, lg *Linear, cmd, host, login, proxy string) ([]Res if benchmarkC == nil { break } - result, err := benchmarkC.Benchmark(ctx, tc) + result, err := benchmarkC.Benchmark(ctx, tc, suite) if err != nil { return results, trace.Wrap(err) } @@ -128,7 +131,7 @@ func ExportLatencyProfile(path string, h *hdrhistogram.Histogram, ticks int32, v timeStamp := time.Now().Format("2006-01-02_15:04:05") suffix := fmt.Sprintf("latency_profile_%s.txt", timeStamp) if path != "." { - if err := os.MkdirAll(path, 0700); err != nil { + if err := os.MkdirAll(path, 0o700); err != nil { return "", trace.Wrap(err) } } @@ -140,7 +143,6 @@ func ExportLatencyProfile(path string, h *hdrhistogram.Histogram, ticks int32, v if _, err := h.PercentilesPrint(fo, ticks, valueScale); err != nil { if err := fo.Close(); err != nil { - logrus.WithError(err).Warningf("failed to close file") } return "", trace.Wrap(err) @@ -152,13 +154,33 @@ func ExportLatencyProfile(path string, h *hdrhistogram.Histogram, ticks int32, v return fo.Name(), nil } +// WorkloadFunc is a function that executes a single benchmark call. +type WorkloadFunc func(context.Context) error + +// BenchmarkSuite is an interface that defines a benchmark suite. +type BenchmarkSuite interface { + // BenchBuilder returns a function that executes a single benchmark call. + // The returned function is called in a loop until the context is canceled. + BenchBuilder(context.Context, *client.TeleportClient) (WorkloadFunc, error) +} + // Benchmark connects to remote server and executes requests in parallel according // to benchmark spec. It returns benchmark result when completed. // This is a blocking function that can be canceled via context argument. -func (c *Config) Benchmark(ctx context.Context, tc *client.TeleportClient) (Result, error) { +func (c *Config) Benchmark(ctx context.Context, tc *client.TeleportClient, suite BenchmarkSuite) (Result, error) { + if suite == nil { + return Result{}, trace.BadParameter("missing benchmark suite") + } + + workload, err := suite.BenchBuilder(ctx, tc) + if err != nil { + return Result{}, trace.Wrap(err) + } + tc.Stdout = io.Discard tc.Stderr = io.Discard tc.Stdin = &bytes.Buffer{} + var delay time.Duration ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -180,11 +202,8 @@ func (c *Config) Benchmark(ctx context.Context, tc *client.TeleportClient) (Resu t := start.Add(delay) measure := benchMeasure{ ResponseStart: t, - command: c.Command, - client: tc, - interactive: c.Interactive, } - go work(ctx, measure, resultC) + go work(ctx, measure, resultC, workload) case <-ctx.Done(): close(requestsC) return @@ -226,13 +245,10 @@ type benchMeasure struct { ResponseStart time.Time End time.Time Error error - client *client.TeleportClient - command []string - interactive bool } -func work(ctx context.Context, m benchMeasure, send chan<- benchMeasure) { - m.Error = execute(m) +func work(ctx context.Context, m benchMeasure, send chan<- benchMeasure, workload WorkloadFunc) { + m.Error = workload(ctx) m.End = time.Now() select { case send <- m: @@ -241,33 +257,6 @@ func work(ctx context.Context, m benchMeasure, send chan<- benchMeasure) { } } -func execute(m benchMeasure) error { - if !m.interactive { - // do not use parent context that will cancel in flight requests - // because we give test some time to gracefully wrap up - // the in-flight connections to avoid extra errors - return m.client.SSH(context.TODO(), m.command, false) - } - config := m.client.Config - client, err := client.NewClient(&config) - if err != nil { - return err - } - reader, writer := io.Pipe() - defer reader.Close() - defer writer.Close() - client.Stdin = reader - out := &utils.SyncBuffer{} - client.Stdout = out - client.Stderr = out - err = m.client.SSH(context.TODO(), nil, false) - if err != nil { - return err - } - writer.Write([]byte(strings.Join(m.command, " ") + "\r\nexit\r\n")) - return nil -} - // makeTeleportClient creates an instance of a teleport client func makeTeleportClient(host, login, proxy string) (*client.TeleportClient, error) { c := client.Config{ diff --git a/lib/benchmark/benchmark_suites.go b/lib/benchmark/benchmark_suites.go new file mode 100644 index 0000000000000..79c1443dd2dc7 --- /dev/null +++ b/lib/benchmark/benchmark_suites.go @@ -0,0 +1,250 @@ +/* +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 benchmark package provides tools to run progressive or independent benchmarks against teleport services. +package benchmark + +import ( + "bytes" + "context" + "io" + "net/http" + "strings" + + "github.com/gravitational/trace" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/remotecommand" + "k8s.io/kubectl/pkg/scheme" + + "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/utils" +) + +// SSHBenchmark is a benchmark suite that runs a single SSH command +// against a Teleport node for a given duration and rate. +type SSHBenchmark struct { + // Command is a command to run + Command []string + // Interactive turns on interactive sessions + Interactive bool +} + +// BenchBuilder returns a WorkloadFunc for the given benchmark suite. +func (s SSHBenchmark) BenchBuilder(ctx context.Context, tc *client.TeleportClient) (WorkloadFunc, error) { + return func(ctx context.Context) error { + if !s.Interactive { + // do not use parent context that will cancel in flight requests + // because we give test some time to gracefully wrap up + // the in-flight connections to avoid extra errors + return tc.SSH(ctx, s.Command, false) + } + config := tc.Config + client, err := client.NewClient(&config) + if err != nil { + return err + } + reader, writer := io.Pipe() + defer reader.Close() + defer writer.Close() + client.Stdin = reader + out := &utils.SyncBuffer{} + client.Stdout = out + client.Stderr = out + err = tc.SSH(ctx, nil, false) + if err != nil { + return err + } + writer.Write([]byte(strings.Join(s.Command, " ") + "\r\nexit\r\n")) + return nil + }, nil +} + +// KubeListBenchmark is a benchmark suite that runs successive kubectl get pods +// against a Teleport Kubernetes proxy for a given duration and rate. +type KubeListBenchmark struct { + // Namespace is the Kubernetes namespace to run the command against. + // If empty, it will include pods from all namespaces. + Namespace string +} + +// BenchBuilder returns a WorkloadFunc for the given benchmark suite. +func (k KubeListBenchmark) BenchBuilder(ctx context.Context, tc *client.TeleportClient) (WorkloadFunc, error) { + restCfg, err := newKubernetesRestConfig(ctx, tc) + if err != nil { + return nil, trace.Wrap(err) + } + clientset, err := kubernetes.NewForConfig(restCfg) + if err != nil { + return nil, trace.Wrap(err) + } + + return func(ctx context.Context) error { + // List all pods in all namespaces. + _, err := clientset.CoreV1().Pods(k.Namespace).List(ctx, metav1.ListOptions{}) + return trace.Wrap(err) + }, nil +} + +// newKubernetesRestConfig returns a new rest.Config for the kubernetes cluster +// that the client wants to connected to. +func newKubernetesRestConfig(ctx context.Context, tc *client.TeleportClient) (*rest.Config, error) { + tlsClientConfig, err := getKubeTLSClientConfig(ctx, tc) + if err != nil { + return nil, trace.Wrap(err) + } + restConfig := &rest.Config{ + Host: tc.KubeClusterAddr(), + TLSClientConfig: tlsClientConfig, + APIPath: "/api", + ContentConfig: rest.ContentConfig{ + GroupVersion: &schema.GroupVersion{Version: "v1"}, + NegotiatedSerializer: scheme.Codecs, + }, + } + return restConfig, nil +} + +// getKubeTLSClientConfig returns a TLS client config for the kubernetes cluster +// that the client wants to connected to. +func getKubeTLSClientConfig(ctx context.Context, tc *client.TeleportClient) (rest.TLSClientConfig, error) { + var k *client.Key + err := client.RetryWithRelogin(ctx, tc, func() error { + var err error + k, err = tc.IssueUserCertsWithMFA(ctx, client.ReissueParams{ + RouteToCluster: tc.SiteName, + KubernetesCluster: tc.KubernetesCluster, + }, nil /*applyOpts*/) + return err + }) + if err != nil { + return rest.TLSClientConfig{}, trace.Wrap(err) + } + + certPem := k.KubeTLSCerts[tc.KubernetesCluster] + + rsaKeyPEM, err := k.PrivateKey.RSAPrivateKeyPEM() + if err != nil { + return rest.TLSClientConfig{}, trace.Wrap(err) + } + + credentials, err := tc.LocalAgent().GetCoreKey() + if err != nil { + return rest.TLSClientConfig{}, trace.Wrap(err) + } + + var clusterCAs [][]byte + if tc.LoadAllCAs { + clusterCAs = credentials.TLSCAs() + } else { + clusterCAs, err = credentials.RootClusterCAs() + if err != nil { + return rest.TLSClientConfig{}, trace.Wrap(err) + } + } + if len(clusterCAs) == 0 { + return rest.TLSClientConfig{}, trace.BadParameter("no trusted CAs found") + } + + tlsServerName := "" + if tc.TLSRoutingEnabled { + k8host, _ := tc.KubeProxyHostPort() + tlsServerName = client.GetKubeTLSServerName(k8host) + } + + return rest.TLSClientConfig{ + CAData: bytes.Join(clusterCAs, []byte("\n")), + CertData: certPem, + KeyData: rsaKeyPEM, + ServerName: tlsServerName, + }, nil +} + +// KubeListBenchmark is a benchmark suite that runs successive kubectl exec +// against a Teleport Kubernetes proxy for a given duration and rate. +type KubeExecBenchmark struct { + // Namespace is the Kubernetes namespace to run the command against. + Namespace string + // PodName is the name of the pod to run the command against. + PodName string + // ContainerName is the name of the container to run the command against. + ContainerName string + // Command is the command to run. + Command []string + // Interactive turns on interactive sessions + Interactive bool +} + +// BenchBuilder returns a WorkloadFunc for the given benchmark suite. +func (k KubeExecBenchmark) BenchBuilder(ctx context.Context, tc *client.TeleportClient) (WorkloadFunc, error) { + restCfg, err := newKubernetesRestConfig(ctx, tc) + if err != nil { + return nil, trace.Wrap(err) + } + if k.Interactive { + // If interactive, we need to set up a pty and we cannot use the + // stderr stream because the server will hang. + tc.Stderr = nil + } else { + // If not interactive, we need to set up stdin to be nil so that + // the server wont wait for input. + tc.Stdin = nil + } + exec, err := k.kubeExecOnPod(ctx, tc, restCfg) + if err != nil { + return nil, trace.Wrap(err) + } + return func(ctx context.Context) error { + stdin := tc.Stdin + if k.Interactive { + stdin = bytes.NewBuffer([]byte(strings.Join(k.Command, " ") + "\r\nexit\r\n")) + } + err := exec.StreamWithContext(ctx, remotecommand.StreamOptions{ + Stdin: stdin, + Stdout: tc.Stdout, + Stderr: tc.Stderr, + Tty: k.Interactive, + }) + return trace.Wrap(err) + }, nil +} + +func (k KubeExecBenchmark) kubeExecOnPod(ctx context.Context, tc *client.TeleportClient, restConfig *rest.Config) (remotecommand.Executor, error) { + restClient, err := rest.RESTClientFor(restConfig) + if err != nil { + return nil, trace.Wrap(err) + } + + req := restClient.Post(). + Resource("pods"). + Name(k.PodName). + Namespace(k.Namespace). + SubResource("exec") + + req.VersionedParams(&corev1.PodExecOptions{ + Container: k.ContainerName, + Command: k.Command, + Stdin: tc.Stdin != nil, + Stdout: tc.Stdout != nil, + Stderr: tc.Stderr != nil, + TTY: k.Interactive, + }, scheme.ParameterCodec) + + exec, err := remotecommand.NewSPDYExecutor(restConfig, http.MethodPost, req.URL()) + return exec, trace.Wrap(err) +} diff --git a/lib/benchmark/linear.go b/lib/benchmark/linear.go index 396ad090d7e11..38d3809c4eb13 100644 --- a/lib/benchmark/linear.go +++ b/lib/benchmark/linear.go @@ -42,7 +42,6 @@ func (lg *Linear) GetBenchmark() *Config { MinimumWindow: lg.MinimumWindow, MinimumMeasurements: lg.MinimumMeasurements, Rate: lg.currentRPS, - Command: lg.config.Command, } if lg.currentRPS < lg.LowerBound { diff --git a/lib/benchmark/linear_test.go b/lib/benchmark/linear_test.go index 60055d2fc4a2c..6e38864829340 100644 --- a/lib/benchmark/linear_test.go +++ b/lib/benchmark/linear_test.go @@ -26,8 +26,6 @@ import ( func TestGetBenchmark(t *testing.T) { initial := &Config{ Rate: 0, - Command: []string{"ls"}, - Interactive: false, MinimumWindow: time.Second * 30, MinimumMeasurements: 1000, } @@ -53,8 +51,6 @@ func TestGetBenchmark(t *testing.T) { func TestGetBenchmarkNotEvenMultiple(t *testing.T) { initial := &Config{ Rate: 0, - Command: []string{"ls"}, - Interactive: false, MinimumWindow: time.Second * 30, MinimumMeasurements: 1000, } diff --git a/lib/client/api.go b/lib/client/api.go index 8ec87bfcab4b5..999b98b467fb5 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -45,10 +45,12 @@ import ( "golang.org/x/crypto/ssh/agent" "golang.org/x/net/http2" "golang.org/x/sync/errgroup" + "google.golang.org/grpc" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/proto" + proxyclient "github.com/gravitational/teleport/api/client/proxy" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" @@ -365,9 +367,6 @@ type Config struct { // MockSSOLogin is used in tests for mocking the SSO login response. MockSSOLogin SSOLoginFunc - // MockConnectToProxy is used in tests to override connection to proxy - MockConnectToProxy ConnectToProxyFunc - // HomePath is where tsh stores profiles HomePath string @@ -415,6 +414,10 @@ type Config struct { // the ssh, scp, and ls commands can use headless login. Other commands will ignore // headless auth connector and default to local instead. AllowHeadless bool + + // DialOpts used by the api.client.proxy.Client when establishing a connection to + // the proxy server. Used by tests. + DialOpts []grpc.DialOption } // CachePolicy defines cache policy for local clients @@ -1181,7 +1184,7 @@ func (tc *TeleportClient) RootClusterName(ctx context.Context) (string, error) { // getTargetNodes returns a list of node addresses this SSH command needs to // operate on. -func (tc *TeleportClient) getTargetNodes(ctx context.Context, proxy *ProxyClient) ([]string, error) { +func (tc *TeleportClient) getTargetNodes(ctx context.Context, clt client.ListResourcesClient) ([]string, error) { ctx, span := tc.Tracer.Start( ctx, "teleportClient/getTargetNodes", @@ -1203,15 +1206,17 @@ func (tc *TeleportClient) getTargetNodes(ctx context.Context, proxy *ProxyClient } // find the nodes matching the labels that were provided - nodes, err := proxy.FindNodesByFilters(ctx, *tc.DefaultResourceFilter()) + filter := tc.DefaultResourceFilter() + filter.ResourceType = types.KindNode + resources, err := client.GetResourcesWithFilters(ctx, clt, *filter) if err != nil { return nil, trace.Wrap(err) } - retval := make([]string, 0, len(nodes)) - for i := 0; i < len(nodes); i++ { + retval := make([]string, 0, len(resources)) + for _, resource := range resources { // always dial nodes by UUID - retval = append(retval, fmt.Sprintf("%s:0", nodes[i].GetName())) + retval = append(retval, fmt.Sprintf("%s:0", resource.GetName())) } return retval, nil @@ -1427,7 +1432,8 @@ func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally "teleportClient/SSH", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("proxy", tc.Config.WebProxyAddr), + attribute.String("proxy_web", tc.Config.WebProxyAddr), + attribute.String("proxy_ssh", tc.Config.SSHProxyAddr), ), ) defer span.End() @@ -1436,14 +1442,15 @@ func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally if !tc.Config.ProxySpecified() { return trace.BadParameter("proxy server is not specified") } - proxyClient, err := tc.ConnectToProxy(ctx) + + clt, err := tc.ConnectToCluster(ctx) if err != nil { return trace.Wrap(err) } - defer proxyClient.Close() + defer clt.Close() // which nodes are we executing this commands on? - nodeAddrs, err := tc.getTargetNodes(ctx, proxyClient) + nodeAddrs, err := tc.getTargetNodes(ctx, clt.AuthClient) if err != nil { return trace.Wrap(err) } @@ -1452,9 +1459,9 @@ func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally } if len(nodeAddrs) > 1 { - return tc.runShellOrCommandOnMultipleNodes(ctx, nodeAddrs, proxyClient, command) + return tc.runShellOrCommandOnMultipleNodes(ctx, clt, nodeAddrs, command) } - return tc.runShellOrCommandOnSingleNode(ctx, nodeAddrs[0], proxyClient, command, runLocally) + return tc.runShellOrCommandOnSingleNode(ctx, clt, nodeAddrs[0], command, runLocally) } // ConnectToNode attempts to establish a connection to the node resolved to by the provided @@ -1463,41 +1470,39 @@ func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally // ceremony is performed and another connection is attempted with the freshly minted // certificates. If it is not required, then the original Access Denied error from the node // is returned. -func (tc *TeleportClient) ConnectToNode(ctx context.Context, proxyClient *ProxyClient, nodeDetails NodeDetails, user string) (*NodeClient, error) { +func (tc *TeleportClient) ConnectToNode(ctx context.Context, clt *ClusterClient, nodeDetails NodeDetails, user string) (*NodeClient, error) { node := nodeName(nodeDetails.Addr) ctx, span := tc.Tracer.Start( ctx, "teleportClient/ConnectToNode", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("site", nodeDetails.Cluster), + attribute.String("cluster", nodeDetails.Cluster), attribute.String("node", node), ), ) defer span.End() - // attempt to use the existing credentials first - authMethods := proxyClient.authMethods + sshConfig := clt.ProxyClient.SSHConfig(user) - // if per-session mfa is required, perform the mfa ceremony to get - // new certificates and use them instead + // if mfa is required generate new config after + // performing the mfa ceremony if nodeDetails.MFACheck != nil && nodeDetails.MFACheck.Required { - am, err := proxyClient.sessionSSHCertificate(ctx, nodeDetails) + cfg, err := clt.SessionSSHConfig(ctx, user, nodeDetails) if err != nil { return nil, trace.Wrap(err) } - authMethods = am + sshConfig = cfg } - // grab the cluster details - details, err := proxyClient.clusterDetails(ctx) + // try connecting to the node + conn, details, err := clt.ProxyClient.DialHost(ctx, nodeDetails.Addr, nodeDetails.Cluster, tc.localAgent.ExtendedAgent) if err != nil { return nil, trace.Wrap(err) } - // try connecting to the node - nodeClient, connectErr := proxyClient.ConnectToNode(ctx, nodeDetails, user, details, authMethods) + nodeClient, connectErr := NewNodeClient(ctx, sshConfig, conn, nodeDetails.ProxyFormat(), nodeDetails.Addr, tc, details.FIPS) switch { case connectErr == nil: // no error return client return nodeClient, nil @@ -1508,73 +1513,57 @@ func (tc *TeleportClient) ConnectToNode(ctx context.Context, proxyClient *ProxyC } // access was denied, determine if it was because per-session mfa is required - clt, err := proxyClient.ConnectToCluster(ctx, nodeDetails.Cluster) - if err != nil { - // return the connection error instead of any errors from connecting to auth - return nil, trace.Wrap(connectErr) - } - - check, err := clt.IsMFARequired(ctx, &proto.IsMFARequiredRequest{ + nodeDetails.MFACheck, err = clt.AuthClient.IsMFARequired(ctx, &proto.IsMFARequiredRequest{ Target: &proto.IsMFARequiredRequest_Node{ Node: &proto.NodeLogin{ Node: node, - Login: proxyClient.hostLogin, + Login: tc.HostLogin, }, }, }) if err != nil { + log.Warnf("Unable to determine if session mfa is required: %v", err) return nil, trace.Wrap(connectErr) } // per-session mfa isn't required, the user simply does not // have access to the provided node - if !check.Required { + if !nodeDetails.MFACheck.Required { return nil, trace.Wrap(connectErr) } - // per-session mfa is required, perform the mfa ceremony - key, err := proxyClient.IssueUserCertsWithMFA( - ctx, - ReissueParams{ - NodeName: node, - RouteToCluster: nodeDetails.Cluster, - MFACheck: check, - AuthClient: clt, - }, - func(ctx context.Context, proxyAddr string, c *proto.MFAAuthenticateChallenge) (*proto.MFAAuthenticateResponse, error) { - return tc.PromptMFAChallenge(ctx, proxyAddr, c, nil /* applyOpts */) - }, - ) + // generate new config after performing the mfa ceremony + cfg, err := clt.SessionSSHConfig(ctx, user, nodeDetails) if err != nil { return nil, trace.Wrap(err) } - // try connecting to the node again with the newly acquired certificates - newAuthMethods, err := key.AsAuthMethod() + conn, details, err = clt.ProxyClient.DialHost(ctx, nodeDetails.Addr, nodeDetails.Cluster, tc.localAgent.ExtendedAgent) if err != nil { return nil, trace.Wrap(err) } - nodeClient, err = proxyClient.ConnectToNode(ctx, nodeDetails, user, details, []ssh.AuthMethod{newAuthMethods}) + nodeClient, err = NewNodeClient(ctx, cfg, conn, nodeDetails.ProxyFormat(), nodeDetails.Addr, tc, details.FIPS) return nodeClient, trace.Wrap(err) } -func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, nodeAddr string, proxyClient *ProxyClient, command []string, runLocally bool) error { +func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, clt *ClusterClient, nodeAddr string, command []string, runLocally bool) error { + cluster := clt.ClusterName() ctx, span := tc.Tracer.Start( ctx, "teleportClient/runShellOrCommandOnSingleNode", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("site", tc.SiteName), attribute.String("node", nodeAddr), + attribute.String("cluster", cluster), ), ) defer span.End() nodeClient, err := tc.ConnectToNode( ctx, - proxyClient, - NodeDetails{Addr: nodeAddr, Namespace: tc.Namespace, Cluster: tc.SiteName}, + clt, + NodeDetails{Addr: nodeAddr, Namespace: tc.Namespace, Cluster: cluster}, tc.Config.HostLogin, ) if err != nil { @@ -1622,18 +1611,19 @@ func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, nod if len(command) > 0 { // Reuse the existing nodeClient we connected above. - return tc.runCommand(ctx, nodeClient, command) + return nodeClient.RunCommand(ctx, command) } return tc.runShell(ctx, nodeClient, types.SessionPeerMode, nil, nil) } -func (tc *TeleportClient) runShellOrCommandOnMultipleNodes(ctx context.Context, nodeAddrs []string, proxyClient *ProxyClient, command []string) error { +func (tc *TeleportClient) runShellOrCommandOnMultipleNodes(ctx context.Context, clt *ClusterClient, nodeAddrs []string, command []string) error { + cluster := clt.ClusterName() ctx, span := tc.Tracer.Start( ctx, "teleportClient/runShellOrCommandOnMultipleNodes", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("site", tc.SiteName), + attribute.String("cluster", cluster), attribute.StringSlice("node", nodeAddrs), ), ) @@ -1642,15 +1632,15 @@ func (tc *TeleportClient) runShellOrCommandOnMultipleNodes(ctx context.Context, // There was a command provided, run a non-interactive session against each match if len(command) > 0 { fmt.Printf("\x1b[1mWARNING\x1b[0m: Multiple nodes matched label selector, running command on all.\n") - return tc.runCommandOnNodes(ctx, tc.SiteName, nodeAddrs, proxyClient, command) + return tc.runCommandOnNodes(ctx, clt, nodeAddrs, command) } // Issue "shell" request to the first matching node. fmt.Printf("\x1b[1mWARNING\x1b[0m: Multiple nodes match the label selector, picking first: %q\n", nodeAddrs[0]) nodeClient, err := tc.ConnectToNode( ctx, - proxyClient, - NodeDetails{Addr: nodeAddrs[0], Namespace: tc.Namespace, Cluster: tc.SiteName}, + clt, + NodeDetails{Addr: nodeAddrs[0], Namespace: tc.Namespace, Cluster: cluster}, tc.Config.HostLogin, ) if err != nil { @@ -1706,15 +1696,14 @@ func (tc *TeleportClient) Join(ctx context.Context, mode types.SessionParticipan if !tc.Config.ProxySpecified() { return trace.BadParameter("proxy server is not specified") } - proxyClient, err := tc.ConnectToProxy(ctx) + clt, err := tc.ConnectToCluster(ctx) if err != nil { return trace.Wrap(err) } - defer proxyClient.Close() - site := proxyClient.CurrentCluster() + defer clt.Close() // Session joining is not supported in proxy recording mode - if recConfig, err := site.GetSessionRecordingConfig(ctx); err != nil { + if recConfig, err := clt.AuthClient.GetSessionRecordingConfig(ctx); err != nil { // If the user can't see the recording mode, just let them try joining below if !trace.IsAccessDenied(err) { return trace.Wrap(err) @@ -1723,7 +1712,7 @@ func (tc *TeleportClient) Join(ctx context.Context, mode types.SessionParticipan return trace.BadParameter("session joining is not supported in proxy recording mode") } - session, err := site.GetSessionTracker(ctx, string(sessionID)) + session, err := clt.AuthClient.GetSessionTracker(ctx, string(sessionID)) if err != nil { if trace.IsNotFound(err) { return trace.NotFound("session %q not found or it has ended", sessionID) @@ -1737,8 +1726,8 @@ func (tc *TeleportClient) Join(ctx context.Context, mode types.SessionParticipan // connect to server: nc, err := tc.ConnectToNode(ctx, - proxyClient, - NodeDetails{Addr: session.GetAddress() + ":0", Namespace: tc.Namespace, Cluster: tc.SiteName}, + clt, + NodeDetails{Addr: session.GetAddress() + ":0", Namespace: tc.Namespace, Cluster: clt.ClusterName()}, tc.Config.HostLogin, ) if err != nil { @@ -1758,7 +1747,7 @@ func (tc *TeleportClient) Join(ctx context.Context, mode types.SessionParticipan if mode == types.SessionModeratorMode { beforeStart = func(out io.Writer) { nc.OnMFA = func() { - runPresenceTask(presenceCtx, out, site, tc, session.GetSessionID()) + runPresenceTask(presenceCtx, out, clt.AuthClient, tc, session.GetSessionID()) } } } @@ -1907,17 +1896,17 @@ func (tc *TeleportClient) ExecuteSCP(ctx context.Context, serverAddr string, cmd return trace.BadParameter("proxy server is not specified") } - proxyClient, err := tc.ConnectToProxy(ctx) + clt, err := tc.ConnectToCluster(ctx) if err != nil { return trace.Wrap(err) } - defer proxyClient.Close() + defer clt.Close() nodeClient, err := tc.ConnectToNode( ctx, - proxyClient, + clt, // We append the ":0" to tell the server to figure out the port for us. - NodeDetails{Addr: serverAddr + ":0", Namespace: tc.Namespace, Cluster: tc.SiteName}, + NodeDetails{Addr: serverAddr + ":0", Namespace: tc.Namespace, Cluster: clt.ClusterName()}, tc.Config.HostLogin, ) if err != nil { @@ -2063,16 +2052,16 @@ func (tc *TeleportClient) TransferFiles(ctx context.Context, hostLogin, nodeAddr if !tc.Config.ProxySpecified() { return trace.BadParameter("proxy server is not specified") } - proxyClient, err := tc.ConnectToProxy(ctx) + clt, err := tc.ConnectToCluster(ctx) if err != nil { return trace.Wrap(err) } - defer proxyClient.Close() + defer clt.Close() client, err := tc.ConnectToNode( ctx, - proxyClient, - NodeDetails{Addr: nodeAddr, Namespace: tc.Namespace, Cluster: tc.SiteName}, + clt, + NodeDetails{Addr: nodeAddr, Namespace: tc.Namespace, Cluster: clt.ClusterName()}, hostLogin, ) if err != nil { @@ -2102,7 +2091,9 @@ func (tc *TeleportClient) ListNodesWithFilters(ctx context.Context) ([]types.Ser } defer proxyClient.Close() - servers, err := proxyClient.FindNodesByFilters(ctx, *tc.DefaultResourceFilter()) + filter := tc.DefaultResourceFilter() + filter.ResourceType = types.KindNode + servers, err := proxyClient.FindNodesByFilters(ctx, *filter) if err != nil { return nil, trace.Wrap(err) } @@ -2491,28 +2482,26 @@ func commandLimit(ctx context.Context, getter roleGetter, mfaRequired bool) int } // runCommandOnNodes executes a given bash command on a bunch of remote nodes. -func (tc *TeleportClient) runCommandOnNodes(ctx context.Context, siteName string, nodeAddresses []string, proxyClient *ProxyClient, command []string) error { +func (tc *TeleportClient) runCommandOnNodes(ctx context.Context, clt *ClusterClient, nodeAddresses []string, command []string) error { + cluster := clt.ClusterName() ctx, span := tc.Tracer.Start( ctx, "teleportClient/runCommandOnNodes", oteltrace.WithSpanKind(oteltrace.SpanKindClient), + oteltrace.WithAttributes( + attribute.String("cluster", cluster), + ), ) defer span.End() - clt, err := proxyClient.ConnectToCluster(ctx, siteName) - if err != nil { - return trace.Wrap(err) - } - defer clt.Close() - // Let's check if the first node requires mfa. // If it's required, run commands sequentially to avoid // race conditions and weird ux during mfa. - mfaRequiredCheck, err := clt.IsMFARequired(ctx, &proto.IsMFARequiredRequest{ + mfaRequiredCheck, err := clt.AuthClient.IsMFARequired(ctx, &proto.IsMFARequiredRequest{ Target: &proto.IsMFARequiredRequest_Node{ Node: &proto.NodeLogin{ Node: nodeName(nodeAddresses[0]), - Login: proxyClient.hostLogin, + Login: tc.Config.HostLogin, }, }, }) @@ -2521,7 +2510,7 @@ func (tc *TeleportClient) runCommandOnNodes(ctx context.Context, siteName string } g, gctx := errgroup.WithContext(ctx) - g.SetLimit(commandLimit(ctx, clt, mfaRequiredCheck.Required)) + g.SetLimit(commandLimit(ctx, clt.AuthClient, mfaRequiredCheck.Required)) for _, address := range nodeAddresses { address := address g.Go(func() error { @@ -2535,11 +2524,11 @@ func (tc *TeleportClient) runCommandOnNodes(ctx context.Context, siteName string nodeClient, err := tc.ConnectToNode( ctx, - proxyClient, + clt, NodeDetails{ Addr: address, Namespace: tc.Namespace, - Cluster: siteName, + Cluster: cluster, MFACheck: mfaRequiredCheck, }, tc.Config.HostLogin, @@ -2552,47 +2541,13 @@ func (tc *TeleportClient) runCommandOnNodes(ctx context.Context, siteName string fmt.Printf("Running command on %v:\n", nodeName(address)) - return trace.Wrap(tc.runCommand(ctx, nodeClient, command)) + return trace.Wrap(nodeClient.RunCommand(ctx, command)) }) } return trace.Wrap(g.Wait()) } -// runCommand executes a given bash command on an established NodeClient. -func (tc *TeleportClient) runCommand(ctx context.Context, nodeClient *NodeClient, command []string) error { - ctx, span := tc.Tracer.Start( - ctx, - "teleportClient/runCommand", - oteltrace.WithSpanKind(oteltrace.SpanKindClient), - ) - defer span.End() - - nodeSession, err := newSession(ctx, nodeClient, nil, tc.newSessionEnv(), tc.Stdin, tc.Stdout, tc.Stderr, tc.EnableEscapeSequences) - if err != nil { - return trace.Wrap(err) - } - defer nodeSession.Close() - if err := nodeSession.runCommand(ctx, types.SessionPeerMode, command, tc.OnShellCreated, tc.Config.InteractiveCommand); err != nil { - originErr := trace.Unwrap(err) - exitErr, ok := originErr.(*ssh.ExitError) - if ok { - tc.ExitStatus = exitErr.ExitStatus() - } else { - // if an error occurs, but no exit status is passed back, GoSSH returns - // a generic error like this. in this case the error message is printed - // to stderr by the remote process so we have to quietly return 1: - if strings.Contains(originErr.Error(), "exited without exit status") { - tc.ExitStatus = 1 - } - } - - return trace.Wrap(err) - } - - return nil -} - func (tc *TeleportClient) newSessionEnv() map[string]string { env := map[string]string{ teleport.SSHSessionWebproxyAddr: tc.WebProxyAddr, @@ -2702,52 +2657,72 @@ func formatConnectToProxyErr(err error) error { return err } -// ConnectToProxyFunc is used in tests to override connection to proxy function. -type ConnectToProxyFunc func(ctx context.Context) (*ProxyClient, error) - -// ConnectToProxy will dial to the proxy server and return a ProxyClient when -// successful. If the passed in context is canceled, this function will return -// a trace.ConnectionProblem right away. -func (tc *TeleportClient) ConnectToProxy(ctx context.Context) (*ProxyClient, error) { - if tc.Config.MockConnectToProxy != nil { - return tc.Config.MockConnectToProxy(ctx) - } - +// ConnectToCluster will dial the auth and proxy server and return a ClusterClient when +// successful. +func (tc *TeleportClient) ConnectToCluster(ctx context.Context) (*ClusterClient, error) { ctx, span := tc.Tracer.Start( ctx, - "teleportClient/ConnectToProxy", + "teleportClient/ConnectToCluster", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("proxy", tc.Config.WebProxyAddr), + attribute.String("proxy_web", tc.Config.WebProxyAddr), + attribute.String("proxy_ssh", tc.Config.SSHProxyAddr), ), ) defer span.End() - var err error - var proxyClient *ProxyClient + cfg, err := tc.generateClientConfig(ctx) + if err != nil { + return nil, trace.Wrap(err) + } - // Use connectContext and the cancel function to signal when a response is - // returned from connectToProxy. - connectContext, cancel := context.WithCancel(ctx) - go func() { - defer cancel() - proxyClient, err = tc.connectToProxy(ctx) - }() + tlsConfig, err := tc.LoadTLSConfig() + if err != nil { + return nil, trace.Wrap(err) + } - select { - // ConnectToProxy returned a result, return that back to the caller. - case <-connectContext.Done(): - return proxyClient, trace.Wrap(formatConnectToProxyErr(err)) - // The passed in context timed out. This is often due to the network being - // down and the user hitting Ctrl-C. - case <-ctx.Done(): - return nil, trace.ConnectionProblem(ctx.Err(), "connection canceled") + pclt, err := proxyclient.NewClient(ctx, proxyclient.ClientConfig{ + ProxyWebAddress: tc.WebProxyAddr, + ProxySSHAddress: cfg.proxyAddress, + TLSRoutingEnabled: tc.TLSRoutingEnabled, + TLSConfig: tlsConfig, + DialOpts: tc.Config.DialOpts, + UnaryInterceptors: []grpc.UnaryClientInterceptor{utils.GRPCClientUnaryErrorInterceptor}, + StreamInterceptors: []grpc.StreamClientInterceptor{utils.GRPCClientStreamErrorInterceptor}, + SSHDialer: proxyclient.SSHDialerFunc(func(ctx context.Context, network string, addr string, config *ssh.ClientConfig) (*tracessh.Client, error) { + clt, err := makeProxySSHClient(ctx, tc, config) + return clt, trace.Wrap(err) + }), + SSHConfig: cfg.ClientConfig, + }) + if err != nil { + return nil, trace.Wrap(err) } + + aclt, err := auth.NewClient(pclt.ClientConfig(ctx, pclt.ClusterName())) + if err != nil { + return nil, trace.NewAggregate(err, pclt.Close()) + } + + return &ClusterClient{ + tc: tc, + ProxyClient: pclt, + AuthClient: aclt, + Tracer: tc.Tracer, + }, nil } -// connectToProxy will dial to the proxy server and return a ProxyClient when -// successful. -func (tc *TeleportClient) connectToProxy(ctx context.Context) (*ProxyClient, error) { +// clientConfig wraps ssh.ClientConfig with additional +// information about a cluster. +type clientConfig struct { + *ssh.ClientConfig + proxyAddress string + clusterName func() string +} + +// generateClientConfig returns clientConfig that can be used to establish a +// connection to a cluster. +func (tc *TeleportClient) generateClientConfig(ctx context.Context) (*clientConfig, error) { sshProxyAddr := tc.Config.SSHProxyAddr hostKeyCallback := tc.HostKeyCallback @@ -2800,13 +2775,64 @@ func (tc *TeleportClient) connectToProxy(ctx context.Context) (*ProxyClient, err return nil, trace.BadParameter("no SSH auth methods loaded, are you logged in?") } - sshConfig := &ssh.ClientConfig{ - User: tc.getProxySSHPrincipal(), - HostKeyCallback: hostKeyCallback, - Auth: authMethods, + return &clientConfig{ + ClientConfig: &ssh.ClientConfig{ + User: tc.getProxySSHPrincipal(), + HostKeyCallback: hostKeyCallback, + Auth: authMethods, + Timeout: apidefaults.DefaultIOTimeout, + }, + proxyAddress: sshProxyAddr, + clusterName: clusterName, + }, nil +} + +// ConnectToProxy will dial to the proxy server and return a ProxyClient when +// successful. If the passed in context is canceled, this function will return +// a trace.ConnectionProblem right away. +func (tc *TeleportClient) ConnectToProxy(ctx context.Context) (*ProxyClient, error) { + ctx, span := tc.Tracer.Start( + ctx, + "teleportClient/ConnectToProxy", + oteltrace.WithSpanKind(oteltrace.SpanKindClient), + oteltrace.WithAttributes( + attribute.String("proxy_web", tc.Config.WebProxyAddr), + attribute.String("proxy_ssh", tc.Config.SSHProxyAddr), + ), + ) + defer span.End() + + var err error + var proxyClient *ProxyClient + + // Use connectContext and the cancel function to signal when a response is + // returned from connectToProxy. + connectContext, cancel := context.WithCancel(ctx) + go func() { + defer cancel() + proxyClient, err = tc.connectToProxy(connectContext) + }() + + select { + // connectToProxy returned a result, return that back to the caller. + case <-connectContext.Done(): + return proxyClient, trace.Wrap(formatConnectToProxyErr(err)) + // The passed in context timed out. This is often due to the network being + // down and the user hitting Ctrl-C. + case <-ctx.Done(): + return nil, trace.ConnectionProblem(ctx.Err(), "connection canceled") + } +} + +// connectToProxy will dial to the proxy server and return a ProxyClient when +// successful. +func (tc *TeleportClient) connectToProxy(ctx context.Context) (*ProxyClient, error) { + cfg, err := tc.generateClientConfig(ctx) + if err != nil { + return nil, trace.Wrap(err) } - sshClient, err := makeProxySSHClient(ctx, tc, sshConfig) + sshClient, err := makeProxySSHClient(ctx, tc, cfg.ClientConfig) if err != nil { return nil, trace.Wrap(err) } @@ -2814,12 +2840,12 @@ func (tc *TeleportClient) connectToProxy(ctx context.Context) (*ProxyClient, err pc := &ProxyClient{ teleportClient: tc, Client: sshClient, - proxyAddress: sshProxyAddr, - proxyPrincipal: sshConfig.User, - hostKeyCallback: sshConfig.HostKeyCallback, - authMethods: sshConfig.Auth, + proxyAddress: cfg.proxyAddress, + proxyPrincipal: cfg.User, + hostKeyCallback: cfg.HostKeyCallback, + authMethods: cfg.Auth, hostLogin: tc.HostLogin, - siteName: clusterName(), + siteName: cfg.clusterName(), clientAddr: tc.ClientAddr, Tracer: tc.Tracer, } @@ -4503,7 +4529,8 @@ func (tc *TeleportClient) HeadlessApprove(ctx context.Context, headlessAuthentic "teleportClient/HeadlessApprove", oteltrace.WithSpanKind(oteltrace.SpanKindClient), oteltrace.WithAttributes( - attribute.String("proxy", tc.Config.WebProxyAddr), + attribute.String("proxy_web", tc.Config.WebProxyAddr), + attribute.String("proxy_ssh", tc.Config.SSHProxyAddr), ), ) defer span.End() diff --git a/lib/client/client.go b/lib/client/client.go index 7edf06e797055..cc1e6941858ae 100644 --- a/lib/client/client.go +++ b/lib/client/client.go @@ -1258,29 +1258,6 @@ func nodeName(node string) string { return n } -// clusterDetails retrieves information about the current cluster needed to connect to nodes. -func (proxy *ProxyClient) clusterDetails(ctx context.Context) (sshutils.ClusterDetails, error) { - ctx, span := proxy.Tracer.Start( - ctx, - "proxyClient/clusterDetails", - oteltrace.WithSpanKind(oteltrace.SpanKindClient), - ) - defer span.End() - - var details sshutils.ClusterDetails - ok, resp, err := proxy.Client.SendRequest(ctx, teleport.ClusterDetailsReqType, true, nil) - if err != nil { - return details, trace.Wrap(err) - } - - if ok { - err = ssh.Unmarshal(resp, &details) - return details, trace.Wrap(err) - } - - return details, trace.BadParameter("failed to get cluster details") -} - // dialAuthServer returns auth server connection forwarded via proxy func (proxy *ProxyClient) dialAuthServer(ctx context.Context, clusterName string) (net.Conn, error) { ctx, span := proxy.Tracer.Start( @@ -1401,7 +1378,7 @@ func requestSubsystem(ctx context.Context, session *tracessh.Session, name strin // ConnectToNode connects to the ssh server via Proxy. // It returns connected and authenticated NodeClient -func (proxy *ProxyClient) ConnectToNode(ctx context.Context, nodeAddress NodeDetails, user string, details sshutils.ClusterDetails, authMethods []ssh.AuthMethod) (*NodeClient, error) { +func (proxy *ProxyClient) ConnectToNode(ctx context.Context, nodeAddress NodeDetails, user string, details sshutils.ClusterDetails) (*NodeClient, error) { ctx, span := proxy.Tracer.Start( ctx, "proxyClient/ConnectToNode", @@ -1416,7 +1393,7 @@ func (proxy *ProxyClient) ConnectToNode(ctx context.Context, nodeAddress NodeDet log.Infof("Client=%v connecting to node=%v", proxy.clientAddr, nodeAddress) if len(proxy.teleportClient.JumpHosts) > 0 { - return proxy.PortForwardToNode(ctx, nodeAddress, user, details, authMethods) + return proxy.PortForwardToNode(ctx, nodeAddress, user, details, proxy.authMethods) } // parse destination first: @@ -1498,7 +1475,7 @@ func (proxy *ProxyClient) ConnectToNode(ctx context.Context, nodeAddress NodeDet sshConfig := &ssh.ClientConfig{ User: user, - Auth: authMethods, + Auth: proxy.authMethods, HostKeyCallback: proxy.hostKeyCallback, } @@ -1651,6 +1628,40 @@ func (c *NodeClient) RunInteractiveShell(ctx context.Context, mode types.Session return nil } +// RunCommand executes a given bash command on the node. +func (c *NodeClient) RunCommand(ctx context.Context, command []string) error { + ctx, span := c.Tracer.Start( + ctx, + "nodeClient/RunCommand", + oteltrace.WithSpanKind(oteltrace.SpanKindClient), + ) + defer span.End() + + nodeSession, err := newSession(ctx, c, nil, c.TC.newSessionEnv(), c.TC.Stdin, c.TC.Stdout, c.TC.Stderr, c.TC.EnableEscapeSequences) + if err != nil { + return trace.Wrap(err) + } + defer nodeSession.Close() + if err := nodeSession.runCommand(ctx, types.SessionPeerMode, command, c.TC.OnShellCreated, c.TC.Config.InteractiveCommand); err != nil { + originErr := trace.Unwrap(err) + exitErr, ok := originErr.(*ssh.ExitError) + if ok { + c.TC.ExitStatus = exitErr.ExitStatus() + } else { + // if an error occurs, but no exit status is passed back, GoSSH returns + // a generic error like this. in this case the error message is printed + // to stderr by the remote process so we have to quietly return 1: + if strings.Contains(originErr.Error(), "exited without exit status") { + c.TC.ExitStatus = 1 + } + } + + return trace.Wrap(err) + } + + return nil +} + func (c *NodeClient) handleGlobalRequests(ctx context.Context, requestCh <-chan *ssh.Request) { for { select { @@ -2029,37 +2040,6 @@ func (c *NodeClient) Close() error { return c.Client.Close() } -func (proxy *ProxyClient) sessionSSHCertificate(ctx context.Context, nodeAddr NodeDetails) ([]ssh.AuthMethod, error) { - if _, err := proxy.teleportClient.localAgent.GetKey(nodeAddr.Cluster); err != nil { - if trace.IsNotFound(err) { - // Either running inside the web UI in a proxy or using an identity - // file. Fall back to whatever AuthMethod we currently have. - return proxy.authMethods, nil - } - return nil, trace.Wrap(err) - } - - key, err := proxy.IssueUserCertsWithMFA( - ctx, - ReissueParams{ - NodeName: nodeName(nodeAddr.Addr), - RouteToCluster: proxy.ClusterName(), - MFACheck: nodeAddr.MFACheck, - }, - func(ctx context.Context, proxyAddr string, c *proto.MFAAuthenticateChallenge) (*proto.MFAAuthenticateResponse, error) { - return proxy.teleportClient.PromptMFAChallenge(ctx, proxyAddr, c, nil /* applyOpts */) - }, - ) - if err != nil { - return nil, trace.Wrap(err) - } - am, err := key.AsAuthMethod() - if err != nil { - return nil, trace.Wrap(err) - } - return []ssh.AuthMethod{am}, nil -} - // localAgent returns for the Teleport client's local agent. func (proxy *ProxyClient) localAgent() *LocalKeyAgent { return proxy.teleportClient.LocalAgent() diff --git a/lib/client/cluster_client.go b/lib/client/cluster_client.go new file mode 100644 index 0000000000000..04d952ae1bb27 --- /dev/null +++ b/lib/client/cluster_client.go @@ -0,0 +1,365 @@ +// 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 client + +import ( + "context" + + "github.com/gravitational/trace" + "go.opentelemetry.io/otel/attribute" + oteltrace "go.opentelemetry.io/otel/trace" + "golang.org/x/crypto/ssh" + + "github.com/gravitational/teleport/api/client/proto" + proxyclient "github.com/gravitational/teleport/api/client/proxy" + "github.com/gravitational/teleport/lib/auth" +) + +// ClusterClient facilitates communicating with both the +// Auth and Proxy services of a cluster. +type ClusterClient struct { + tc *TeleportClient + ProxyClient *proxyclient.Client + AuthClient auth.ClientI + Tracer oteltrace.Tracer +} + +// ClusterName returns the name of the cluster that the client +// is connected to. +func (c *ClusterClient) ClusterName() string { + cluster := c.ProxyClient.ClusterName() + if len(c.tc.JumpHosts) > 0 && cluster != "" { + return cluster + } + + return c.tc.SiteName +} + +// Close terminates the connections to Auth and Proxy. +func (c *ClusterClient) Close() error { + // close auth client first since it is tunneled through the proxy client + return trace.NewAggregate(c.AuthClient.Close(), c.ProxyClient.Close()) +} + +// SessionSSHConfig returns the [ssh.ClientConfig] that should be used to connected to the +// provided target for the provided user. If per session MFA is required to establish the +// connection, then the MFA ceremony will be performed. +func (c *ClusterClient) SessionSSHConfig(ctx context.Context, user string, target NodeDetails) (*ssh.ClientConfig, error) { + ctx, span := c.Tracer.Start( + ctx, + "clusterClient/SessionSSHConfig", + oteltrace.WithSpanKind(oteltrace.SpanKindClient), + oteltrace.WithAttributes( + attribute.String("cluster", c.tc.SiteName), + ), + ) + defer span.End() + + sshConfig := c.ProxyClient.SSHConfig(user) + + if target.MFACheck != nil && !target.MFACheck.Required { + return sshConfig, nil + } + + key, err := c.tc.localAgent.GetKey(target.Cluster, WithAllCerts...) + if err != nil { + if trace.IsNotFound(err) { + // Either running inside the web UI in a proxy or using an identity + // file. Fall back to whatever AuthMethod we currently have. + return sshConfig, nil + } + return nil, trace.Wrap(err) + } + + params := ReissueParams{ + NodeName: nodeName(target.Addr), + RouteToCluster: target.Cluster, + MFACheck: target.MFACheck, + } + + // requiredCheck passed from param can be nil. + if target.MFACheck == nil { + check, err := c.AuthClient.IsMFARequired(ctx, params.isMFARequiredRequest(c.tc.HostLogin)) + if err != nil { + return nil, trace.Wrap(err) + } + target.MFACheck = check + } + + if !target.MFACheck.Required { + log.Debug("MFA not required for access.") + // MFA is not required. + // SSH certs can be used without embedding the node name. + if params.usage() == proto.UserCertsRequest_SSH { + return sshConfig, nil + } + + // All other targets need their name embedded in the cert for routing, + // fall back to non-MFA reissue. + key, err := c.reissueUserCerts(ctx, CertCacheKeep, params) + if err != nil { + return nil, trace.Wrap(err) + } + + am, err := key.AsAuthMethod() + if err != nil { + return nil, trace.Wrap(err) + } + + sshConfig.Auth = []ssh.AuthMethod{am} + return sshConfig, nil + } + + // Always connect to root for getting new credentials, but attempt to reuse + // the existing client if possible. + rootClusterName, err := key.RootClusterName() + if err != nil { + return nil, trace.Wrap(err) + } + + mfaClt := c + if params.RouteToCluster != rootClusterName { + jumpHosts := c.tc.JumpHosts + // In case of MFA connect to root teleport proxy instead of JumpHost to request + // MFA certificates. + c.tc.JumpHosts = nil + clt, err := c.tc.ConnectToCluster(ctx) + c.tc.JumpHosts = jumpHosts + if err != nil { + return nil, trace.Wrap(err) + } + + mfaClt = clt + defer clt.Close() + } + + log.Debug("Attempting to issue a single-use user certificate with an MFA check.") + key, err = performMFACeremony(ctx, mfaClt, params, key) + if err != nil { + return nil, trace.Wrap(err) + } + + log.Debug("Issued single-use user certificate after an MFA check.") + am, err := key.AsAuthMethod() + if err != nil { + return nil, trace.Wrap(err) + } + + sshConfig.Auth = []ssh.AuthMethod{am} + return sshConfig, nil +} + +// reissueUserCerts gets new user certificates from the root Auth server. +func (c *ClusterClient) reissueUserCerts(ctx context.Context, cachePolicy CertCachePolicy, params ReissueParams) (*Key, error) { + if params.RouteToCluster == "" { + params.RouteToCluster = c.tc.SiteName + } + key := params.ExistingCreds + if key == nil { + var err error + + // Don't load the certs if we're going to drop all of them all as part + // of the re-issue. If we load all of the old certs now we won't be able + // to differentiate between legacy certificates (that need to be + // deleted) and newly re-issued certs (that we definitely do *not* want + // to delete) when it comes time to drop them from the local agent. + var certOptions []CertOption + if cachePolicy == CertCacheKeep { + certOptions = WithAllCerts + } + + key, err = c.tc.localAgent.GetKey(params.RouteToCluster, certOptions...) + if err != nil { + return nil, trace.Wrap(err) + } + } + + req, err := c.prepareUserCertsRequest(params, key) + if err != nil { + return nil, trace.Wrap(err) + } + + root, err := c.tc.rootClusterName() + if err != nil { + return nil, trace.Wrap(err) + } + + clt, err := auth.NewClient(c.ProxyClient.ClientConfig(ctx, root)) + if err != nil { + return nil, trace.Wrap(err) + } + defer clt.Close() + + certs, err := clt.GenerateUserCerts(ctx, *req) + if err != nil { + return nil, trace.Wrap(err) + } + + key.ClusterName = params.RouteToCluster + + // Only update the parts of key that match the usage. See the docs on + // proto.UserCertsRequest_CertUsage for which certificates match which + // usage. + // + // This prevents us from overwriting the top-level key.TLSCert with + // usage-restricted certificates. + switch params.usage() { + case proto.UserCertsRequest_All: + key.Cert = certs.SSH + key.TLSCert = certs.TLS + case proto.UserCertsRequest_SSH: + key.Cert = certs.SSH + case proto.UserCertsRequest_App: + key.AppTLSCerts[params.RouteToApp.Name] = certs.TLS + case proto.UserCertsRequest_Database: + dbCert, err := makeDatabaseClientPEM(params.RouteToDatabase.Protocol, certs.TLS, key) + if err != nil { + return nil, trace.Wrap(err) + } + key.DBTLSCerts[params.RouteToDatabase.ServiceName] = dbCert + case proto.UserCertsRequest_Kubernetes: + key.KubeTLSCerts[params.KubernetesCluster] = certs.TLS + case proto.UserCertsRequest_WindowsDesktop: + key.WindowsDesktopCerts[params.RouteToWindowsDesktop.WindowsDesktop] = certs.TLS + } + return key, nil +} + +// prepareUserCertsRequest creates a [proto.UserCertsRequest] with the fields +// set accordingly from the provided ReissueParams. +func (c *ClusterClient) prepareUserCertsRequest(params ReissueParams, key *Key) (*proto.UserCertsRequest, error) { + tlsCert, err := key.TeleportTLSCertificate() + if err != nil { + return nil, trace.Wrap(err) + } + + if len(params.AccessRequests) == 0 { + // Get the active access requests to include in the cert. + activeRequests, err := key.ActiveRequests() + // key.ActiveRequests can return a NotFound error if it doesn't have an + // SSH cert. That's OK, we just assume that there are no AccessRequests + // in that case. + if err != nil && !trace.IsNotFound(err) { + return nil, trace.Wrap(err) + } + params.AccessRequests = activeRequests.AccessRequests + } + + return &proto.UserCertsRequest{ + PublicKey: key.MarshalSSHPublicKey(), + Username: tlsCert.Subject.CommonName, + Expires: tlsCert.NotAfter, + RouteToCluster: params.RouteToCluster, + KubernetesCluster: params.KubernetesCluster, + AccessRequests: params.AccessRequests, + DropAccessRequests: params.DropAccessRequests, + RouteToDatabase: params.RouteToDatabase, + RouteToWindowsDesktop: params.RouteToWindowsDesktop, + RouteToApp: params.RouteToApp, + NodeName: params.NodeName, + Usage: params.usage(), + Format: c.tc.CertificateFormat, + RequesterName: params.RequesterName, + }, nil +} + +// performMFACeremony runs the mfa ceremony to completion. If successful the returned +// [Key] will be authorized to connect to the target. +func performMFACeremony(ctx context.Context, clt *ClusterClient, params ReissueParams, key *Key) (*Key, error) { + stream, err := clt.AuthClient.GenerateUserSingleUseCerts(ctx) + if err != nil { + if trace.IsNotImplemented(err) { + // Probably talking to an older server, use the old non-MFA endpoint. + log.WithError(err).Debug("Auth server does not implement GenerateUserSingleUseCerts.") + // SSH certs can be used without reissuing. + if params.usage() == proto.UserCertsRequest_SSH && key.Cert != nil { + return key, nil + } + + key, err := clt.reissueUserCerts(ctx, CertCacheKeep, params) + return key, trace.Wrap(err) + } + return nil, trace.Wrap(err) + } + defer func() { + // CloseSend closes the client side of the stream + stream.CloseSend() + // Recv to wait for the server side of the stream to end, this needs to + // be called to ensure the spans are finished properly + stream.Recv() + }() + + initReq, err := clt.prepareUserCertsRequest(params, key) + if err != nil { + return nil, trace.Wrap(err) + } + + err = stream.Send(&proto.UserSingleUseCertsRequest{Request: &proto.UserSingleUseCertsRequest_Init{ + Init: initReq, + }}) + if err != nil { + return nil, trace.Wrap(err) + } + + resp, err := stream.Recv() + if err != nil { + return nil, trace.Wrap(err) + } + mfaChal := resp.GetMFAChallenge() + if mfaChal == nil { + return nil, trace.BadParameter("server sent a %T on GenerateUserSingleUseCerts, expected MFAChallenge", resp.Response) + } + mfaResp, err := clt.tc.PromptMFAChallenge(ctx, clt.tc.WebProxyAddr, mfaChal, nil /* applyOpts */) + if err != nil { + return nil, trace.Wrap(err) + } + err = stream.Send(&proto.UserSingleUseCertsRequest{Request: &proto.UserSingleUseCertsRequest_MFAResponse{MFAResponse: mfaResp}}) + if err != nil { + return nil, trace.Wrap(err) + } + + resp, err = stream.Recv() + if err != nil { + return nil, trace.Wrap(err) + } + certResp := resp.GetCert() + if certResp == nil { + return nil, trace.BadParameter("server sent a %T on GenerateUserSingleUseCerts, expected SingleUseUserCert", resp.Response) + } + switch crt := certResp.Cert.(type) { + case *proto.SingleUseUserCert_SSH: + key.Cert = crt.SSH + case *proto.SingleUseUserCert_TLS: + switch initReq.Usage { + case proto.UserCertsRequest_Kubernetes: + key.KubeTLSCerts[initReq.KubernetesCluster] = crt.TLS + case proto.UserCertsRequest_Database: + dbCert, err := makeDatabaseClientPEM(params.RouteToDatabase.Protocol, crt.TLS, key) + if err != nil { + return nil, trace.Wrap(err) + } + key.DBTLSCerts[params.RouteToDatabase.ServiceName] = dbCert + case proto.UserCertsRequest_WindowsDesktop: + key.WindowsDesktopCerts[params.RouteToWindowsDesktop.WindowsDesktop] = crt.TLS + default: + return nil, trace.BadParameter("server returned a TLS certificate but cert request usage was %s", initReq.Usage) + } + default: + return nil, trace.BadParameter("server sent a %T SingleUseUserCert in response", certResp.Cert) + } + key.ClusterName = params.RouteToCluster + + return key, nil +} diff --git a/lib/proxy/router.go b/lib/proxy/router.go index 3427bd95d483e..7be0099c47954 100644 --- a/lib/proxy/router.go +++ b/lib/proxy/router.go @@ -191,23 +191,19 @@ func NewRouter(cfg RouterConfig) (*Router, error) { }, nil } -// SignerCreator allows the caller to configure a [ssh.Signer] if a ssh -// user agent isn't available, ie when connecting to agentless nodes. -type SignerCreator func() (ssh.Signer, error) - // DialHost dials the node that matches the provided host, port and cluster. If no matching node // is found an error is returned. If more than one matching node is found and the cluster networking // configuration is not set to route to the most recent an error is returned. Also returns teleport version of the // target server if it's a teleport server // DELETE IN 14.0: remove returning teleport version, it was needed for compatibility -func (r *Router) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net.Addr, host, port, clusterName string, accessChecker services.AccessChecker, agentGetter teleagent.Getter, singerCreator SignerCreator) (_ net.Conn, teleportVersion string, err error) { +func (r *Router) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net.Addr, host, port, clusterName string, accessChecker services.AccessChecker, agentGetter teleagent.Getter, signer func(context.Context) (ssh.Signer, error)) (_ net.Conn, teleportVersion string, err error) { ctx, span := r.tracer.Start( ctx, "router/DialHost", oteltrace.WithAttributes( attribute.String("host", host), attribute.String("port", port), - attribute.String("site", clusterName), + attribute.String("cluster", clusterName), ), ) defer func() { @@ -279,9 +275,9 @@ func (r *Router) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net. // if the node is a registered openssh node, create a signer for auth // and don't set agentGetter so a SSH user agent will not be created // when connecting to the remote node - var signer ssh.Signer + var sshSigner ssh.Signer if isAgentlessNode { - signer, err = singerCreator() + sshSigner, err = signer(ctx) if err != nil { return nil, "", trace.Wrap(err) } @@ -293,7 +289,7 @@ func (r *Router) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net. To: &utils.NetAddr{AddrNetwork: "tcp", Addr: serverAddr}, OriginalClientDstAddr: clientDstAddr, GetUserAgent: agentGetter, - AgentlessSigner: signer, + AgentlessSigner: sshSigner, Address: host, Principals: principals, ServerID: serverID, @@ -316,7 +312,7 @@ func (r *Router) getRemoteCluster(ctx context.Context, clusterName string, check ctx, "router/getRemoteCluster", oteltrace.WithAttributes( - attribute.String("site", clusterName), + attribute.String("cluster", clusterName), ), ) defer span.End() @@ -458,7 +454,7 @@ func (r *Router) DialSite(ctx context.Context, clusterName string, clientSrcAddr ctx, "router/DialSite", oteltrace.WithAttributes( - attribute.String("site", clusterName), + attribute.String("cluster", clusterName), ), ) defer span.End() diff --git a/lib/proxy/router_test.go b/lib/proxy/router_test.go index 70465a32ef85c..c5102fac7e32e 100644 --- a/lib/proxy/router_test.go +++ b/lib/proxy/router_test.go @@ -360,7 +360,7 @@ func TestRouter_DialHost(t *testing.T) { agentGetter := func() (teleagent.Agent, error) { return nil, nil } - createSigner := func() (ssh.Signer, error) { + createSigner := func(context.Context) (ssh.Signer, error) { key, err := native.GeneratePrivateKey() if err != nil { return nil, err diff --git a/lib/service/service.go b/lib/service/service.go index da106f3580270..9899e0b053e00 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -62,9 +62,12 @@ import ( "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" kubeproto "github.com/gravitational/teleport/api/gen/proto/go/teleport/kube/v1" + transportpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/transport/v1" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" apiutils "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/lib" + "github.com/gravitational/teleport/lib/agentless" "github.com/gravitational/teleport/lib/auditd" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/keygen" @@ -116,6 +119,7 @@ import ( "github.com/gravitational/teleport/lib/srv/desktop" "github.com/gravitational/teleport/lib/srv/ingress" "github.com/gravitational/teleport/lib/srv/regular" + "github.com/gravitational/teleport/lib/srv/transport/transportv1" "github.com/gravitational/teleport/lib/system" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" @@ -3852,11 +3856,16 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { ClusterName: clusterName, } + tlscfg := serverTLSConfig.Clone() + tlscfg.ClientAuth = tls.RequireAndVerifyClientCert + if lib.IsInsecureDevMode() { + tlscfg.InsecureSkipVerify = true + tlscfg.ClientAuth = tls.RequireAnyClientCert + } creds, err := auth.NewTransportCredentials(auth.TransportCredentialsConfig{ - TransportCredentials: credentials.NewTLS(serverTLSConfig), + TransportCredentials: credentials.NewTLS(tlscfg), UserGetter: authMiddleware, Authorizer: authorizer, - Enforcer: sessionController, }) if err != nil { return trace.Wrap(err) @@ -3874,6 +3883,33 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { grpc.Creds(creds), ) + connMonitor, err := srv.NewConnectionMonitor(srv.ConnectionMonitorConfig{ + AccessPoint: accessPoint, + LockWatcher: lockWatcher, + Clock: process.Clock, + ServerID: serverID, + Emitter: streamEmitter, + Logger: process.log, + }) + if err != nil { + return trace.Wrap(err) + } + + transportService, err := transportv1.NewService(transportv1.ServerConfig{ + FIPS: cfg.FIPS, + Logger: process.log.WithField(trace.Component, "transport"), + Dialer: proxyRouter, + SignerFn: func(authzCtx *authz.Context) func(context.Context) (ssh.Signer, error) { + return agentless.SignerFromAuthzContext(authzCtx, conn.Client) + }, + ConnectionMonitor: connMonitor, + LocalAddr: listeners.sshGRPC.Addr(), + }) + if err != nil { + return trace.Wrap(err) + } + transportpb.RegisterTransportServiceServer(sshGRPCServer, transportService) + process.RegisterCriticalFunc("proxy.ssh", func() error { utils.Consolef(cfg.Console, log, teleport.ComponentProxy, "SSH proxy service %s:%s is starting on %v.", teleport.Version, teleport.Gitref, cfg.Proxy.SSHAddr.Addr) diff --git a/lib/srv/db/sqlserver/connect.go b/lib/srv/db/sqlserver/connect.go index ee099b3f01f40..631b247be5368 100644 --- a/lib/srv/db/sqlserver/connect.go +++ b/lib/srv/db/sqlserver/connect.go @@ -29,12 +29,22 @@ import ( "github.com/microsoft/go-mssqldb/azuread" "github.com/microsoft/go-mssqldb/msdsn" + "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/windows" "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/sqlserver/kinit" "github.com/gravitational/teleport/lib/srv/db/sqlserver/protocol" ) +const ( + // ResourceIDDSNKey represents the resource ID DSN parameter key. This value + // is defined by the go-mssqldb library. + ResourceIDDSNKey = "resource id" + // FederatedAuthDSNKey represents the federated auth DSN parameter key. This + // value is defined by the go-mssqldb library. + FederatedAuthDSNKey = "fedauth" +) + // Connector defines an interface for connecting to a SQL Server so it can be // swapped out in tests. type Connector interface { @@ -109,35 +119,19 @@ func (c *connector) Connect(ctx context.Context, sessionCtx *common.Session, log } var connector *mssql.Connector - if sessionCtx.Database.IsAzure() && sessionCtx.Database.GetAD().Domain == "" { + switch { + case sessionCtx.Database.IsAzure() && sessionCtx.Database.GetAD().Domain == "": // If the client is connecting to Azure SQL, and no AD configuration is // provided, authenticate using the Azure AD Integrated authentication // method. - managedIdentityID, err := c.DBAuth.GetAzureIdentityResourceID(ctx, sessionCtx.DatabaseUser) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - dsnConfig.Parameters = map[string]string{ - "fedauth": azuread.ActiveDirectoryManagedIdentity, - "resource id": managedIdentityID, - } - - connector, err = azuread.NewConnectorFromConfig(dsnConfig) - if err != nil { - return nil, nil, trace.Wrap(err) - } - } else { - kc, err := c.getKerberosClient(ctx, sessionCtx) - if err != nil { - return nil, nil, trace.Wrap(err) - } - dbAuth, err := c.getAuth(sessionCtx, kc) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - connector = mssql.NewConnectorConfig(dsnConfig, dbAuth) + connector, err = c.getAzureConnector(ctx, sessionCtx, dsnConfig) + case sessionCtx.Database.GetType() == types.DatabaseTypeRDSProxy: + connector, err = c.getAccessTokenConnector(sessionCtx, dsnConfig) + default: + connector, err = c.getKerberosConnector(ctx, sessionCtx, dsnConfig) + } + if err != nil { + return nil, nil, trace.Wrap(err) } conn, err := connector.Connect(ctx) @@ -154,3 +148,46 @@ func (c *connector) Connect(ctx context.Context, sessionCtx *common.Session, log // back to the client. return mssqlConn.GetUnderlyingConn(), mssqlConn.GetLoginFlags(), nil } + +// getKerberosConnector generates a Kerberos connector using proper Kerberos +// client. +func (c *connector) getKerberosConnector(ctx context.Context, sessionCtx *common.Session, dsnConfig msdsn.Config) (*mssql.Connector, error) { + kc, err := c.getKerberosClient(ctx, sessionCtx) + if err != nil { + return nil, trace.Wrap(err) + } + dbAuth, err := c.getAuth(sessionCtx, kc) + if err != nil { + return nil, trace.Wrap(err) + } + + return mssql.NewConnectorConfig(dsnConfig, dbAuth), nil +} + +// getAzureConnector generates a connector that authenticates using Azure AD. +func (c *connector) getAzureConnector(ctx context.Context, sessionCtx *common.Session, dsnConfig msdsn.Config) (*mssql.Connector, error) { + managedIdentityID, err := c.DBAuth.GetAzureIdentityResourceID(ctx, sessionCtx.DatabaseUser) + if err != nil { + return nil, trace.Wrap(err) + } + + dsnConfig.Parameters = map[string]string{ + FederatedAuthDSNKey: azuread.ActiveDirectoryManagedIdentity, + ResourceIDDSNKey: managedIdentityID, + } + + connector, err := azuread.NewConnectorFromConfig(dsnConfig) + if err != nil { + return nil, trace.Wrap(err) + } + + return connector, nil +} + +// getAccessTokenConnector generates a connector that uses a token to +// authenticate. +func (c *connector) getAccessTokenConnector(sessionCtx *common.Session, dsnConfig msdsn.Config) (*mssql.Connector, error) { + return mssql.NewSecurityTokenConnector(dsnConfig, func(ctx context.Context) (string, error) { + return c.DBAuth.GetRDSAuthToken(sessionCtx) + }) +} diff --git a/lib/srv/db/sqlserver/connect_test.go b/lib/srv/db/sqlserver/connect_test.go index 495b86d74b14a..7c77d53d5b5fe 100644 --- a/lib/srv/db/sqlserver/connect_test.go +++ b/lib/srv/db/sqlserver/connect_test.go @@ -97,6 +97,24 @@ func TestConnectorSelection(t *testing.T) { require.Contains(t, err.Error(), "unable to open tcp connection with host") }, }, + { + desc: "RDS Proxied database", + databaseSpec: types.DatabaseSpecV3{ + Protocol: defaults.ProtocolSQLServer, + URI: "proxy-sqlserver.proxy-000000000000.us-east-1.rds.amazonaws.com:1433", + AWS: types.AWS{ + RDSProxy: types.RDSProxy{ + Name: "proxy-sqlserver", + }, + }, + }, + // RDS proxies cannot be accessed outside their VPC. So, this test + // case should not resolve their host. + errAssertion: func(t require.TestingT, err error, _ ...interface{}) { + require.Error(t, err) + require.Contains(t, err.Error(), "no such host") + }, + }, } { t.Run(tt.desc, func(t *testing.T) { database, err := types.NewDatabaseV3(types.Metadata{ diff --git a/lib/srv/discovery/fetchers/db/aws_rds_proxy.go b/lib/srv/discovery/fetchers/db/aws_rds_proxy.go index f23d52f95b71b..cac0f6dcf9e6f 100644 --- a/lib/srv/discovery/fetchers/db/aws_rds_proxy.go +++ b/lib/srv/discovery/fetchers/db/aws_rds_proxy.go @@ -82,14 +82,6 @@ func (f *rdsDBProxyFetcher) getRDSProxyDatabases(ctx context.Context) (types.Dat var databases types.Databases for _, dbProxy := range rdsProxies { - // TODO(greedy52) RDS Proxy supports MS SQL Server but it requires MS - // SQL Server engine support for IAM auth. - // - // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-setup.html#rds-proxy-connecting-sqlserver - if aws.StringValue(dbProxy.EngineFamily) == rds.EngineFamilySqlserver { - f.log.Debugf("RDS Proxy %q with engine family %q is not supported yet. Skipping.", aws.StringValue(dbProxy.DBProxyName), aws.StringValue(dbProxy.EngineFamily)) - continue - } if !aws.BoolValue(dbProxy.RequireTLS) { f.log.Debugf("RDS Proxy %q doesn't support TLS. Skipping.", aws.StringValue(dbProxy.DBProxyName)) continue diff --git a/lib/srv/regular/proxy.go b/lib/srv/regular/proxy.go index fad37e5c90bcb..65bbf9e3d9bc5 100644 --- a/lib/srv/regular/proxy.go +++ b/lib/srv/regular/proxy.go @@ -24,7 +24,6 @@ import ( "io" "net" "strings" - "time" "github.com/gravitational/trace" "github.com/sirupsen/logrus" @@ -261,12 +260,14 @@ func (t *proxySubsys) proxyToHost(ctx context.Context, ch ssh.Channel, clientSrc t.log.Debugf("proxy connecting to host=%v port=%v, exact port=%v", t.host, t.port, t.SpecifiedPort()) aGetter := t.ctx.StartAgentChannel - signerCreator := func() (ssh.Signer, error) { - validBefore := time.Unix(int64(t.ctx.Identity.Certificate.ValidBefore), 0) - ttl := time.Until(validBefore) - return agentless.CreateAuthSigner(ctx, t.ctx.Identity.TeleportUser, t.clusterName, ttl, t.router) + + client, err := t.router.GetSiteClient(ctx, t.clusterName) + if err != nil { + return trace.Wrap(err) } - conn, teleportVersion, err := t.router.DialHost(ctx, clientSrcAddr, clientDstAddr, t.host, t.port, t.clusterName, t.ctx.Identity.AccessChecker, aGetter, signerCreator) + + signer := agentless.SignerFromSSHCertificate(t.ctx.Identity.Certificate, client) + conn, teleportVersion, err := t.router.DialHost(ctx, clientSrcAddr, clientDstAddr, t.host, t.port, t.clusterName, t.ctx.Identity.AccessChecker, aGetter, signer) if err != nil { return trace.Wrap(err) } diff --git a/lib/srv/transport/transportv1/transport.go b/lib/srv/transport/transportv1/transport.go index abb073b7d6e8c..a9eb17e524341 100644 --- a/lib/srv/transport/transportv1/transport.go +++ b/lib/srv/transport/transportv1/transport.go @@ -22,6 +22,7 @@ import ( "github.com/gravitational/trace" "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" @@ -29,6 +30,7 @@ import ( transportv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/transport/v1" streamutils "github.com/gravitational/teleport/api/utils/grpc/stream" "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/teleagent" "github.com/gravitational/teleport/lib/utils" @@ -36,24 +38,38 @@ import ( // Dialer is the interface that groups basic dialing methods. type Dialer interface { - DialSite(ctx context.Context, clusterName string) (net.Conn, error) - DialHost(ctx context.Context, from net.Addr, host, port, clusterName string, accessChecker services.AccessChecker, agentGetter teleagent.Getter) (net.Conn, error) + DialSite(ctx context.Context, cluster string, clientSrcAddr, clientDstAddr net.Addr) (net.Conn, error) + DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net.Addr, host, port, cluster string, checker services.AccessChecker, agentGetter teleagent.Getter, singer func(context.Context) (ssh.Signer, error)) (_ net.Conn, teleportVersion string, err error) +} + +// ConnMonitor monitors authorized connnections and terminates them when +// session controls dictate so. +type ConnectionMonitor interface { + MonitorConn(ctx context.Context, authCtx *authz.Context, conn net.Conn) (context.Context, error) } // ServerConfig holds creation parameters for Service. type ServerConfig struct { // FIPS indicates whether the cluster if configured - // to run in FIPS mode + // to run in FIPS mode. FIPS bool - // Logger provides a mechanism to log output + // Logger provides a mechanism to log output. Logger logrus.FieldLogger - // Dialer is used to establish remote connections + // Dialer is used to establish remote connections. Dialer Dialer + // SignerFn is used to create an [ssh.Signer] for an authenticated connection. + SignerFn func(authzCtx *authz.Context) func(context.Context) (ssh.Signer, error) + // ConnectionMonitor is used to monitor the connection for activity and terminate it + // when conditions are met. + ConnectionMonitor ConnectionMonitor + // LocalAddr is the local address of the service. + LocalAddr net.Addr // agentGetterFn used by tests to serve the agent directly agentGetterFn func(rw io.ReadWriter) teleagent.Getter - accessCheckerFn func(info credentials.AuthInfo) (services.AccessChecker, error) + // authzContextFn used by tests to inject an access checker + authzContextFn func(info credentials.AuthInfo) (*authz.Context, error) } // CheckAndSetDefaults ensures required parameters are set @@ -63,6 +79,10 @@ func (c *ServerConfig) CheckAndSetDefaults() error { return trace.BadParameter("parameter Dialer required") } + if c.LocalAddr == nil { + return trace.BadParameter("parameter LocalAddr required") + } + if c.Logger == nil { c.Logger = utils.NewLogger().WithField(trace.Component, "transport") } @@ -75,14 +95,14 @@ func (c *ServerConfig) CheckAndSetDefaults() error { } } - if c.accessCheckerFn == nil { - c.accessCheckerFn = func(info credentials.AuthInfo) (services.AccessChecker, error) { + if c.authzContextFn == nil { + c.authzContextFn = func(info credentials.AuthInfo) (*authz.Context, error) { identityInfo, ok := info.(auth.IdentityInfo) if !ok { return nil, trace.AccessDenied("client is not authenticated") } - return identityInfo.AuthContext.Checker, nil + return identityInfo.AuthContext, nil } } @@ -120,7 +140,13 @@ func (s *Service) ProxyCluster(stream transportv1pb.TransportService_ProxyCluste return trace.Wrap(err, "failed receiving first frame") } - conn, err := s.cfg.Dialer.DialSite(stream.Context(), req.Cluster) + ctx := stream.Context() + p, ok := peer.FromContext(ctx) + if !ok { + return trace.BadParameter("unable to find peer") + } + + conn, err := s.cfg.Dialer.DialSite(ctx, req.Cluster, p.Addr, s.cfg.LocalAddr) if err != nil { return trace.Wrap(err, "failed dialing cluster %q", req.Cluster) } @@ -139,7 +165,7 @@ func (s *Service) ProxyCluster(stream transportv1pb.TransportService_ProxyCluste return trace.Wrap(err, "failed constructing streamer") } - return trace.Wrap(utils.ProxyConn(stream.Context(), conn, streamRW)) + return trace.Wrap(utils.ProxyConn(ctx, conn, streamRW)) } // clusterStream implements the [streamutils.Source] interface @@ -168,7 +194,7 @@ func (c clusterStream) Send(frame []byte) error { // ProxySSH establishes a connection to a host and proxies both the SSH and SSH // Agent protocol over the stream. The first request from the client must contain // a valid dial target before the connection can be established. -func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) error { +func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) (err error) { ctx := stream.Context() p, ok := peer.FromContext(ctx) @@ -176,7 +202,7 @@ func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) return trace.BadParameter("unable to find peer") } - checker, err := s.cfg.accessCheckerFn(p.AuthInfo) + authzContext, err := s.cfg.authzContextFn(p.AuthInfo) if err != nil { return trace.Wrap(err) } @@ -235,15 +261,34 @@ func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) } defer agentStreamRW.Close() - conn, err := s.cfg.Dialer.DialHost(ctx, p.Addr, host, port, req.DialTarget.Cluster, checker, s.cfg.agentGetterFn(agentStreamRW)) + // create a reader/writer for SSH protocol + sshStreamRW, err := streamutils.NewReadWriter(sshStream) + if err != nil { + return trace.Wrap(err, "failed constructing ssh streamer") + } + + signer := s.cfg.SignerFn(authzContext) + hostConn, _, err := s.cfg.Dialer.DialHost(ctx, p.Addr, s.cfg.LocalAddr, host, port, req.DialTarget.Cluster, authzContext.Checker, s.cfg.agentGetterFn(agentStreamRW), signer) if err != nil { return trace.Wrap(err, "failed to dial target host") } - // create a reader/writer for SSH protocol - sshStreamRW, err := streamutils.NewReadWriter(sshStream) + // ensure the connection to the target host + // gets closed when exiting + defer func() { + hostConn.Close() + }() + + targetAddr, err := utils.ParseAddr(req.DialTarget.HostPort) if err != nil { - return trace.Wrap(err, "failed constructing ssh streamer") + return trace.Wrap(err) + } + + // monitor the user connection + userConn := streamutils.NewConn(sshStreamRW, p.Addr, targetAddr) + monitorCtx, err := s.cfg.ConnectionMonitor.MonitorConn(ctx, authzContext, userConn) + if err != nil { + return trace.Wrap(err) } // send back the cluster details to alert the other side that @@ -254,8 +299,8 @@ func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) return trace.Wrap(err, "failed sending cluster details ") } - // copy data to/from the connection and ssh stream - return trace.Wrap(utils.ProxyConn(ctx, conn, sshStreamRW)) + // copy data to/from the host/user + return trace.Wrap(utils.ProxyConn(monitorCtx, hostConn, userConn)) } // sshStream implements the [streamutils.Source] interface diff --git a/lib/srv/transport/transportv1/transport_test.go b/lib/srv/transport/transportv1/transport_test.go index 8085d7efc712a..1a1c54f2b6b06 100644 --- a/lib/srv/transport/transportv1/transport_test.go +++ b/lib/srv/transport/transportv1/transport_test.go @@ -41,6 +41,7 @@ import ( transportv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/transport/v1" streamutils "github.com/gravitational/teleport/api/utils/grpc/stream" + "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/teleagent" "github.com/gravitational/teleport/lib/utils" @@ -99,7 +100,7 @@ type fakeDialer struct { hostConns map[string]net.Conn } -func (f fakeDialer) DialSite(ctx context.Context, clusterName string) (net.Conn, error) { +func (f fakeDialer) DialSite(ctx context.Context, clusterName string, clientSrcAddr, clientDstAddr net.Addr) (net.Conn, error) { conn, ok := f.siteConns[clusterName] if !ok { return nil, trace.NotFound(clusterName) @@ -108,14 +109,14 @@ func (f fakeDialer) DialSite(ctx context.Context, clusterName string) (net.Conn, return conn, nil } -func (f fakeDialer) DialHost(ctx context.Context, from net.Addr, host, port, clusterName string, accessChecker services.AccessChecker, agentGetter teleagent.Getter) (net.Conn, error) { - key := fmt.Sprintf("%s.%s.%s", host, port, clusterName) +func (f fakeDialer) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net.Addr, host, port, cluster string, checker services.AccessChecker, agentGetter teleagent.Getter, singer func(context.Context) (ssh.Signer, error)) (_ net.Conn, teleportVersion string, err error) { + key := fmt.Sprintf("%s.%s.%s", host, port, cluster) conn, ok := f.hostConns[key] if !ok { - return nil, trace.NotFound(key) + return nil, "", trace.NotFound(key) } - return conn, nil + return conn, "", nil } // testPack used to test a [Service]. @@ -178,6 +179,18 @@ func newServer(t *testing.T, cfg ServerConfig) testPack { } } +func fakeSigner(authzCtx *authz.Context) func(context.Context) (ssh.Signer, error) { + return func(context.Context) (ssh.Signer, error) { + return nil, nil + } +} + +type fakeMonitor struct{} + +func (f fakeMonitor) MonitorConn(ctx context.Context, authCtx *authz.Context, conn net.Conn) (context.Context, error) { + return ctx, nil +} + // TestService_GetClusterDetails validates that a [Service] returns // the expected cluster details. func TestService_GetClusterDetails(t *testing.T) { @@ -200,8 +213,12 @@ func TestService_GetClusterDetails(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() srv := newServer(t, ServerConfig{ - Dialer: fakeDialer{}, - FIPS: test.FIPS, + Dialer: fakeDialer{}, + Logger: utils.NewLoggerForTests(), + FIPS: test.FIPS, + SignerFn: fakeSigner, + ConnectionMonitor: fakeMonitor{}, + LocalAddr: &utils.NetAddr{}, }) resp, err := srv.Client.GetClusterDetails(context.Background(), &transportv1pb.GetClusterDetailsRequest{}) @@ -280,6 +297,10 @@ func TestService_ProxyCluster(t *testing.T) { cluster: conn, }, }, + Logger: utils.NewLoggerForTests(), + SignerFn: fakeSigner, + ConnectionMonitor: fakeMonitor{}, + LocalAddr: &utils.NetAddr{}, }) stream, err := srv.Client.ProxyCluster(context.Background()) @@ -407,8 +428,18 @@ func TestService_ProxySSH_Errors(t *testing.T) { fakeHost: conn, }, }, - Logger: utils.NewLoggerForTests(), - accessCheckerFn: test.checkerFn, + SignerFn: fakeSigner, + ConnectionMonitor: fakeMonitor{}, + Logger: utils.NewLoggerForTests(), + LocalAddr: &utils.NetAddr{}, + authzContextFn: func(info credentials.AuthInfo) (*authz.Context, error) { + checker, err := test.checkerFn(info) + if err != nil { + return nil, trace.Wrap(err) + } + + return &authz.Context{Checker: checker}, nil + }, }) stream, err := srv.Client.ProxySSH(context.Background()) @@ -461,7 +492,11 @@ func TestService_ProxySSH(t *testing.T) { // create a server that will open a new connection to the // ssh server created above on each dial request srv := newServer(t, ServerConfig{ - Dialer: sshSrv, + Dialer: sshSrv, + SignerFn: fakeSigner, + Logger: utils.NewLoggerForTests(), + LocalAddr: &utils.NetAddr{}, + ConnectionMonitor: fakeMonitor{}, agentGetterFn: func(rw io.ReadWriter) teleagent.Getter { return func() (teleagent.Agent, error) { srw, ok := rw.(*streamutils.ReadWriter) @@ -474,8 +509,8 @@ func TestService_ProxySSH(t *testing.T) { }, nil } }, - accessCheckerFn: func(info credentials.AuthInfo) (services.AccessChecker, error) { - return fakeChecker{}, nil + authzContextFn: func(info credentials.AuthInfo) (*authz.Context, error) { + return &authz.Context{Checker: fakeChecker{}}, nil }, }) @@ -619,7 +654,7 @@ type sshServer struct { } // DialSite returns a connection to the sshServer -func (s *sshServer) DialSite(context.Context, string) (net.Conn, error) { +func (s *sshServer) DialSite(ctx context.Context, clusterName string, clientSrcAddr, clientDstAddr net.Addr) (net.Conn, error) { conn, err := s.dial() if err != nil { return nil, trace.Wrap(err) @@ -632,31 +667,31 @@ func (s *sshServer) DialSite(context.Context, string) (net.Conn, error) { // nil and is of type testAgent, then the server will serve its keyring // over the underlying [streamutils.ReadWriter] so that tests can exercise // ssh agent multiplexing. -func (s *sshServer) DialHost(ctx context.Context, from net.Addr, host, port, clusterName string, accessChecker services.AccessChecker, agentGetter teleagent.Getter) (net.Conn, error) { +func (s *sshServer) DialHost(ctx context.Context, clientSrcAddr, clientDstAddr net.Addr, host, port, cluster string, checker services.AccessChecker, agentGetter teleagent.Getter, singer func(context.Context) (ssh.Signer, error)) (_ net.Conn, teleportVersion string, err error) { conn, err := s.dial() if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } if agentGetter == nil { - return conn, nil + return conn, "", nil } agnt, err := agentGetter() if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } rw, ok := agnt.(testAgent) if !ok { - return conn, nil + return conn, "", nil } go func() { agent.ServeAgent(s.keyring, rw) }() - return conn, nil + return conn, "", nil } func (s *sshServer) Run() { diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 3dcdb0d26fde2..20c4517a2f7c2 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -545,12 +545,6 @@ func (h *Handler) bindDefaultEndpoints() { // App sessions h.POST("/webapi/sessions/app", h.WithAuth(h.createAppSession)) - // DELETE IN 13, deprecated/unused web sessions routes (avatus) - // https://github.com/gravitational/teleport/pull/19892 - h.POST("/webapi/sessions", httplib.WithCSRFProtection(h.WithLimiterHandlerFunc(h.createWebSession))) - h.DELETE("/webapi/sessions", h.WithAuth(h.deleteWebSession)) - h.POST("/webapi/sessions/renew", h.WithAuth(h.renewWebSession)) - // Web sessions h.POST("/webapi/sessions/web", httplib.WithCSRFProtection(h.WithLimiterHandlerFunc(h.createWebSession))) h.DELETE("/webapi/sessions/web", h.WithAuth(h.deleteWebSession)) diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index bcdba808962a4..5ce2895145e68 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -6741,6 +6741,10 @@ func (mock authProviderMock) GenerateUserSingleUseCerts(ctx context.Context) (au return nil, nil } +func (mock authProviderMock) GenerateOpenSSHCert(ctx context.Context, req *authproto.OpenSSHCertRequest) (*authproto.OpenSSHCert, error) { + return nil, nil +} + type terminalOpt func(t *TerminalRequest) func withSessionID(sid session.ID) terminalOpt { diff --git a/lib/web/terminal.go b/lib/web/terminal.go index 95896741e81ce..47874695870e5 100644 --- a/lib/web/terminal.go +++ b/lib/web/terminal.go @@ -95,6 +95,7 @@ type AuthProvider interface { GetSessionTracker(ctx context.Context, sessionID string) (types.SessionTracker, error) IsMFARequired(ctx context.Context, req *authproto.IsMFARequiredRequest) (*authproto.IsMFARequiredResponse, error) GenerateUserSingleUseCerts(ctx context.Context) (authproto.AuthService_GenerateUserSingleUseCertsClient, error) + GenerateOpenSSHCert(ctx context.Context, req *authproto.OpenSSHCertRequest) (*authproto.OpenSSHCert, error) } // NewTerminal creates a web-based terminal based on WebSockets and returns a @@ -633,16 +634,15 @@ func (t *TerminalHandler) streamTerminal(ws *websocket.Conn, tc *client.Teleport getAgent := func() (teleagent.Agent, error) { return teleagent.NopCloser(tc.LocalAgent()), nil } - signerCreator := func() (ssh.Signer, error) { - cert, err := t.ctx.GetSSHCertificate() - if err != nil { - return nil, trace.Wrap(err) - } - validBefore := time.Unix(int64(cert.ValidBefore), 0) - ttl := time.Until(validBefore) - return agentless.CreateAuthSigner(t.terminalContext, t.ctx.GetUser(), tc.SiteName, ttl, t.router) + cert, err := t.ctx.GetSSHCertificate() + if err != nil { + t.log.WithError(err).Warn("Unable to stream terminal - failed to get certificate") + t.writeError(err) + return } - conn, _, err := t.router.DialHost(ctx, ws.RemoteAddr(), ws.LocalAddr(), t.sessionData.ServerID, strconv.Itoa(t.sessionData.ServerHostPort), tc.SiteName, accessChecker, getAgent, signerCreator) + + signer := agentless.SignerFromSSHCertificate(cert, t.authProvider) + conn, _, err := t.router.DialHost(ctx, ws.RemoteAddr(), ws.LocalAddr(), t.sessionData.ServerID, strconv.Itoa(t.sessionData.ServerHostPort), tc.SiteName, accessChecker, getAgent, signer) if err != nil { t.log.WithError(err).Warn("Unable to stream terminal - failed to dial host.") @@ -715,7 +715,7 @@ func (t *TerminalHandler) streamTerminal(ws *websocket.Conn, tc *client.Teleport sshConfig.Auth = tc.AuthMethods // connect to the node again with the new certs - conn, _, err = t.router.DialHost(ctx, ws.RemoteAddr(), ws.LocalAddr(), t.sessionData.ServerID, strconv.Itoa(t.sessionData.ServerHostPort), tc.SiteName, accessChecker, getAgent, signerCreator) + conn, _, err = t.router.DialHost(ctx, ws.RemoteAddr(), ws.LocalAddr(), t.sessionData.ServerID, strconv.Itoa(t.sessionData.ServerHostPort), tc.SiteName, accessChecker, getAgent, signer) if err != nil { t.log.WithError(err).Warn("Unable to stream terminal - failed to dial host") t.writeError(err) diff --git a/proto/prehog/v1alpha/connect.proto b/proto/prehog/v1alpha/connect.proto index df4c16b392ac2..835432afafa1e 100644 --- a/proto/prehog/v1alpha/connect.proto +++ b/proto/prehog/v1alpha/connect.proto @@ -40,6 +40,8 @@ message ConnectProtocolUseEvent { string user_name = 2; // one of ssh/db/kube string protocol = 3; + // one of resource_table/search_bar/connection_list/reopened_session (optional) + string origin = 4; } message ConnectAccessRequestCreateEvent { diff --git a/rfd/0000-rfds.md b/rfd/0000-rfds.md index 29a1e2d0965a0..9f2ec6d710adb 100644 --- a/rfd/0000-rfds.md +++ b/rfd/0000-rfds.md @@ -117,7 +117,7 @@ something like the following. ``` # Required Approvers * Engineering @zmb3 && (@codingllama || @nklaassen ) -* Security @reed +* Security: (@reedloden || @jentfoo) * Product: (@xinding33 || @klizhentas ) ``` diff --git a/tool/tsh/tsh.go b/tool/tsh/tsh.go index ee40964677379..7bf8917fc490b 100644 --- a/tool/tsh/tsh.go +++ b/tool/tsh/tsh.go @@ -863,17 +863,30 @@ func Run(ctx context.Context, args []string, opts ...cliOption) error { // bench bench := app.Command("bench", "Run shell or execute a command on a remote SSH node").Hidden() bench.Flag("cluster", clusterHelp).Short('c').StringVar(&cf.SiteName) - bench.Arg("[user@]host", "Remote hostname and the login to use").Required().StringVar(&cf.UserHost) - bench.Arg("command", "Command to execute on a remote host").Required().StringsVar(&cf.RemoteCommand) - bench.Flag("port", "SSH port on a remote host").Short('p').Int32Var(&cf.NodePort) bench.Flag("duration", "Test duration").Default("1s").DurationVar(&cf.BenchDuration) bench.Flag("rate", "Requests per second rate").Default("10").IntVar(&cf.BenchRate) - bench.Flag("interactive", "Create interactive SSH session").BoolVar(&cf.BenchInteractive) bench.Flag("export", "Export the latency profile").BoolVar(&cf.BenchExport) bench.Flag("path", "Directory to save the latency profile to, default path is the current directory").Default(".").StringVar(&cf.BenchExportPath) bench.Flag("ticks", "Ticks per half distance").Default("100").Int32Var(&cf.BenchTicks) bench.Flag("scale", "Value scale in which to scale the recorded values").Default("1.0").Float64Var(&cf.BenchValueScale) + benchSSH := bench.Command("ssh", "Run SSH benchmark test") + benchSSH.Arg("[user@]host", "Remote hostname and the login to use").Required().StringVar(&cf.UserHost) + benchSSH.Arg("command", "Command to execute on a remote host").Required().StringsVar(&cf.RemoteCommand) + benchSSH.Flag("port", "SSH port on a remote host").Short('p').Int32Var(&cf.NodePort) + benchSSH.Flag("interactive", "Create interactive SSH session").BoolVar(&cf.BenchInteractive) + var benchKubeOpts benchKubeOptions + benchKube := bench.Command("kube", "Run Kube benchmark test") + benchKube.Flag("kube-namespace", "Selects the ").Default("default").StringVar(&benchKubeOpts.namespace) + benchListKube := benchKube.Command("ls", "Run a benchmark test to list Pods") + benchListKube.Arg("kube_cluster", "Kubernetes cluster to use").Required().StringVar(&cf.KubernetesCluster) + benchExecKube := benchKube.Command("exec", "Run a benchmark test to exec into the specified Pod") + benchExecKube.Arg("kube_cluster", "Kubernetes cluster to use").Required().StringVar(&cf.KubernetesCluster) + benchExecKube.Arg("pod", "Pod name to exec into").Required().StringVar(&benchKubeOpts.pod) + benchExecKube.Arg("command", "Command to execute on a pod").Required().StringsVar(&cf.RemoteCommand) + benchExecKube.Flag("container", "Selects the container to exec into.").StringVar(&benchKubeOpts.container) + benchExecKube.Flag("interactive", "Create interactive Kube session").BoolVar(&cf.BenchInteractive) + // show key show := app.Command("show", "Read an identity from file and print to stdout").Hidden() show.Arg("identity_file", "The file containing a public key or a certificate").Required().StringVar(&cf.IdentityFileIn) @@ -1129,8 +1142,31 @@ func Run(ctx context.Context, args []string, opts ...cliOption) error { err = onVersion(&cf) case ssh.FullCommand(): err = onSSH(&cf) - case bench.FullCommand(): - err = onBenchmark(&cf) + case benchSSH.FullCommand(): + err = onBenchmark( + &cf, + &benchmark.SSHBenchmark{ + Command: cf.RemoteCommand, + }, + ) + case benchListKube.FullCommand(): + err = onBenchmark( + &cf, + &benchmark.KubeListBenchmark{ + Namespace: benchKubeOpts.namespace, + }, + ) + case benchExecKube.FullCommand(): + err = onBenchmark( + &cf, + &benchmark.KubeExecBenchmark{ + Command: cf.RemoteCommand, + Namespace: benchKubeOpts.namespace, + PodName: benchKubeOpts.pod, + ContainerName: benchKubeOpts.container, + Interactive: cf.BenchInteractive, + }, + ) case join.FullCommand(): err = onJoin(&cf) case scp.FullCommand(): @@ -1404,6 +1440,12 @@ func fetchProxyVersion(cf *CLIConf) (string, string, error) { return pingRes.ServerVersion, pingRes.Proxy.SSH.PublicAddr, nil } +type benchKubeOptions struct { + pod string + container string + namespace string +} + func serializeVersion(format string, proxyVersion string, proxyPublicAddress string) (string, error) { versionInfo := struct { Version string `json:"version"` @@ -3084,17 +3126,16 @@ func onSSH(cf *CLIConf) error { } // onBenchmark executes benchmark -func onBenchmark(cf *CLIConf) error { +func onBenchmark(cf *CLIConf, suite benchmark.BenchmarkSuite) error { tc, err := makeClient(cf, false) if err != nil { return trace.Wrap(err) } cnf := benchmark.Config{ - Command: cf.RemoteCommand, MinimumWindow: cf.BenchDuration, Rate: cf.BenchRate, } - result, err := cnf.Benchmark(cf.Context, tc) + result, err := cnf.Benchmark(cf.Context, tc, suite) if err != nil { fmt.Fprintln(os.Stderr, utils.UserMessageFromError(err)) return trace.Wrap(&common.ExitCodeError{Code: 255}) diff --git a/web/packages/design/src/Icon/Icon.jsx b/web/packages/design/src/Icon/Icon.jsx index 149cffbfd4553..db42a3f4c6c3f 100644 --- a/web/packages/design/src/Icon/Icon.jsx +++ b/web/packages/design/src/Icon/Icon.jsx @@ -16,13 +16,13 @@ limitations under the License. import React from 'react'; import styled from 'styled-components'; -import { space, fontSize, width, color } from 'styled-system'; +import { space, fontSize, width, color, lineHeight } from 'styled-system'; import '../assets/icomoon/style.css'; const Icon = styled.span` display: inline-block; transition: color 0.3s; - ${space} ${width} ${color} ${fontSize} + ${space} ${width} ${color} ${fontSize} ${lineHeight} `; Icon.displayName = `Icon`; diff --git a/web/packages/shared/components/Highlight/Highlight.story.tsx b/web/packages/shared/components/Highlight/Highlight.story.tsx new file mode 100644 index 0000000000000..7b66967d04c53 --- /dev/null +++ b/web/packages/shared/components/Highlight/Highlight.story.tsx @@ -0,0 +1,79 @@ +/** + * 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. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { Flex, Text } from 'design'; + +import { Highlight } from './Highlight'; + +export default { + title: 'Shared/Highlight', +}; + +export const Story = () => { + const keywords = [ + 'Aliquam', + 'olor', + // Overlapping matches: 'lor' and 'rem' both match 'lorem', so the whole word should get + // highlighted. + 'lor', + 'rem', + // https://www.contentful.com/blog/unicode-javascript-and-the-emoji-family/ + // Unfortunately, the library we use for highlighting seems to match only the first emoji of + // such group, e.g. searching for the emoji of a son won't match a group in which the son is + // present. + '👩', + '👨‍👨‍👦‍👦', + '🥑', + ]; + return ( + + + + Highlighting {keywords.join(', ')} in the below text: + + + + + + + + Custom highlighting + + + + + + + + ); +}; + +const loremIpsum = + 'Lorem ipsum 👩‍👩‍👧‍👦 dolor sit amet, 👨‍👨‍👦‍👦 consectetur adipiscing elit. 🥑 Aliquam vel augue varius, venenatis velit sit amet, aliquam arcu. Morbi dictum mattis ultrices. Nullam ut porta ipsum, porta ornare nibh. Vivamus magna felis, semper sed enim sit amet, varius rhoncus leo. Aenean ornare convallis sem ut accumsan.'; + +const CustomHighlight = styled.div` + mark { + background-color: magenta; + } +`; diff --git a/web/packages/shared/components/Highlight/Highlight.tsx b/web/packages/shared/components/Highlight/Highlight.tsx new file mode 100644 index 0000000000000..d7fde1b33ed51 --- /dev/null +++ b/web/packages/shared/components/Highlight/Highlight.tsx @@ -0,0 +1,50 @@ +/** + * 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. + */ + +import React from 'react'; +import { findAll } from 'highlight-words-core'; + +/** + * Highlight wraps the keywords found in the text in tags. + * + * It is a simplified version of the component provided by the react-highlight-words package. + * It can be extended with the features provided by highlight-words-core (e.g. case sensitivity). + * + * It doesn't handle Unicode super well because highlight-words-core uses a regex with the i flag + * underneath. This means that the component will not always ignore differences in case, for example + * when matching a string with the Turkish İ. + */ +export function Highlight(props: { text: string; keywords: string[] }) { + const chunks = findAll({ + textToHighlight: props.text, + searchWords: props.keywords, + }); + + return ( + <> + {chunks.map((chunk, index) => { + const { end, highlight, start } = chunk; + const chunkText = props.text.substr(start, end - start); + + if (highlight) { + return {chunkText}; + } else { + return chunkText; + } + })} + + ); +} diff --git a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/index.ts b/web/packages/shared/components/Highlight/index.ts similarity index 92% rename from web/packages/teleterm/src/ui/TopBar/NavigationMenu/index.ts rename to web/packages/shared/components/Highlight/index.ts index 54c74cd460de1..0ca17dc47f87d 100644 --- a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/index.ts +++ b/web/packages/shared/components/Highlight/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { NavigationMenu } from './NavigationMenu'; +export { Highlight } from './Highlight'; diff --git a/web/packages/shared/hooks/useAsync.ts b/web/packages/shared/hooks/useAsync.ts index 6f4919348a9c5..1536dba4e0ec2 100644 --- a/web/packages/shared/hooks/useAsync.ts +++ b/web/packages/shared/hooks/useAsync.ts @@ -188,3 +188,23 @@ export function makeErrorAttempt(statusText: string): Attempt { statusText, }; } + +/** + * mapAttempt maps attempt data but only if the attempt is successful. + */ +export function mapAttempt( + attempt: Attempt, + mapFunction: (attemptData: A) => B +): Attempt { + if (attempt.status !== 'success') { + return { + ...attempt, + data: null, + }; + } + + return { + ...attempt, + data: mapFunction(attempt.data), + }; +} diff --git a/web/packages/shared/package.json b/web/packages/shared/package.json index d2cb17790d977..5492f31a1d738 100644 --- a/web/packages/shared/package.json +++ b/web/packages/shared/package.json @@ -14,6 +14,7 @@ "create-react-class": "^15.6.3", "cross-env": "5.0.5", "date-fns": "^2.28.0", + "highlight-words-core": "^1.2.2", "react": "^16.8.4", "react-day-picker": "7.3.2", "react-dom": "^16.8.4", diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.story.tsx b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.story.tsx index a9e254d05a58d..386ed74130787 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.story.tsx +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.story.tsx @@ -35,13 +35,13 @@ export const InitSelfHostedPostgres = () => ( export const InitSelfHostedMySql = () => ( - + ); export const InitAws = () => ( - + ); @@ -76,7 +76,7 @@ const props: State = { registerDatabase: () => null, canCreateDatabase: true, pollTimeout: Date.now() + 30000, - dbEngine: DatabaseEngine.PostgreSQL, + dbEngine: DatabaseEngine.Postgres, dbLocation: DatabaseLocation.SelfHosted, isDbCreateErr: false, prevStep: () => null, diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.tsx b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.tsx index 2ded5e6b2a81a..f07dd5f2869ff 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.tsx +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabase.tsx @@ -103,7 +103,7 @@ export function CreateDatabaseView({ } let awsRds: AwsRds; - if (dbLocation === DatabaseLocation.AWS) { + if (dbLocation === DatabaseLocation.Aws) { awsRds = { accountId: awsAccountId, resourceId: awsResourceId, @@ -119,7 +119,7 @@ export function CreateDatabaseView({ }); } - const isAws = dbLocation === DatabaseLocation.AWS; + const isAws = dbLocation === DatabaseLocation.Aws; return ( {({ validator }) => ( @@ -204,7 +204,7 @@ export function CreateDatabaseView({ width="30%" /> - {dbLocation === DatabaseLocation.AWS && ( + {dbLocation === DatabaseLocation.Aws && ( <> ( export const LoadedMongo = () => ( - + ); export const LoadedSqlMaria = () => ( - + ); @@ -74,5 +74,5 @@ const props: State = { onNextStep: () => null, canUpdateDatabase: true, curlCmd: `some curl command`, - dbEngine: DatabaseEngine.PostgreSQL, + dbEngine: DatabaseEngine.Postgres, }; diff --git a/web/packages/teleport/src/Discover/Database/MutualTls/MutualTls.tsx b/web/packages/teleport/src/Discover/Database/MutualTls/MutualTls.tsx index e6502a20e86cf..6e56433c74704 100644 --- a/web/packages/teleport/src/Discover/Database/MutualTls/MutualTls.tsx +++ b/web/packages/teleport/src/Discover/Database/MutualTls/MutualTls.tsx @@ -124,7 +124,7 @@ export function MutualTlsView({ } function DbEngineInstructions({ dbEngine }: { dbEngine: DatabaseEngine }) { - if (dbEngine === DatabaseEngine.PostgreSQL) { + if (dbEngine === DatabaseEngine.Postgres) { return ( @@ -154,7 +154,7 @@ function DbEngineInstructions({ dbEngine }: { dbEngine: DatabaseEngine }) { ); } - if (dbEngine === DatabaseEngine.Mongo) { + if (dbEngine === DatabaseEngine.MongoDb) { return ( @@ -212,7 +212,7 @@ function DbEngineInstructions({ dbEngine }: { dbEngine: DatabaseEngine }) { ); } - if (dbEngine === DatabaseEngine.MySQL) { + if (dbEngine === DatabaseEngine.MySql) { return ( diff --git a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.story.tsx b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.story.tsx index 2ae9b2ba26b07..00de37186ff9a 100644 --- a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.story.tsx +++ b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.story.tsx @@ -39,7 +39,7 @@ export const WithTraitsAwsPostgres = () => ( ); @@ -48,7 +48,7 @@ export const WithTraitsAwsMySql = () => ( ); @@ -61,13 +61,13 @@ export const WithTraitsPostgres = () => ( export const WithTraitsMongo = () => ( - + ); export const WithTraitsMySql = () => ( - + ); @@ -98,10 +98,7 @@ const props: State = { initSelectedOptionsHelper({ trait, staticTraits, dynamicTraits }), dynamicTraits: {} as any, staticTraits: {} as any, - resourceSpec: getDbMeta( - DatabaseEngine.PostgreSQL, - DatabaseLocation.SelfHosted - ), + resourceSpec: getDbMeta(DatabaseEngine.Postgres, DatabaseLocation.SelfHosted), }; const staticTraits = { diff --git a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx index ec6fea07d5a5c..f7b596e750e2c 100644 --- a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx +++ b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx @@ -97,7 +97,7 @@ export function SetupAccess(props: State) { const { engine, location } = resourceSpec.dbMeta; let hasTraits = selectedUsers.length > 0; // Postgres connection testing requires both db user and a db name. - if (engine === DatabaseEngine.PostgreSQL) { + if (engine === DatabaseEngine.Postgres) { hasTraits = hasTraits && selectedNames.length > 0; } @@ -199,8 +199,8 @@ function DbEngineInstructions({ dbLocation: DatabaseLocation; }) { switch (dbLocation) { - case DatabaseLocation.AWS: - if (dbEngine === DatabaseEngine.PostgreSQL) { + case DatabaseLocation.Aws: + if (dbEngine === DatabaseEngine.Postgres) { return ( @@ -227,7 +227,7 @@ function DbEngineInstructions({ ); } - if (dbEngine === DatabaseEngine.MySQL) { + if (dbEngine === DatabaseEngine.MySql) { return ( @@ -272,7 +272,7 @@ function DbEngineInstructions({ // self-hosted databases default: - if (dbEngine === DatabaseEngine.PostgreSQL) { + if (dbEngine === DatabaseEngine.Postgres) { return ( @@ -305,7 +305,7 @@ function DbEngineInstructions({ ); } - if (dbEngine === DatabaseEngine.Mongo) { + if (dbEngine === DatabaseEngine.MongoDb) { return ( @@ -333,7 +333,7 @@ function DbEngineInstructions({ ); } - if (dbEngine === DatabaseEngine.MySQL) { + if (dbEngine === DatabaseEngine.MySql) { return ( diff --git a/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.story.tsx b/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.story.tsx index 5275a6cd802e9..3a91dae5ea15a 100644 --- a/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.story.tsx +++ b/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.story.tsx @@ -35,7 +35,7 @@ export const InitMySql = () => ( export const InitPostgres = () => ( - + ); @@ -62,7 +62,7 @@ const props: State = { users: ['user1', 'user2'], hostname: 'db-hostname', }, - dbEngine: DatabaseEngine.MySQL, + dbEngine: DatabaseEngine.MySql, showMfaDialog: false, cancelMfaDialog: () => null, }; diff --git a/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.tsx b/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.tsx index 754a96d3f426c..17ae07ea8cbf4 100644 --- a/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.tsx +++ b/web/packages/teleport/src/Discover/Database/TestConnection/TestConnection.tsx @@ -129,7 +129,7 @@ export function TestConnectionView({ attempt.status === 'processing' || nameOpts.length === 0 } // Database name is required for Postgres. - isClearable={dbEngine !== DatabaseEngine.PostgreSQL} + isClearable={dbEngine !== DatabaseEngine.Postgres} /> diff --git a/web/packages/teleport/src/Discover/Database/index.tsx b/web/packages/teleport/src/Discover/Database/index.tsx index 3e84683537795..b811f6754afb8 100644 --- a/web/packages/teleport/src/Discover/Database/index.tsx +++ b/web/packages/teleport/src/Discover/Database/index.tsx @@ -41,7 +41,7 @@ export const DatabaseResource: ResourceViewConfig = { let configureResourceViews; if (resource && resource.dbMeta) { switch (resource.dbMeta.location) { - case DatabaseLocation.AWS: + case DatabaseLocation.Aws: configureResourceViews = [ { title: 'Register a Database', diff --git a/web/packages/teleport/src/Discover/Discover.test.tsx b/web/packages/teleport/src/Discover/Discover.test.tsx index 9af95f1623bc4..5955328724a2b 100644 --- a/web/packages/teleport/src/Discover/Discover.test.tsx +++ b/web/packages/teleport/src/Discover/Discover.test.tsx @@ -36,6 +36,7 @@ import { import { DATABASES, DATABASES_UNGUIDED, + DATABASES_UNGUIDED_DOC, } from 'teleport/Discover/SelectResource/databases'; import { ResourceKind } from './Shared'; @@ -103,7 +104,9 @@ describe('discover', () => { create('database', getAcl()); expect(screen.getAllByTestId(ResourceKind.Database)).toHaveLength( - DATABASES.length + DATABASES_UNGUIDED.length + DATABASES.length + + DATABASES_UNGUIDED.length + + DATABASES_UNGUIDED_DOC.length ); }); }); diff --git a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx index fc58a29a7ad28..42d1d22882a29 100644 --- a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx +++ b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx @@ -15,7 +15,7 @@ */ import React, { useState } from 'react'; -import { useLocation } from 'react-router'; +import { useLocation, useHistory } from 'react-router'; import * as Icons from 'design/Icon'; import styled from 'styled-components'; @@ -44,6 +44,7 @@ interface SelectResourceProps { export function SelectResource(props: SelectResourceProps) { const ctx = useTeleport(); const location = useLocation<{ entity: AddButtonResourceKind }>(); + const history = useHistory(); const [search, setSearch] = useState(''); const [resources, setResources] = useState([]); @@ -62,6 +63,11 @@ export function SelectResource(props: SelectResourceProps) { setSearch(s); } + function onClearSearch() { + history.replace({ state: {} }); // Clear any loc state. + onSearch(''); + } + React.useEffect(() => { // Apply access check to each resource. const userContext = ctx.storeUser.state; @@ -115,7 +121,7 @@ export function SelectResource(props: SelectResourceProps) { max={100} /> - {search && onSearch('')} />} + {search && } {resources.length > 0 && ( <> @@ -123,8 +129,6 @@ export function SelectResource(props: SelectResourceProps) { {resources.map((r, index) => { const title = r.name; const pretitle = getResourcePretitle(r); - const selectResourceFn = - r.unguidedLink || !r.hasAccess ? null : () => props.onSelect(r); // There can be two types of click behavior with the resource cards: // 1) If the resource has no interactive UI flow ("unguided"), @@ -140,7 +144,7 @@ export function SelectResource(props: SelectResourceProps) { as={r.unguidedLink ? Link : null} href={r.hasAccess ? r.unguidedLink : null} target={r.unguidedLink ? '_blank' : null} - onClick={selectResourceFn} + onClick={() => r.hasAccess && props.onSelect(r)} className={r.unguidedLink ? 'unguided' : ''} > {!r.unguidedLink && r.hasAccess && ( diff --git a/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap b/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap index cd9de7347542d..344d1f59323c5 100644 --- a/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap +++ b/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap @@ -567,6 +567,46 @@ exports[`render with URL loc state set to "server" 1`] = ` + +
+
+ +
+
+
+ Microsoft +
+
+ SQL Server (Preview) +
+
+
+
@@ -1423,10 +1463,10 @@ exports[`render with all access 1`] = ` width="24px" >
- RDS & Aurora + Redshift PostgreSQL
@@ -1451,7 +1491,7 @@ exports[`render with all access 1`] = ` @@ -1463,10 +1503,10 @@ exports[`render with all access 1`] = ` width="24px" >
- RDS Proxy + Redshift Serverless
@@ -1491,7 +1531,7 @@ exports[`render with all access 1`] = `
@@ -1517,13 +1557,13 @@ exports[`render with all access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift PostgreSQL + Cache for Redis
@@ -1531,7 +1571,7 @@ exports[`render with all access 1`] = `
@@ -1557,13 +1597,13 @@ exports[`render with all access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift Serverless + PostgreSQL
@@ -1571,7 +1611,7 @@ exports[`render with all access 1`] = `
@@ -1603,7 +1643,7 @@ exports[`render with all access 1`] = ` class="c13" color="white" > - Cache for Redis + MySQL @@ -1611,7 +1651,7 @@ exports[`render with all access 1`] = ` @@ -1643,7 +1683,7 @@ exports[`render with all access 1`] = ` class="c13" color="white" > - PostgreSQL & MySQL + SQL Server (Preview) @@ -1651,7 +1691,7 @@ exports[`render with all access 1`] = ` @@ -1677,7 +1717,7 @@ exports[`render with all access 1`] = ` color="#a8afb2" font-size="12px" > - Azure + Microsoft
+ +
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ RDS Proxy +
+
+
+
+ +
+
+ +
+
+
+ Database +
+
+ High Availability +
+
+
+
+ +
+
+ +
+
+
+ Database +
+
+ Dynamic Registration +
+
+
+
- RDS & Aurora + Redshift PostgreSQL
@@ -2996,10 +3156,10 @@ exports[`render with no access 1`] = ` width="24px" >
- RDS Proxy + Redshift Serverless
@@ -3054,13 +3214,13 @@ exports[`render with no access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift PostgreSQL + Cache for Redis
@@ -3098,13 +3258,13 @@ exports[`render with no access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift Serverless + PostgreSQL
@@ -3148,7 +3308,7 @@ exports[`render with no access 1`] = ` class="c14" color="white" > - Cache for Redis + MySQL @@ -3192,7 +3352,7 @@ exports[`render with no access 1`] = ` class="c14" color="white" > - PostgreSQL & MySQL + SQL Server (Preview) @@ -3230,7 +3390,7 @@ exports[`render with no access 1`] = ` color="#a8afb2" font-size="12px" > - Azure + Microsoft
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ RDS Proxy +
+
+
+
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Database +
+
+ High Availability +
+
+
+
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Database +
+
+ Dynamic Registration +
+
+
+
- RDS & Aurora + Redshift PostgreSQL
@@ -4618,10 +4910,10 @@ exports[`render with partial access 1`] = ` width="24px" >
- RDS Proxy + Redshift Serverless
@@ -4676,13 +4968,13 @@ exports[`render with partial access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift PostgreSQL + Cache for Redis
@@ -4720,13 +5012,13 @@ exports[`render with partial access 1`] = ` color="#a8afb2" font-size="12px" > - Amazon Web Services (AWS) + Azure
- Redshift Serverless + PostgreSQL
@@ -4770,7 +5062,7 @@ exports[`render with partial access 1`] = ` class="c13" color="white" > - Cache for Redis + MySQL @@ -4814,7 +5106,7 @@ exports[`render with partial access 1`] = ` class="c13" color="white" > - PostgreSQL & MySQL + SQL Server (Preview) @@ -4852,7 +5144,7 @@ exports[`render with partial access 1`] = ` color="#a8afb2" font-size="12px" > - Azure + Microsoft
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ RDS Proxy +
+
+
+
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Database +
+
+ High Availability +
+
+
+
+ +
+ Lacking Permissions +
+
+
+ +
+
+
+ Database +
+
+ Dynamic Registration +
+
+
+
, Application: , + Database: , Aws: , Azure: , Cockroach: , diff --git a/web/packages/teleport/src/Discover/SelectResource/resources.tsx b/web/packages/teleport/src/Discover/SelectResource/resources.tsx index 36dad739586d8..4236897602cdc 100644 --- a/web/packages/teleport/src/Discover/SelectResource/resources.tsx +++ b/web/packages/teleport/src/Discover/SelectResource/resources.tsx @@ -14,10 +14,16 @@ * limitations under the License. */ +import { DiscoverEventResource } from 'teleport/services/userEvent'; + import { ResourceKind } from '../Shared/ResourceKind'; -import { DATABASES, DATABASES_UNGUIDED } from './databases'; -import { ResourceSpec, DatabaseLocation } from './types'; +import { + DATABASES, + DATABASES_UNGUIDED, + DATABASES_UNGUIDED_DOC, +} from './databases'; +import { ResourceSpec, DatabaseLocation, DatabaseEngine } from './types'; import { icons } from './icons'; const baseServerKeywords = 'server node'; @@ -27,30 +33,35 @@ export const SERVERS: ResourceSpec[] = [ kind: ResourceKind.Server, keywords: baseServerKeywords + 'ubuntu', Icon: icons.Linux, + event: DiscoverEventResource.Server, }, { name: 'Debian 8+', kind: ResourceKind.Server, keywords: baseServerKeywords + 'debian', Icon: icons.Linux, + event: DiscoverEventResource.Server, }, { name: 'RHEL/CentOS 7+', kind: ResourceKind.Server, keywords: baseServerKeywords + 'rhel centos', Icon: icons.Linux, + event: DiscoverEventResource.Server, }, { name: 'Amazon Linux 2', kind: ResourceKind.Server, keywords: baseServerKeywords + 'amazon linux', Icon: icons.Aws, + event: DiscoverEventResource.Server, }, { name: 'macOS (Intel)', kind: ResourceKind.Server, keywords: baseServerKeywords + 'mac macos intel', Icon: icons.Apple, + event: DiscoverEventResource.Server, }, ]; @@ -62,6 +73,7 @@ export const APPLICATIONS: ResourceSpec[] = [ Icon: icons.Application, unguidedLink: 'https://goteleport.com/docs/application-access/getting-started/', + event: DiscoverEventResource.ApplicationHttp, }, ]; @@ -71,6 +83,7 @@ export const WINDOWS_DESKTOPS: ResourceSpec[] = [ kind: ResourceKind.Desktop, keywords: 'windows desktop active directory ad', Icon: icons.Windows, + event: DiscoverEventResource.WindowsDesktop, }, // { // name: 'Non Active Directory', @@ -87,6 +100,7 @@ export const KUBERNETES: ResourceSpec[] = [ kind: ResourceKind.Kubernetes, keywords: 'kubernetes cluster kubes', Icon: icons.Kube, + event: DiscoverEventResource.Kubernetes, }, ]; @@ -97,6 +111,7 @@ export const RESOURCES: ResourceSpec[] = [ ...SERVERS, ...DATABASES, ...DATABASES_UNGUIDED, + ...DATABASES_UNGUIDED_DOC, ]; export function getResourcePretitle(r: ResourceSpec) { @@ -108,14 +123,20 @@ export function getResourcePretitle(r: ResourceSpec) { case ResourceKind.Database: if (r.dbMeta) { switch (r.dbMeta.location) { - case DatabaseLocation.AWS: + case DatabaseLocation.Aws: return 'Amazon Web Services (AWS)'; - case DatabaseLocation.GCP: + case DatabaseLocation.Gcp: return 'Google Cloud Provider (GCP)'; case DatabaseLocation.SelfHosted: return 'Self-Hosted'; case DatabaseLocation.Azure: return 'Azure'; + case DatabaseLocation.Microsoft: + return 'Microsoft'; + } + + if (r.dbMeta.engine === DatabaseEngine.Doc) { + return 'Database'; } } break; diff --git a/web/packages/teleport/src/Discover/SelectResource/types.ts b/web/packages/teleport/src/Discover/SelectResource/types.ts index 5d2baeae93b31..8f5aaf6b5e757 100644 --- a/web/packages/teleport/src/Discover/SelectResource/types.ts +++ b/web/packages/teleport/src/Discover/SelectResource/types.ts @@ -16,26 +16,33 @@ import { ResourceKind } from '../Shared/ResourceKind'; +import type { DiscoverEventResource } from 'teleport/services/userEvent'; + export enum DatabaseLocation { - AWS, + Aws, SelfHosted, - GCP, + Gcp, Azure, - Snowflake, - Mongo, + Microsoft, TODO, } +// DatabaseEngine represents the db "protocol". export enum DatabaseEngine { - PostgreSQL, - MySQL, - Mongo, - SQLServer, - RedShift, + Postgres, + MySql, + MongoDb, Redis, + CoackroachDb, + SqlServer, + Snowflake, + Cassandra, + ElasticSearch, + DynamoDb, + Redshift, - TODO, + Doc, } export interface ResourceSpec { @@ -54,4 +61,8 @@ export interface ResourceSpec { // It is used as a flag, that when defined, means that // this resource is not "guided" (has no UI interactive flow). unguidedLink?: string; + // event is the expected backend enum event name that describes + // the type of this resource (eg. server v. kubernetes), + // used for usage reporting. + event: DiscoverEventResource; } diff --git a/web/packages/teleport/src/Discover/Shared/ResourceKind.ts b/web/packages/teleport/src/Discover/Shared/ResourceKind.ts index 61ad34401edf4..0852bb899a7ae 100644 --- a/web/packages/teleport/src/Discover/Shared/ResourceKind.ts +++ b/web/packages/teleport/src/Discover/Shared/ResourceKind.ts @@ -14,14 +14,6 @@ * limitations under the License. */ -import { DiscoverEventResource } from 'teleport/services/userEvent'; - -import { - DatabaseEngine, - DatabaseLocation, - ResourceSpec, -} from '../SelectResource/types'; - import type { JoinRole } from 'teleport/services/joinToken'; export enum ResourceKind { @@ -46,68 +38,3 @@ export function resourceKindToJoinRole(kind: ResourceKind): JoinRole { return 'Node'; } } - -export function resourceKindToEventResource( - resourceSpec: ResourceSpec -): DiscoverEventResource { - switch (resourceSpec.kind) { - case ResourceKind.Database: - const { engine, location } = resourceSpec.dbMeta; - if (location === DatabaseLocation.AWS) { - if (engine === DatabaseEngine.PostgreSQL) { - return DiscoverEventResource.DatabasePostgresRds; - } - if (engine === DatabaseEngine.MySQL) { - return DiscoverEventResource.DatabaseMysqlRds; - } - if (engine === DatabaseEngine.SQLServer) { - return DiscoverEventResource.DatabaseSqlServerRds; - } - if (engine === DatabaseEngine.RedShift) { - return DiscoverEventResource.DatabasePostgresRedshift; - } - } - - if (location === DatabaseLocation.SelfHosted) { - if (engine === DatabaseEngine.PostgreSQL) { - return DiscoverEventResource.DatabasePostgresSelfHosted; - } - if (engine === DatabaseEngine.MySQL) { - return DiscoverEventResource.DatabaseMysqlSelfHosted; - } - if (engine === DatabaseEngine.Mongo) { - return DiscoverEventResource.DatabaseMongodbSelfHosted; - } - if (engine === DatabaseEngine.SQLServer) { - return DiscoverEventResource.DatabaseSqlServerSelfHosted; - } - if (engine === DatabaseEngine.Redis) { - return DiscoverEventResource.DatabaseRedisSelfHosted; - } - } - - if (location === DatabaseLocation.GCP) { - if (engine === DatabaseEngine.PostgreSQL) { - return DiscoverEventResource.DatabasePostgresGcp; - } - if (engine === DatabaseEngine.MySQL) { - return DiscoverEventResource.DatabaseMysqlGcp; - } - if (engine === DatabaseEngine.SQLServer) { - return DiscoverEventResource.DatabaseMysqlGcp; - } - } - console.error(`resource database event not defined for ${resourceSpec}`); - return null; - case ResourceKind.Desktop: - return DiscoverEventResource.WindowsDesktop; - case ResourceKind.Kubernetes: - return DiscoverEventResource.Kubernetes; - case ResourceKind.Server: - return DiscoverEventResource.Server; - case ResourceKind.Application: - return DiscoverEventResource.ApplicationHttp; - default: - console.error(`resource event not defined for ${resourceSpec}`); - } -} diff --git a/web/packages/teleport/src/Discover/useDiscover.test.tsx b/web/packages/teleport/src/Discover/useDiscover.test.tsx index 28780a6bf9055..a1623a10f8196 100644 --- a/web/packages/teleport/src/Discover/useDiscover.test.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.test.tsx @@ -111,7 +111,6 @@ describe('emitting events', () => { expect.objectContaining({ id: expect.any(String), currEventName: DiscoverEvent.DeployService, - eventResourceName: DiscoverEventResource.Server, }) ); @@ -162,7 +161,6 @@ describe('emitting events', () => { expect(result.current.eventState).toEqual({ id, currEventName: DiscoverEvent.PrincipalsConfigure, - eventResourceName: DiscoverEventResource.Server, }); jest.resetAllMocks(); diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx index 5bf5c5b795582..47c292acf5e9b 100644 --- a/web/packages/teleport/src/Discover/useDiscover.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.tsx @@ -32,7 +32,6 @@ import { ResourceViewConfig, View, } from './flow'; -import { resourceKindToEventResource } from './Shared/ResourceKind'; import { viewConfigs } from './resourceViewConfigs'; import type { Node } from 'teleport/services/nodes'; @@ -61,7 +60,6 @@ type EventState = { id: string; currEventName: DiscoverEvent; manuallyEmitSuccessEvent: boolean; - eventResourceName: DiscoverEventResource; }; type CustomEventInput = { @@ -87,19 +85,19 @@ export function DiscoverProvider(props: React.PropsWithChildren) { const emitEvent = useCallback( (status: DiscoverEventStepStatus, custom?: CustomEventInput) => { - const { id, currEventName, eventResourceName } = eventState; + const { id, currEventName } = eventState; userEventService.captureDiscoverEvent({ event: custom?.eventName || currEventName, eventData: { id, - resource: custom?.eventResourceName || eventResourceName, + resource: custom?.eventResourceName || resourceSpec?.event, autoDiscoverResourcesCount: custom?.autoDiscoverResourcesCount, ...status, }, }); }, - [eventState] + [eventState, resourceSpec] ); useEffect(() => { @@ -141,7 +139,6 @@ export function DiscoverProvider(props: React.PropsWithChildren) { setEventState({ id, currEventName: DiscoverEvent.Started, - eventResourceName: null, manuallyEmitSuccessEvent: null, }); userEventService.captureDiscoverEvent({ @@ -159,6 +156,20 @@ export function DiscoverProvider(props: React.PropsWithChildren) { // onSelectResources initializes all the required // variables needed to start a guided flow. function onSelectResource(resource: ResourceSpec) { + // We still want to emit an event if user clicked on + // unguided links to gather data on which unguided resource + // is most popular. + if (resource.unguidedLink) { + emitEvent( + { stepStatus: DiscoverEventStatus.Success }, + { + eventName: DiscoverEvent.ResourceSelection, + eventResourceName: resource.event, + } + ); + return; + } + // Process each view and assign each with an index number. const currCfg = viewConfigs.find(r => r.kind === resource.kind); let indexedViews = []; @@ -173,20 +184,20 @@ export function DiscoverProvider(props: React.PropsWithChildren) { indexedViews, currentStep ); - const eventResourceName = resourceKindToEventResource(resource); - // At this point it's considered the user has // successfully selected a resource, so we send an event. emitEvent( { stepStatus: DiscoverEventStatus.Success }, - { eventName: DiscoverEvent.ResourceSelection, eventResourceName } + { + eventName: DiscoverEvent.ResourceSelection, + eventResourceName: resource.event, + } ); // Init all required states to start the flow. setEventState({ ...eventState, currEventName: eventName, - eventResourceName, manuallyEmitSuccessEvent, }); setViewConfig(currCfg); diff --git a/web/packages/teleport/src/services/userEvent/types.ts b/web/packages/teleport/src/services/userEvent/types.ts index 3521b368399ba..313b4d06e0f1d 100644 --- a/web/packages/teleport/src/services/userEvent/types.ts +++ b/web/packages/teleport/src/services/userEvent/types.ts @@ -74,6 +74,28 @@ export enum DiscoverEventResource { DatabaseMysqlGcp = 'DISCOVER_RESOURCE_DATABASE_MYSQL_GCP', DatabaseSqlServerGcp = 'DISCOVER_RESOURCE_DATABASE_SQLSERVER_GCP', + DatabasePostgresRedshiftServerless = 'DISCOVER_RESOURCE_DATABASE_POSTGRES_REDSHIFT_SERVERLESS', + DatabasePostgresAzure = 'DISCOVER_RESOURCE_DATABASE_POSTGRES_AZURE', + DatabaseDynamoDb = 'DISCOVER_RESOURCE_DATABASE_DYNAMODB', + DatabaseCassandraKeyspaces = 'DISCOVER_RESOURCE_DATABASE_CASSANDRA_KEYSPACES', + DatabaseCassandraSelfHosted = 'DISCOVER_RESOURCE_DATABASE_CASSANDRA_SELF_HOSTED', + DatabaseElasticSearchSelfHosted = 'DISCOVER_RESOURCE_DATABASE_ELASTICSEARCH_SELF_HOSTED', + DatabaseRedisElasticache = 'DISCOVER_RESOURCE_DATABASE_REDIS_ELASTICACHE', + DatabaseRedisMemoryDb = 'DISCOVER_RESOURCE_DATABASE_REDIS_MEMORYDB', + DatabaseRedisAzureCache = 'DISCOVER_RESOURCE_DATABASE_REDIS_AZURE_CACHE', + DatabaseRedisClusterSelfHosted = 'DISCOVER_RESOURCE_DATABASE_REDIS_CLUSTER_SELF_HOSTED', + + DatabaseMysqlAzure = 'DISCOVER_RESOURCE_DATABASE_MYSQL_AZURE', + DatabaseSqlServerAzure = 'DISCOVER_RESOURCE_DATABASE_SQLSERVER_AZURE', + DatabaseSqlServerMicrosoft = 'DISCOVER_RESOURCE_DATABASE_SQLSERVER_MICROSOFT', + DatabaseCockroachDbSelfHosted = 'DISCOVER_RESOURCE_DATABASE_COCKROACHDB_SELF_HOSTED', + DatabaseMongodbAtlas = 'DISCOVER_RESOURCE_DATABASE_MONGODB_ATLAS', + DatabaseSnowflake = 'DISCOVER_RESOURCE_DATABASE_SNOWFLAKE', + + DatabaseDocRdsProxy = 'DISCOVER_RESOURCE_DOC_DATABASE_RDS_PROXY', + DatabaseDocHighAvailability = 'DISCOVER_RESOURCE_DOC_DATABASE_HIGH_AVAILABILITY', + DatabaseDocDynamicRegistration = 'DISCOVER_RESOURCE_DOC_DATABASE_DYNAMIC_REGISTRATION', + ApplicationHttp = 'DISCOVER_RESOURCE_APPLICATION_HTTP', ApplicationTcp = 'DISCOVER_RESOURCE_APPLICATION_TCP', WindowsDesktop = 'DISCOVER_RESOURCE_WINDOWS_DESKTOP', diff --git a/web/packages/teleterm/src/services/config/appConfigSchema.ts b/web/packages/teleterm/src/services/config/appConfigSchema.ts index aa93dbcf6a160..bb18acbe21225 100644 --- a/web/packages/teleterm/src/services/config/appConfigSchema.ts +++ b/web/packages/teleterm/src/services/config/appConfigSchema.ts @@ -35,6 +35,10 @@ export const createAppConfigSchema = (platform: Platform) => { .boolean() .default(false) .describe('Enables collecting of anonymous usage data.'), + 'feature.searchBar': z + .boolean() + .default(false) + .describe('Replaces the command bar with the new search bar'), 'keymap.tab1': shortcutSchema .default(defaultKeymap['tab1']) .describe(getShortcutDesc('open tab 1')), @@ -68,6 +72,9 @@ export const createAppConfigSchema = (platform: Platform) => { 'keymap.newTab': shortcutSchema .default(defaultKeymap['newTab']) .describe(getShortcutDesc('open a new tab')), + 'keymap.newTerminalTab': shortcutSchema + .default(defaultKeymap['newTerminalTab']) + .describe(getShortcutDesc('open a new terminal tab')), 'keymap.previousTab': shortcutSchema .default(defaultKeymap['previousTab']) .describe(getShortcutDesc('go to the previous tab')), @@ -118,6 +125,7 @@ export type KeyboardShortcutAction = | 'tab9' | 'closeTab' | 'newTab' + | 'newTerminalTab' | 'previousTab' | 'nextTab' | 'openCommandBar' @@ -125,7 +133,9 @@ export type KeyboardShortcutAction = | 'openClusters' | 'openProfiles'; -const getDefaultKeymap = (platform: Platform) => { +const getDefaultKeymap = ( + platform: Platform +): Record => { switch (platform) { case 'win32': return { @@ -140,6 +150,7 @@ const getDefaultKeymap = (platform: Platform) => { tab9: 'Ctrl+9', closeTab: 'Ctrl+W', newTab: 'Ctrl+T', + newTerminalTab: 'Ctrl+Shift+T', previousTab: 'Ctrl+Shift+Tab', nextTab: 'Ctrl+Tab', openCommandBar: 'Ctrl+K', @@ -160,6 +171,7 @@ const getDefaultKeymap = (platform: Platform) => { tab9: 'Alt+9', closeTab: 'Ctrl+W', newTab: 'Ctrl+T', + newTerminalTab: 'Ctrl+Shift+T', previousTab: 'Ctrl+Shift+Tab', nextTab: 'Ctrl+Tab', openCommandBar: 'Ctrl+K', @@ -180,6 +192,7 @@ const getDefaultKeymap = (platform: Platform) => { tab9: 'Command+9', closeTab: 'Command+W', newTab: 'Command+T', + newTerminalTab: 'Shift+Command+T', previousTab: 'Control+Shift+Tab', nextTab: 'Control+Tab', openCommandBar: 'Command+K', diff --git a/web/packages/teleterm/src/services/tshd/createClient.ts b/web/packages/teleterm/src/services/tshd/createClient.ts index 7541485a23276..cf2b36eb15e73 100644 --- a/web/packages/teleterm/src/services/tshd/createClient.ts +++ b/web/packages/teleterm/src/services/tshd/createClient.ts @@ -61,20 +61,24 @@ export default function createClient( async getKubes({ clusterUri, search, - sort = { fieldName: 'name', dir: 'ASC' }, + sort, query, searchAsRoles, startKey, limit, - }: types.ServerSideParams) { + }: types.GetResourcesParams) { const req = new api.GetKubesRequest() .setClusterUri(clusterUri) .setSearchAsRoles(searchAsRoles) .setStartKey(startKey) - .setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`) .setSearch(search) .setQuery(query) .setLimit(limit); + + if (sort) { + req.setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`); + } + return new Promise((resolve, reject) => { tshd.getKubes(req, (err, response) => { if (err) { @@ -128,20 +132,24 @@ export default function createClient( async getDatabases({ clusterUri, search, - sort = { fieldName: 'name', dir: 'ASC' }, + sort, query, searchAsRoles, startKey, limit, - }: types.ServerSideParams) { + }: types.GetResourcesParams) { const req = new api.GetDatabasesRequest() .setClusterUri(clusterUri) .setSearchAsRoles(searchAsRoles) .setStartKey(startKey) - .setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`) .setSearch(search) .setQuery(query) .setLimit(limit); + + if (sort) { + req.setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`); + } + return new Promise((resolve, reject) => { tshd.getDatabases(req, (err, response) => { if (err) { @@ -198,19 +206,23 @@ export default function createClient( clusterUri, search, query, - sort = { fieldName: 'hostname', dir: 'ASC' }, + sort, searchAsRoles, startKey, limit, - }: types.ServerSideParams) { + }: types.GetResourcesParams) { const req = new api.GetServersRequest() .setClusterUri(clusterUri) .setSearchAsRoles(searchAsRoles) .setStartKey(startKey) - .setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`) .setSearch(search) .setQuery(query) .setLimit(limit); + + if (sort) { + req.setSortBy(`${sort.fieldName}:${sort.dir.toLowerCase()}`); + } + return new Promise((resolve, reject) => { tshd.getServers(req, (err, response) => { if (err) { diff --git a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts index 5809b0f5edd2e..343af90cb640c 100644 --- a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts +++ b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts @@ -29,7 +29,7 @@ import { LoginPasswordlessParams, LoginSsoParams, ReviewAccessRequestParams, - ServerSideParams, + GetResourcesParams, TshAbortController, TshAbortSignal, TshClient, @@ -39,13 +39,13 @@ import { export class MockTshClient implements TshClient { listRootClusters: () => Promise; listLeafClusters: (clusterUri: string) => Promise; - getKubes: (params: ServerSideParams) => Promise; - getDatabases: (params: ServerSideParams) => Promise; + getKubes: (params: GetResourcesParams) => Promise; + getDatabases: (params: GetResourcesParams) => Promise; listDatabaseUsers: (dbUri: string) => Promise; getRequestableRoles: ( params: GetRequestableRolesParams ) => Promise; - getServers: (params: ServerSideParams) => Promise; + getServers: (params: GetResourcesParams) => Promise; assumeRole: ( clusterUri: string, requestIds: string[], diff --git a/web/packages/teleterm/src/services/tshd/mapUsageEvent.ts b/web/packages/teleterm/src/services/tshd/mapUsageEvent.ts index 269e97caa928a..c23c1a9ad7f3a 100644 --- a/web/packages/teleterm/src/services/tshd/mapUsageEvent.ts +++ b/web/packages/teleterm/src/services/tshd/mapUsageEvent.ts @@ -65,7 +65,8 @@ function mapPrehogBody( const reqEvent = new prehogApi.ConnectProtocolUseEvent() .setClusterName(event.clusterName) .setUserName(event.userName) - .setProtocol(event.protocol); + .setProtocol(event.protocol) + .setOrigin(event.origin); return req.setProtocolUse(reqEvent); } diff --git a/web/packages/teleterm/src/services/tshd/types.ts b/web/packages/teleterm/src/services/tshd/types.ts index 043b31f40b622..2cbce08b2bc3c 100644 --- a/web/packages/teleterm/src/services/tshd/types.ts +++ b/web/packages/teleterm/src/services/tshd/types.ts @@ -26,6 +26,7 @@ import apiDb from 'gen-proto-js/teleport/lib/teleterm/v1/database_pb'; import apiGateway from 'gen-proto-js/teleport/lib/teleterm/v1/gateway_pb'; import apiServer from 'gen-proto-js/teleport/lib/teleterm/v1/server_pb'; import apiKube from 'gen-proto-js/teleport/lib/teleterm/v1/kube_pb'; +import apiLabel from 'gen-proto-js/teleport/lib/teleterm/v1/label_pb'; import apiService, { FileTransferDirection, } from 'gen-proto-js/teleport/lib/teleterm/v1/service_pb'; @@ -144,8 +145,8 @@ export type LoginPasswordlessRequest = export type TshClient = { listRootClusters: () => Promise; listLeafClusters: (clusterUri: uri.RootClusterUri) => Promise; - getKubes: (params: ServerSideParams) => Promise; - getDatabases: (params: ServerSideParams) => Promise; + getKubes: (params: GetResourcesParams) => Promise; + getDatabases: (params: GetResourcesParams) => Promise; listDatabaseUsers: (dbUri: uri.DatabaseUri) => Promise; assumeRole: ( clusterUri: uri.RootClusterUri, @@ -155,7 +156,7 @@ export type TshClient = { getRequestableRoles: ( params: GetRequestableRolesParams ) => Promise; - getServers: (params: ServerSideParams) => Promise; + getServers: (params: GetResourcesParams) => Promise; getAccessRequests: ( clusterUri: uri.RootClusterUri ) => Promise; @@ -248,18 +249,25 @@ export type CreateGatewayParams = { subresource_name?: string; }; -export type ServerSideParams = { +export type GetResourcesParams = { clusterUri: uri.ClusterUri; + // sort is a required field because it has direct implications on performance of ListResources. + sort: SortType | null; + // limit cannot be omitted and must be greater than zero, otherwise ListResources is going to + // return an error. + limit: number; // search is used for regular search. search?: string; searchAsRoles?: string; - sort?: SortType; startKey?: string; - limit?: number; // query is used for advanced search. query?: string; }; +// Compatibility type to make sure teleport.e doesn't break. +// TODO(ravicious): Remove after teleterm.e is updated to use GetResourcesParams. +export type ServerSideParams = GetResourcesParams; + export type ReviewAccessRequestParams = { state: RequestState; reason: string; @@ -288,5 +296,7 @@ export type AssumedRequest = { export { FileTransferDirection }; +export type Label = apiLabel.Label.AsObject; + // Replaces object property with a new type type Modify = Omit & R; diff --git a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/Databases.tsx b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/Databases.tsx index 44603cc5073e1..31f6823b1ab5f 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/Databases.tsx +++ b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/Databases.tsx @@ -170,7 +170,7 @@ function getMenuLoginOptions( async function getDatabaseUsers(appContext: IAppContext, dbUri: DatabaseUri) { try { const dbUsers = await retryWithRelogin(appContext, dbUri, () => - appContext.clustersService.getDbUsers(dbUri) + appContext.resourcesService.getDbUsers(dbUri) ); return dbUsers.map(user => ({ login: user, url: '' })); } catch (e) { diff --git a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/useDatabases.ts b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/useDatabases.ts index abeea60105821..f5ccd1443b048 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/useDatabases.ts +++ b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Databases/useDatabases.ts @@ -18,7 +18,7 @@ import { useAppContext } from 'teleterm/ui/appContextProvider'; import { Database, GatewayProtocol, - ServerSideParams, + GetResourcesParams, } from 'teleterm/services/tshd/types'; import { routing } from 'teleterm/ui/uri'; import { makeDatabase } from 'teleterm/ui/services/clusters'; @@ -31,7 +31,7 @@ export function useDatabases() { const { fetchAttempt, ...serverSideResources } = useServerSideResources( { fieldName: 'name', dir: 'ASC' }, // default sort - (params: ServerSideParams) => + (params: GetResourcesParams) => appContext.resourcesService.fetchDatabases(params) ); @@ -46,13 +46,16 @@ export function useDatabases() { targetUri: db.uri, targetName: db.name, targetUser: getTargetUser(db.protocol as GatewayProtocol, dbUser), + origin: 'resource_table', }); const connectionToReuse = appContext.connectionTracker.findConnectionByDocument(doc); if (connectionToReuse) { - appContext.connectionTracker.activateItem(connectionToReuse.id); + appContext.connectionTracker.activateItem(connectionToReuse.id, { + origin: 'resource_table', + }); } else { documentsService.add(doc); documentsService.open(doc.uri); diff --git a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Kubes/useKubes.ts b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Kubes/useKubes.ts index 58f5f7b5e5347..21cca334ddb0b 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Kubes/useKubes.ts +++ b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Kubes/useKubes.ts @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Kube, ServerSideParams } from 'teleterm/services/tshd/types'; +import { Kube, GetResourcesParams } from 'teleterm/services/tshd/types'; import { useAppContext } from 'teleterm/ui/appContextProvider'; import { useClusterContext } from 'teleterm/ui/DocumentCluster/clusterContext'; +import { KubeUri } from 'teleterm/ui/uri'; import { useServerSideResources } from '../useServerSideResources'; @@ -25,11 +26,13 @@ export function useKubes() { const ctx = useClusterContext(); const { fetchAttempt, ...serversideResources } = useServerSideResources( { fieldName: 'name', dir: 'ASC' }, // default sort - (params: ServerSideParams) => appContext.resourcesService.fetchKubes(params) + (params: GetResourcesParams) => + appContext.resourcesService.fetchKubes(params) ); return { - connect: ctx.connectKube, + connect: (kubeUri: KubeUri) => + ctx.connectKube(kubeUri, { origin: 'resource_table' }), fetchAttempt, ...serversideResources, }; diff --git a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Servers/useServers.ts b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Servers/useServers.ts index 9dad56feb3f26..056d301cd2981 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Servers/useServers.ts +++ b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/Servers/useServers.ts @@ -13,7 +13,7 @@ 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. */ -import { Server, ServerSideParams } from 'teleterm/services/tshd/types'; +import { Server, GetResourcesParams } from 'teleterm/services/tshd/types'; import { useAppContext } from 'teleterm/ui/appContextProvider'; import { makeServer } from 'teleterm/ui/services/clusters'; @@ -27,7 +27,7 @@ export function useServers() { const { fetchAttempt, ...serversideResources } = useServerSideResources( { fieldName: 'hostname', dir: 'ASC' }, // default sort - (params: ServerSideParams) => + (params: GetResourcesParams) => appContext.resourcesService.fetchServers(params) ); @@ -42,7 +42,9 @@ export function useServers() { ); const documentsService = appContext.workspacesService.getWorkspaceDocumentService(rootCluster.uri); - const doc = documentsService.createTshNodeDocument(server.uri); + const doc = documentsService.createTshNodeDocument(server.uri, { + origin: 'resource_table', + }); doc.title = `${login}@${server.hostname}`; doc.login = login; diff --git a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/useServerSideResources.ts b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/useServerSideResources.ts index 72427b65ccfd2..6a48d349e6abf 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/useServerSideResources.ts +++ b/web/packages/teleterm/src/ui/DocumentCluster/ClusterResources/useServerSideResources.ts @@ -17,14 +17,19 @@ import { useState, useEffect, useMemo } from 'react'; import { SortType } from 'design/DataTable/types'; import { useAsync } from 'shared/hooks/useAsync'; -import { AgentFilter, AgentLabel } from 'teleport/services/agents'; +import { + AgentFilter as WeakAgentFilter, + AgentLabel, +} from 'teleport/services/agents'; -import { ServerSideParams } from 'teleterm/services/tshd/types'; +import { GetResourcesParams } from 'teleterm/services/tshd/types'; import { useAppContext } from 'teleterm/ui/appContextProvider'; import { retryWithRelogin } from 'teleterm/ui/utils'; import { useClusterContext } from '../clusterContext'; +type AgentFilter = WeakAgentFilter & { sort: SortType }; + function addAgentLabelToQuery(filter: AgentFilter, label: AgentLabel) { const queryParts = []; @@ -49,7 +54,7 @@ const limit = 15; export function useServerSideResources( defaultSort: SortType, - fetchFunction: (params: ServerSideParams) => Promise> + fetchFunction: (params: GetResourcesParams) => Promise> ) { const ctx = useAppContext(); const { clusterUri } = useClusterContext(); diff --git a/web/packages/teleterm/src/ui/DocumentCluster/clusterContext.tsx b/web/packages/teleterm/src/ui/DocumentCluster/clusterContext.tsx index e2057ee613532..2c90902b45da8 100644 --- a/web/packages/teleterm/src/ui/DocumentCluster/clusterContext.tsx +++ b/web/packages/teleterm/src/ui/DocumentCluster/clusterContext.tsx @@ -20,6 +20,7 @@ import { useStore, Store } from 'shared/libs/stores'; import { IAppContext } from 'teleterm/ui/types'; import { ClusterUri, DocumentUri, KubeUri, routing } from 'teleterm/ui/uri'; +import { DocumentOrigin } from 'teleterm/ui/services/workspacesService'; import type * as tsh from 'teleterm/services/tshd/types'; @@ -71,8 +72,11 @@ class ClusterContext extends Store { }); }; - connectKube = (kubeUri: KubeUri) => { - this.appCtx.commandLauncher.executeCommand('kube-connect', { kubeUri }); + connectKube = (kubeUri: KubeUri, params: { origin: DocumentOrigin }) => { + this.appCtx.commandLauncher.executeCommand('kube-connect', { + kubeUri, + origin: params.origin, + }); }; refresh = () => { diff --git a/web/packages/teleterm/src/ui/DocumentGateway/useDocumentGateway.ts b/web/packages/teleterm/src/ui/DocumentGateway/useDocumentGateway.ts index 0450cfb9f1a5c..eb7d0408a70b7 100644 --- a/web/packages/teleterm/src/ui/DocumentGateway/useDocumentGateway.ts +++ b/web/packages/teleterm/src/ui/DocumentGateway/useDocumentGateway.ts @@ -62,6 +62,7 @@ export default function useGateway(doc: types.DocumentGateway) { // same port number. port: gw.localPort, }); + ctx.usageService.captureProtocolUse(doc.targetUri, 'db', doc.origin); }); const [disconnectAttempt, disconnect] = useAsync(async () => { diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx index c1f0edce17155..aa57f69678873 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx +++ b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx @@ -82,6 +82,7 @@ const getDocTshNodeWithServerId: () => DocumentTshNodeWithServerId = () => ({ rootClusterId: 'test', leafClusterId: undefined, login: 'user', + origin: 'resource_table', }); const getDocTshNodeWithLoginHost: () => DocumentTshNodeWithLoginHost = () => { diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts index 72807f0309b78..471df9495ddeb 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts +++ b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts @@ -216,11 +216,11 @@ async function setUpPtyProcess( const cmd = createCmd(doc, rootCluster.proxyHost, getClusterName()); const ptyProcess = await createPtyProcess(ctx, cmd); - if (cmd.kind === 'pty.tsh-login') { - ctx.usageService.captureProtocolUse(clusterUri, 'ssh'); + if (doc.kind === 'doc.terminal_tsh_node') { + ctx.usageService.captureProtocolUse(clusterUri, 'ssh', doc.origin); } - if (cmd.kind === 'pty.tsh-kube-login') { - ctx.usageService.captureProtocolUse(clusterUri, 'kube'); + if (doc.kind === 'doc.terminal_tsh_kube') { + ctx.usageService.captureProtocolUse(clusterUri, 'kube', doc.origin); } const openContextMenu = () => ctx.mainProcessClient.openTerminalContextMenu(); diff --git a/web/packages/teleterm/src/ui/Documents/KeyboardShortcutsPanel.tsx b/web/packages/teleterm/src/ui/Documents/KeyboardShortcutsPanel.tsx index 79d79b9d80b37..266a5e48babce 100644 --- a/web/packages/teleterm/src/ui/Documents/KeyboardShortcutsPanel.tsx +++ b/web/packages/teleterm/src/ui/Documents/KeyboardShortcutsPanel.tsx @@ -31,6 +31,10 @@ export function KeyboardShortcutsPanel() { title: 'Open New Tab', shortcutAction: 'newTab', }, + { + title: 'Open New Terminal Tab', + shortcutAction: 'newTerminalTab', + }, { title: 'Go To Next Tab', shortcutAction: 'nextTab', diff --git a/web/packages/teleterm/src/ui/QuickInput/QuickInput.tsx b/web/packages/teleterm/src/ui/QuickInput/QuickInput.tsx index 2d46400a581fb..9f53219e46622 100644 --- a/web/packages/teleterm/src/ui/QuickInput/QuickInput.tsx +++ b/web/packages/teleterm/src/ui/QuickInput/QuickInput.tsx @@ -145,11 +145,13 @@ function QuickInput() { return ( props.theme.space[7]}px * 2); + height: 100%; + `} flex={1} ref={refContainer} onFocus={handleOnFocus} diff --git a/web/packages/teleterm/src/ui/QuickInput/useQuickInput.ts b/web/packages/teleterm/src/ui/QuickInput/useQuickInput.ts index 621ae556039bc..dfed5e5e7daae 100644 --- a/web/packages/teleterm/src/ui/QuickInput/useQuickInput.ts +++ b/web/packages/teleterm/src/ui/QuickInput/useQuickInput.ts @@ -35,7 +35,12 @@ import { assertUnreachable, retryWithRelogin } from '../utils'; export default function useQuickInput() { const appContext = useAppContext(); - const { quickInputService, workspacesService, commandLauncher } = appContext; + const { + quickInputService, + workspacesService, + commandLauncher, + usageService, + } = appContext; workspacesService.useState(); const documentsService = workspacesService.getActiveWorkspaceDocumentService(); @@ -105,6 +110,14 @@ export default function useQuickInput() { const params = routing.parseClusterUri( workspacesService.getActiveWorkspace()?.localClusterUri ).params; + // ugly hack but QuickInput will be removed in v13 + if (inputValue.startsWith('tsh proxy db')) { + usageService.captureProtocolUse( + workspacesService.getRootClusterUri(), + 'db', + 'search_bar' + ); + } documentsService.openNewTerminal({ initCommand: inputValue, rootClusterId: routing.parseClusterUri( @@ -120,6 +133,7 @@ export default function useQuickInput() { commandLauncher.executeCommand('tsh-ssh', { loginHost: command.loginHost, localClusterUri, + origin: 'search_bar', }); break; } diff --git a/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx b/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx new file mode 100644 index 0000000000000..eb60125a2e0a8 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx @@ -0,0 +1,103 @@ +/** + * 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. + */ + +import React from 'react'; +import { render, screen } from 'design/utils/testing'; +import { makeSuccessAttempt } from 'shared/hooks/useAsync'; + +import { MockAppContext } from 'teleterm/ui/fixtures/mocks'; +import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider'; + +import * as pickers from './pickers/pickers'; +import * as useSearchAttempts from './pickers/useSearchAttempts'; +import * as SearchContext from './SearchContext'; + +import { SearchBarConnected } from './SearchBar'; + +it('does not display empty results copy after selecting two filters', () => { + const appContext = new MockAppContext(); + appContext.workspacesService.setState(draft => { + draft.rootClusterUri = '/clusters/foo'; + }); + + const mockAttempts = [makeSuccessAttempt([])]; + jest + .spyOn(useSearchAttempts, 'useSearchAttempts') + .mockImplementation(() => mockAttempts); + jest.spyOn(SearchContext, 'useSearchContext').mockImplementation(() => ({ + filters: [ + { filter: 'cluster', clusterUri: '/clusters/foo' }, + { filter: 'resource-type', resourceType: 'servers' }, + ], + inputValue: '', + setFilter: () => {}, + removeFilter: () => {}, + opened: true, + open: () => {}, + close: () => {}, + closeAndResetInput: () => {}, + resetInput: () => {}, + changeActivePicker: () => {}, + onInputValueChange: () => {}, + activePicker: pickers.actionPicker, + inputRef: undefined, + })); + + render( + + + + ); + + const results = screen.getByRole('menu'); + expect(results).not.toHaveTextContent('No matching results found'); +}); + +it('does display empty results copy after providing search query for which there is no results', () => { + const appContext = new MockAppContext(); + appContext.workspacesService.setState(draft => { + draft.rootClusterUri = '/clusters/foo'; + }); + + const mockAttempts = [makeSuccessAttempt([])]; + jest + .spyOn(useSearchAttempts, 'useSearchAttempts') + .mockImplementation(() => mockAttempts); + jest.spyOn(SearchContext, 'useSearchContext').mockImplementation(() => ({ + inputValue: 'foo', + filters: [], + setFilter: () => {}, + removeFilter: () => {}, + opened: true, + open: () => {}, + close: () => {}, + closeAndResetInput: () => {}, + resetInput: () => {}, + changeActivePicker: () => {}, + onInputValueChange: () => {}, + activePicker: pickers.actionPicker, + inputRef: undefined, + })); + + render( + + + + ); + + const results = screen.getByRole('menu'); + expect(results).toHaveTextContent('No matching results found'); +}); diff --git a/web/packages/teleterm/src/ui/Search/SearchBar.tsx b/web/packages/teleterm/src/ui/Search/SearchBar.tsx new file mode 100644 index 0000000000000..27e0107349dbb --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/SearchBar.tsx @@ -0,0 +1,172 @@ +/** + * 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. + */ + +import React, { useRef, useEffect } from 'react'; +import styled from 'styled-components'; +import { Box, Flex } from 'design'; +import { space, width, color, height } from 'styled-system'; + +import { + SearchContextProvider, + useSearchContext, +} from 'teleterm/ui/Search/SearchContext'; +import { KeyboardShortcutAction } from 'teleterm/services/config'; +import { + useKeyboardShortcutFormatters, + useKeyboardShortcuts, +} from 'teleterm/ui/services/keyboardShortcuts'; + +import { useAppContext } from '../appContextProvider'; + +const OPEN_COMMAND_BAR_SHORTCUT_ACTION: KeyboardShortcutAction = + 'openCommandBar'; + +export function SearchBarConnected() { + const { workspacesService } = useAppContext(); + workspacesService.useState(); + + if (!workspacesService.getRootClusterUri()) { + return null; + } + + return ( + + + + ); +} + +function SearchBar() { + const containerRef = useRef(); + const { getAccelerator } = useKeyboardShortcutFormatters(); + const { + activePicker, + inputValue, + onInputValueChange, + inputRef, + opened, + open, + close, + } = useSearchContext(); + const ctx = useAppContext(); + ctx.clustersService.useState(); + + useKeyboardShortcuts({ + [OPEN_COMMAND_BAR_SHORTCUT_ACTION]: () => { + open(); + }, + }); + + useEffect(() => { + const onClickOutside = e => { + if (!e.composedPath().includes(containerRef.current)) { + close(); + } + }; + if (opened) { + window.addEventListener('click', onClickOutside); + return () => window.removeEventListener('click', onClickOutside); + } + }, [close, opened]); + + function handleOnFocus(e: React.FocusEvent) { + open(e.relatedTarget); + } + + const defaultInputProps = { + ref: inputRef, + role: 'searchbox', + placeholder: activePicker.placeholder, + value: inputValue, + onChange: e => { + onInputValueChange(e.target.value); + }, + spellCheck: false, + }; + + return ( + props.theme.space[7]}px * 2); + height: 100%; + background: ${props => props.theme.colors.levels.surface}; + border: 1px ${props => props.theme.colors.action.disabledBackground} + solid; + `} + justifyContent="center" + ref={containerRef} + onFocus={handleOnFocus} + > + {!opened && ( + <> + + + {getAccelerator(OPEN_COMMAND_BAR_SHORTCUT_ACTION)} + + + )} + {opened && ( + } + /> + )} + + ); +} + +const Input = styled.input(props => { + const { theme } = props; + return { + height: '100%', + background: theme.colors.levels.sunkenSecondary, + boxSizing: 'border-box', + color: theme.colors.text.primary, + width: '100%', + outline: 'none', + border: 'none', + padding: `${theme.space[1]}px ${theme.space[2]}px`, + '&:hover, &:focus': { + color: theme.colors.text.contrast, + background: theme.colors.levels.surface, + + opacity: 1, + }, + '::placeholder': { + color: theme.colors.text.secondary, + }, + + ...space(props), + ...width(props), + ...height(props), + ...color(props), + }; +}); + +const Shortcut = styled(Box).attrs({ p: 1 })` + position: absolute; + right: ${props => props.theme.space[2]}px; + top: 50%; + transform: translate(0, -50%); + color: ${({ theme }) => theme.colors.text.secondary}; + background-color: ${({ theme }) => theme.colors.levels.surface}; + line-height: 12px; + font-size: 12px; + border-radius: 2px; +`; diff --git a/web/packages/teleterm/src/ui/Search/SearchContext.tsx b/web/packages/teleterm/src/ui/Search/SearchContext.tsx new file mode 100644 index 0000000000000..8885cbc49b3f7 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/SearchContext.tsx @@ -0,0 +1,143 @@ +/** + * Copyright 2022 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. + */ + +import React, { + useContext, + useState, + FC, + useCallback, + createContext, + useRef, + MutableRefObject, +} from 'react'; + +import { SearchFilter } from 'teleterm/ui/Search/searchResult'; + +import { actionPicker, SearchPicker } from './pickers/pickers'; + +export interface SearchContext { + inputRef: MutableRefObject; + inputValue: string; + opened: boolean; + filters: SearchFilter[]; + activePicker: SearchPicker; + onInputValueChange(value: string): void; + changeActivePicker(picker: SearchPicker): void; + close(): void; + closeAndResetInput(): void; + open(fromElement?: Element): void; + resetInput(): void; + setFilter(filter: SearchFilter): void; + removeFilter(filter: SearchFilter): void; +} + +const SearchContext = createContext(null); + +export const SearchContextProvider: FC = props => { + const previouslyActive = useRef(); + const inputRef = useRef(); + const [opened, setOpened] = useState(false); + const [inputValue, setInputValue] = useState(''); + const [activePicker, setActivePicker] = useState(actionPicker); + // TODO(ravicious): Consider using another data structure for search filters as we know that we + // always have only two specific filters: one for clusters and one for resource type. + // + // This could probably be represented by an object instead plus an array for letting the user + // provide those filters in any order they want. The array would be used in the UI that renders + // the filters while code that uses the search filters, such as ResourcesService.searchResources, + // could operate on the object instead. + const [filters, setFilters] = useState([]); + + function changeActivePicker(picker: SearchPicker): void { + setActivePicker(picker); + setInputValue(''); + } + + const close = useCallback(() => { + setOpened(false); + setActivePicker(actionPicker); + if (previouslyActive.current instanceof HTMLElement) { + previouslyActive.current.focus(); + } + }, []); + + const closeAndResetInput = useCallback(() => { + close(); + setInputValue(''); + }, [close]); + + const resetInput = useCallback(() => { + setInputValue(''); + }, []); + + function open(fromElement?: Element): void { + if (opened) { + return; + } + previouslyActive.current = fromElement || document.activeElement; + setOpened(true); + } + + function setFilter(filter: SearchFilter) { + // UI prevents adding more than one filter of the same type + setFilters(prevState => [...prevState, filter]); + inputRef.current?.focus(); + } + + function removeFilter(filter: SearchFilter) { + setFilters(prevState => { + const index = prevState.indexOf(filter); + if (index >= 0) { + const copied = [...prevState]; + copied.splice(index, 1); + return copied; + } + return prevState; + }); + inputRef.current?.focus(); + } + + return ( + + ); +}; + +export const useSearchContext = () => { + const context = useContext(SearchContext); + + if (!context) { + throw new Error('SearchContext requires SearchContextProvider context.'); + } + + return context; +}; diff --git a/web/packages/teleterm/src/ui/Search/actions.tsx b/web/packages/teleterm/src/ui/Search/actions.tsx new file mode 100644 index 0000000000000..6cf57422e2c09 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/actions.tsx @@ -0,0 +1,167 @@ +/** + * 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. + */ + +import { IAppContext } from 'teleterm/ui/types'; +import { routing } from 'teleterm/ui/uri'; +import { GatewayProtocol } from 'teleterm/services/tshd/types'; +import { SearchResult } from 'teleterm/ui/Search/searchResult'; +import { SearchContext } from 'teleterm/ui/Search/SearchContext'; + +export interface SimpleAction { + type: 'simple-action'; + searchResult: SearchResult; + preventAutoClose?: boolean; // TODO(gzdunek): consider other options (callback preventClose() in perform?) + + perform(): void; +} + +export interface ParametrizedAction { + type: 'parametrized-action'; + searchResult: SearchResult; + preventAutoClose?: boolean; + parameter: { + getSuggestions(): Promise; + placeholder: string; + }; + + perform(parameter: string): void; +} + +export type SearchAction = SimpleAction | ParametrizedAction; + +export function mapToActions( + ctx: IAppContext, + searchContext: SearchContext, + searchResults: SearchResult[] +): SearchAction[] { + return searchResults.map(result => { + if (result.kind === 'server') { + return { + type: 'parametrized-action', + searchResult: result, + parameter: { + getSuggestions: async () => + ctx.clustersService.findClusterByResource(result.resource.uri) + ?.loggedInUser?.sshLoginsList, + placeholder: 'Provide login', + }, + perform(login) { + ctx.commandLauncher.executeCommand('tsh-ssh', { + localClusterUri: result.resource.uri, + loginHost: `${login}@${result.resource.hostname}`, + origin: 'search_bar', + }); + }, + }; + } + if (result.kind === 'kube') { + return { + type: 'simple-action', + searchResult: result, + perform() { + ctx.commandLauncher.executeCommand('kube-connect', { + kubeUri: result.resource.uri, + origin: 'search_bar', + }); + }, + }; + } + if (result.kind === 'database') { + return { + type: 'parametrized-action', + searchResult: result, + parameter: { + getSuggestions: () => + ctx.resourcesService.getDbUsers(result.resource.uri), + placeholder: 'Provide db username', + }, + async perform(dbUsername) { + const rootClusterUri = routing.ensureRootClusterUri( + result.resource.uri + ); + const documentsService = + ctx.workspacesService.getWorkspaceDocumentService(rootClusterUri); + + const doc = documentsService.createGatewayDocument({ + // Not passing the `gatewayUri` field here, as at this point the gateway doesn't exist yet. + // `port` is not passed as well, we'll let the tsh daemon pick a random one. + targetUri: result.resource.uri, + targetName: result.resource.name, + targetUser: getTargetUser( + result.resource.protocol as GatewayProtocol, + dbUsername + ), + origin: 'search_bar', + }); + + await ctx.workspacesService.setActiveWorkspace(rootClusterUri); + + const connectionToReuse = + ctx.connectionTracker.findConnectionByDocument(doc); + + if (connectionToReuse) { + ctx.connectionTracker.activateItem(connectionToReuse.id, { + origin: 'search_bar', + }); + } else { + documentsService.add(doc); + documentsService.open(doc.uri); + } + }, + }; + } + if (result.kind === 'resource-type-filter') { + return { + type: 'simple-action', + searchResult: result, + preventAutoClose: true, + perform() { + searchContext.setFilter({ + filter: 'resource-type', + resourceType: result.resource, + }); + }, + }; + } + if (result.kind === 'cluster-filter') { + return { + type: 'simple-action', + searchResult: result, + preventAutoClose: true, + perform() { + searchContext.setFilter({ + filter: 'cluster', + clusterUri: result.resource.uri, + }); + }, + }; + } + }); +} + +function getTargetUser( + protocol: GatewayProtocol, + providedDbUser: string +): string { + // we are replicating tsh behavior (user can be omitted for Redis) + // https://github.com/gravitational/teleport/blob/796e37bdbc1cb6e0a93b07115ffefa0e6922c529/tool/tsh/db.go#L240-L244 + // but unlike tsh, Connect has to provide a user that is then used in a gateway document + if (protocol === 'redis') { + return providedDbUser || 'default'; + } + + return providedDbUser; +} diff --git a/web/packages/teleterm/src/ui/Search/index.ts b/web/packages/teleterm/src/ui/Search/index.ts new file mode 100644 index 0000000000000..bd0feaffc015f --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/index.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ + +export { SearchBarConnected as SearchBar } from './SearchBar'; diff --git a/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.story.tsx b/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.story.tsx new file mode 100644 index 0000000000000..63879911dae7d --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.story.tsx @@ -0,0 +1,267 @@ +/** + * 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. + */ + +import React from 'react'; +import { makeSuccessAttempt } from 'shared/hooks/useAsync'; + +import { routing } from 'teleterm/ui/uri'; + +import { SearchResult } from '../searchResult'; +import { + makeDatabase, + makeKube, + makeResourceResult, + makeServer, + makeLabelsList, +} from '../searchResultTestHelpers'; + +import { ComponentMap } from './ActionPicker'; +import { ResultList } from './ResultList'; + +import type * as uri from 'teleterm/ui/uri'; + +export default { + title: 'Teleterm/Search/ActionPicker', +}; + +const clusterUri: uri.ClusterUri = '/clusters/teleport-local'; + +export const Items = () => { + return ( +
* { + max-height: unset; + } + `} + > + +
+ ); +}; +export const ItemsNarrow = () => { + return ( +
* { + max-height: unset; + } + `} + > + +
+ ); +}; + +const List = () => { + const searchResults: SearchResult[] = [ + makeResourceResult({ + kind: 'server', + resource: makeServer({ + hostname: 'long-label-list', + uri: `${clusterUri}/servers/2f96e498-88ec-442f-a25b-569fa915041c`, + name: '2f96e498-88ec-442f-a25b-569fa915041c', + labelsList: makeLabelsList({ + arch: 'aarch64', + external: '32.192.113.93', + internal: '10.0.0.175', + kernel: '5.13.0-1234-aws', + service: 'ansible', + }), + }), + }), + makeResourceResult({ + kind: 'server', + resource: makeServer({ + hostname: 'short-label-list', + addr: '', + tunnel: true, + uri: `${clusterUri}/servers/90a29595-aac7-42eb-a484-c6c0e23f1a21`, + name: '90a29595-aac7-42eb-a484-c6c0e23f1a21', + labelsList: makeLabelsList({ + arch: 'aarch64', + service: 'ansible', + external: '32.192.113.93', + internal: '10.0.0.175', + }), + }), + }), + makeResourceResult({ + kind: 'server', + resourceMatches: [{ field: 'name', searchTerm: 'bbaaceba-6bd1-4750' }], + resource: makeServer({ + hostname: 'uuid-match', + addr: '', + tunnel: true, + uri: `${clusterUri}/servers/bbaaceba-6bd1-4750-9d3d-1a80e0cc8a63`, + name: 'bbaaceba-6bd1-4750-9d3d-1a80e0cc8a63', + labelsList: makeLabelsList({ + internal: '10.0.0.175', + service: 'ansible', + external: '32.192.113.93', + arch: 'aarch64', + }), + }), + }), + makeResourceResult({ + kind: 'database', + resource: makeDatabase({ + uri: `${clusterUri}/dbs/no-desc`, + name: 'no-desc', + desc: '', + labelsList: makeLabelsList({ + 'aws/Accounting': 'dev-ops', + 'aws/Environment': 'demo-13-biz', + 'aws/Name': 'db-bastion-4-13biz', + 'aws/Owner': 'foobar', + 'aws/Service': 'teleport-db', + engine: '🐘', + env: 'dev', + 'teleport.dev/origin': 'config-file', + }), + }), + }), + makeResourceResult({ + kind: 'database', + resource: makeDatabase({ + uri: `${clusterUri}/dbs/short-desc`, + name: 'short-desc', + desc: 'Lorem ipsum', + labelsList: makeLabelsList({ + 'aws/Environment': 'demo-13-biz', + 'aws/Name': 'db-bastion-4-13biz', + 'aws/Accounting': 'dev-ops', + 'aws/Owner': 'foobar', + 'aws/Service': 'teleport-db', + engine: '🐘', + env: 'dev', + 'teleport.dev/origin': 'config-file', + }), + }), + }), + makeResourceResult({ + kind: 'database', + resource: makeDatabase({ + uri: `${clusterUri}/dbs/long-desc`, + name: 'long-desc', + desc: 'Eget dignissim lectus nisi vitae nunc', + labelsList: makeLabelsList({ + 'aws/Environment': 'demo-13-biz', + 'aws/Name': 'db-bastion-4-13biz', + 'aws/Accounting': 'dev-ops', + 'aws/Owner': 'foobar', + 'aws/Service': 'teleport-db', + engine: '🐘', + env: 'dev', + 'teleport.dev/origin': 'config-file', + }), + }), + }), + makeResourceResult({ + kind: 'database', + resource: makeDatabase({ + uri: `${clusterUri}/dbs/super-long-desc`, + name: 'super-long-desc', + desc: 'Duis id tortor at purus tincidunt finibus. Mauris eu semper orci, non commodo lacus. Praesent sollicitudin magna id laoreet porta. Nunc lobortis varius sem vel fringilla.', + labelsList: makeLabelsList({ + 'aws/Environment': 'demo-13-biz', + 'aws/Accounting': 'dev-ops', + 'aws/Name': 'db-bastion-4-13biz', + engine: '🐘', + 'aws/Owner': 'foobar', + 'aws/Service': 'teleport-db', + env: 'dev', + 'teleport.dev/origin': 'config-file', + }), + }), + }), + makeResourceResult({ + kind: 'kube', + resource: makeKube({ + name: 'short-label-list', + labelsList: makeLabelsList({ + 'im-just-a-smol': 'kube', + kube: 'kubersson', + with: 'little-to-no-labels', + }), + }), + }), + makeResourceResult({ + kind: 'kube', + resource: makeKube({ + name: 'long-label-list', + uri: `${clusterUri}/kubes/long-label-list`, + labelsList: makeLabelsList({ + 'aws/Environment': 'demo-13-biz', + 'aws/Owner': 'foobar', + 'aws/Name': 'db-bastion-4-13biz', + kube: 'kubersson', + with: 'little-to-no-labels', + }), + }), + }), + { + kind: 'resource-type-filter', + resource: 'kubes', + nameMatch: '', + score: 0, + }, + { + kind: 'cluster-filter', + resource: { + name: 'teleport-local', + uri: clusterUri, + authClusterId: '', + connected: true, + leaf: false, + proxyHost: 'teleport-local.dev:3090', + }, + nameMatch: '', + score: 0, + }, + ]; + const attempt = makeSuccessAttempt(searchResults); + + return ( + + attempts={[attempt]} + onPick={() => {}} + onBack={() => {}} + render={searchResult => { + const Component = ComponentMap[searchResult.kind]; + + return { + key: + searchResult.kind !== 'resource-type-filter' + ? searchResult.resource.uri + : searchResult.resource, + Component: ( + + ), + }; + }} + /> + ); +}; diff --git a/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.tsx b/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.tsx new file mode 100644 index 0000000000000..ee8b78d68ab30 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/ActionPicker.tsx @@ -0,0 +1,501 @@ +/** + * 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. + */ + +import React, { ReactElement, useCallback } from 'react'; +import styled from 'styled-components'; +import { Box, ButtonPrimary, Flex, Label as DesignLabel, Text } from 'design'; +import * as icons from 'design/Icon'; +import { Highlight } from 'shared/components/Highlight'; + +import { useAppContext } from 'teleterm/ui/appContextProvider'; +import { + ResourceMatch, + SearchResult, + ResourceSearchResult, + SearchResultDatabase, + SearchResultKube, + SearchResultServer, + SearchResultCluster, + SearchResultResourceType, +} from 'teleterm/ui/Search/searchResult'; +import * as tsh from 'teleterm/services/tshd/types'; +import * as uri from 'teleterm/ui/uri'; + +import { SearchAction } from '../actions'; +import { useSearchContext } from '../SearchContext'; + +import { useSearchAttempts } from './useSearchAttempts'; +import { getParameterPicker } from './pickers'; +import { ResultList, EmptyListCopy } from './ResultList'; + +export function ActionPicker(props: { input: ReactElement }) { + const ctx = useAppContext(); + const { clustersService } = ctx; + ctx.clustersService.useState(); + + const { + changeActivePicker, + close, + inputValue, + resetInput, + closeAndResetInput, + filters, + removeFilter, + } = useSearchContext(); + const attempts = useSearchAttempts(); + const totalCountOfClusters = clustersService.getClusters().length; + + const getClusterName = useCallback( + (resourceUri: uri.ClusterOrResourceUri) => { + if (totalCountOfClusters === 1) { + return; + } + + const clusterUri = uri.routing.ensureClusterUri(resourceUri); + const cluster = clustersService.findCluster(clusterUri); + + return cluster ? cluster.name : uri.routing.parseClusterName(resourceUri); + }, + [clustersService, totalCountOfClusters] + ); + + const onPick = useCallback( + (action: SearchAction) => { + if (action.type === 'simple-action') { + action.perform(); + // TODO: This logic probably should be encapsulated inside SearchContext, so that ActionPicker + // and ParameterPicker can reuse it. + // + // Overall, the context should probably encapsulate more logic so that the components don't + // have to worry about low-level stuff such as input state. Input state already lives in the + // search context so it should be managed from there, if possible. + if (action.preventAutoClose === true) { + resetInput(); + } else { + closeAndResetInput(); + } + } + if (action.type === 'parametrized-action') { + changeActivePicker(getParameterPicker(action)); + } + }, + [changeActivePicker, closeAndResetInput, resetInput] + ); + + // If the input is empty, we don't want to say "No matching results found" if the user is yet to + // type anything. This can happen e.g. after selecting two filters. + const NoResultsComponent = + inputValue.length > 0 ? ( + + No matching results found. + + ) : null; + + const filterButtons = filters.map(s => { + if (s.filter === 'resource-type') { + return ( + removeFilter(s)} + > + {s.resourceType} + + ); + } + if (s.filter === 'cluster') { + const clusterName = getClusterName(s.clusterUri); + return ( + removeFilter(s)} + > + {clusterName} + + ); + } + }); + + function handleKeyDown(e: React.KeyboardEvent) { + const { length } = filters; + if (e.key === 'Backspace' && inputValue === '' && length) { + removeFilter(filters[length - 1]); + } + } + + return ( + <> + + {filterButtons} + {props.input} + + + attempts={attempts} + onPick={onPick} + onBack={close} + render={item => { + const Component = ComponentMap[item.searchResult.kind]; + return { + key: + item.searchResult.kind !== 'resource-type-filter' + ? item.searchResult.resource.uri + : item.searchResult.resource, + Component: ( + + ), + }; + }} + NoResultsComponent={NoResultsComponent} + /> + + ); +} + +export const ComponentMap: Record< + SearchResult['kind'], + React.FC> +> = { + server: ServerItem, + kube: KubeItem, + database: DatabaseItem, + 'cluster-filter': ClusterFilterItem, + 'resource-type-filter': ResourceTypeFilterItem, +}; + +type SearchResultItem = { + searchResult: T; + getClusterName: (uri: uri.ResourceUri) => string; +}; + +function Item( + props: React.PropsWithChildren<{ + Icon: React.ComponentType<{ + color: string; + fontSize: string; + lineHeight: string; + }>; + iconColor: string; + }> +) { + return ( + + {/* lineHeight of the icon needs to match the line height of the first row of props.children */} + + + {props.children} + + + ); +} + +function ClusterFilterItem(props: SearchResultItem) { + return ( + + + Search only in{' '} + + + + + + ); +} + +function ResourceTypeFilterItem( + props: SearchResultItem +) { + return ( + + + Search only for{' '} + + + + + + ); +} + +export function ServerItem(props: SearchResultItem) { + const { searchResult } = props; + const server = searchResult.resource; + const hasUuidMatches = searchResult.resourceMatches.some( + match => match.field === 'name' + ); + + return ( + + + + Connect over SSH to{' '} + + + + + + + {props.getClusterName(server.uri)} + + + + + + + {server.tunnel ? ( + + ↵ tunnel + + ) : ( + + + + )} + + {hasUuidMatches && ( + + UUID:{' '} + + + )} + + + + ); +} + +export function DatabaseItem(props: SearchResultItem) { + const { searchResult } = props; + const db = searchResult.resource; + + const $resourceFields = ( + + + + / + + + {db.desc && ( + + + + )} + + ); + + return ( + + + + Set up a db connection for{' '} + + + + + + + {props.getClusterName(db.uri)} + + + + + {/* If the description is long, put the resource fields on a separate line. + Otherwise show the resource fields and the labels together in a single line. + */} + {db.desc.length >= 30 ? ( + <> + {$resourceFields} + + + ) : ( + {$resourceFields} + )} + + ); +} + +export function KubeItem(props: SearchResultItem) { + const { searchResult } = props; + + return ( + + + + Log in to Kubernetes cluster{' '} + + + + + + + {props.getClusterName(searchResult.resource.uri)} + + + + + + + ); +} + +function Labels( + props: React.PropsWithChildren<{ + searchResult: ResourceSearchResult; + }> +) { + const { searchResult } = props; + + // Label name to score. + const scoreMap: Map = new Map(); + searchResult.labelMatches.forEach(match => { + const currentScore = scoreMap.get(match.labelName) || 0; + scoreMap.set(match.labelName, currentScore + match.score); + }); + + const sortedLabelsList = [...searchResult.resource.labelsList]; + sortedLabelsList.sort( + (a, b) => + // Highest score first. + (scoreMap.get(b.name) || 0) - (scoreMap.get(a.name) || 0) + ); + + return ( + + {props.children} + {sortedLabelsList.map(label => ( + + ); +} + +const LabelsFlex = styled(Flex).attrs({ gap: 1 })` + overflow-x: hidden; + flex-wrap: nowrap; + align-items: baseline; + + // Make the children not shrink, otherwise they would shrink in attempt to render all labels in + // the same row. + & > * { + flex-shrink: 0; + } +`; + +const ResourceFields = styled(Flex).attrs({ gap: 1 })` + color: ${props => props.theme.colors.text.primary}; + font-size: ${props => props.theme.fontSizes[0]}px; +`; + +function Label(props: { + searchResult: ResourceSearchResult; + label: tsh.Label; +}) { + const { searchResult: item, label } = props; + const labelMatches = item.labelMatches.filter( + match => match.labelName == label.name + ); + const nameMatches = labelMatches + .filter(match => match.kind === 'label-name') + .map(match => match.searchTerm); + const valueMatches = labelMatches + .filter(match => match.kind === 'label-value') + .map(match => match.searchTerm); + + return ( + + :{' '} + + + ); +} + +function HighlightField(props: { + searchResult: ResourceSearchResult; + field: ResourceMatch['field']; +}) { + // `as` used as a workaround for a TypeScript issue. + // https://github.com/microsoft/TypeScript/issues/33591 + const keywords = ( + props.searchResult.resourceMatches as ResourceMatch< + ResourceSearchResult['kind'] + >[] + ) + .filter(match => match.field === props.field) + .map(match => match.searchTerm); + + return ( + + ); +} diff --git a/web/packages/teleterm/src/ui/Search/pickers/ParameterPicker.tsx b/web/packages/teleterm/src/ui/Search/pickers/ParameterPicker.tsx new file mode 100644 index 0000000000000..9fb949fc4ba49 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/ParameterPicker.tsx @@ -0,0 +1,88 @@ +/** + * 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. + */ + +import React, { ReactElement, useCallback, useEffect } from 'react'; +import { Highlight } from 'shared/components/Highlight'; +import { + makeSuccessAttempt, + mapAttempt, + useAsync, +} from 'shared/hooks/useAsync'; + +import { useSearchContext } from '../SearchContext'; +import { ParametrizedAction } from '../actions'; + +import { ResultList } from './ResultList'; +import { actionPicker } from './pickers'; + +interface ParameterPickerProps { + action: ParametrizedAction; + input: ReactElement; +} + +export function ParameterPicker(props: ParameterPickerProps) { + const { inputValue, closeAndResetInput, changeActivePicker, resetInput } = + useSearchContext(); + const [suggestionsAttempt, fetch] = useAsync( + props.action.parameter.getSuggestions + ); + const inputSuggestionAttempt = makeSuccessAttempt(inputValue && [inputValue]); + + useEffect(() => { + fetch(); + }, [props.action]); + + const attempt = mapAttempt(suggestionsAttempt, suggestions => + suggestions.filter( + v => + v.toLocaleLowerCase().includes(inputValue.toLocaleLowerCase()) && + v !== inputValue + ) + ); + + const onPick = useCallback( + (item: string) => { + props.action.perform(item); + if (props.action.preventAutoClose === true) { + resetInput(); + } else { + closeAndResetInput(); + } + }, + [closeAndResetInput, resetInput, props.action] + ); + + const onBack = useCallback(() => { + changeActivePicker(actionPicker); + }, [changeActivePicker]); + + return ( + <> + {props.input} + + attempts={[inputSuggestionAttempt, attempt]} + onPick={onPick} + onBack={onBack} + render={item => ({ + key: item, + Component: ( + + ), + })} + /> + + ); +} diff --git a/web/packages/teleterm/src/ui/Search/pickers/ResultList.tsx b/web/packages/teleterm/src/ui/Search/pickers/ResultList.tsx new file mode 100644 index 0000000000000..22fd67d05ae40 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/ResultList.tsx @@ -0,0 +1,213 @@ +/** + * 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. + */ + +import React, { + ReactElement, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import styled from 'styled-components'; + +import { Attempt } from 'shared/hooks/useAsync'; +import { Box } from 'design'; + +import LinearProgress from 'teleterm/ui/components/LinearProgress'; + +type ResultListProps = { + /** + * List of attempts containing results to render. + * Displayed items will follow the order of attempts. + * If any attempt is loading, then the loading bar is visible. + */ + attempts: Attempt[]; + /** + * NoResultsComponent is the element that's going to be rendered instead of the list if the + * attempt has successfully finished but there's no results to show. + */ + NoResultsComponent?: ReactElement; + onPick(item: T): void; + onBack(): void; + render(item: T): { Component: ReactElement; key: string }; +}; + +export function ResultList(props: ResultListProps) { + const { attempts, NoResultsComponent, onPick, onBack } = props; + const activeItemRef = useRef(); + const [activeItemIndex, setActiveItemIndex] = useState(0); + const shouldShowNoResultsCopy = + NoResultsComponent && + attempts.every(a => a.status === 'success' && a.data.length === 0); + + const items = useMemo(() => { + return attempts.map(a => a.data || []).flat(); + }, [attempts]); + + // Reset the active item index if it's greater than the number of available items. + // This can happen in cases where the user selects the nth item and then filters the list so that + // there's only one item. + if (activeItemIndex !== 0 && activeItemIndex >= items.length) { + setActiveItemIndex(0); + } + + useEffect(() => { + const handleArrowKey = (e: KeyboardEvent, nudge: number) => { + const next = getNext(activeItemIndex + nudge, items.length); + setActiveItemIndex(next); + // `false` - bottom of the element will be aligned to the bottom of the visible area of the scrollable ancestor + activeItemRef.current?.scrollIntoView(false); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + switch (e.key) { + case 'Enter': { + e.stopPropagation(); + e.preventDefault(); + + const item = items[activeItemIndex]; + if (item) { + onPick(item); + } + break; + } + case 'Escape': { + onBack(); + break; + } + case 'ArrowUp': + e.stopPropagation(); + e.preventDefault(); + + handleArrowKey(e, -1); + break; + case 'ArrowDown': + e.stopPropagation(); + e.preventDefault(); + + handleArrowKey(e, 1); + break; + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [items, onPick, onBack, activeItemIndex]); + + return ( + + {attempts.some(a => a.status === 'processing') && ( +
+ +
+ )} + {items.map((r, index) => { + const isActive = index === activeItemIndex; + const { Component, key } = props.render(r); + + return ( + props.onPick(r)} + > + {Component} + + ); + })} + {shouldShowNoResultsCopy && NoResultsComponent} +
+ ); +} + +const StyledItem = styled.div` + &:hover, + &:focus { + cursor: pointer; + background: ${props => props.theme.colors.levels.elevated}; + } + + & mark { + color: inherit; + background-color: ${props => props.theme.colors.brand.accent}; + } + + :not(:last-of-type) { + border-bottom: 2px solid + ${props => props.theme.colors.levels.surfaceSecondary}; + } + + padding: ${props => props.theme.space[2]}px; + color: ${props => props.theme.colors.text.contrast}; + background: ${props => + props.$active + ? props.theme.colors.levels.elevated + : props.theme.colors.levels.surface}; +`; + +export const EmptyListCopy = styled(Box)` + width: 100%; + height: 100%; + padding: ${props => props.theme.space[2]}px; + line-height: 1.5em; + + ul { + margin: 0; + padding-inline-start: 2em; + } +`; + +function getNext(selectedIndex = 0, max = 0) { + let index = selectedIndex % max; + if (index < 0) { + index += max; + } + return index; +} + +const StyledGlobalSearchResults = styled.div(({ theme }) => { + return { + boxShadow: '8px 8px 18px rgb(0 0 0)', + color: theme.colors.text.contrast, + background: theme.colors.levels.surface, + boxSizing: 'border-box', + // Account for border. + width: 'calc(100% + 2px)', + // Careful, this is hardcoded based on the input height. + marginTop: '38px', + display: 'block', + position: 'absolute', + border: '1px solid ' + theme.colors.action.hover, + fontSize: '12px', + listStyle: 'none outside none', + textShadow: 'none', + zIndex: '1000', + maxHeight: '350px', + overflow: 'auto', + // Hardcoded to height of the shortest item. + minHeight: '42px', + }; +}); diff --git a/web/packages/teleterm/src/ui/Search/pickers/pickers.tsx b/web/packages/teleterm/src/ui/Search/pickers/pickers.tsx new file mode 100644 index 0000000000000..5dd16cc5ab012 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/pickers.tsx @@ -0,0 +1,44 @@ +/** + * 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. + */ + +import React from 'react'; + +import { ParametrizedAction } from '../actions'; + +import { ActionPicker } from './ActionPicker'; +import { ParameterPicker } from './ParameterPicker'; + +export const actionPicker: SearchPicker = { + picker: props => , + placeholder: 'Search for resources by name and labels across clusters', +}; +export const getParameterPicker = ( + parametrizedAction: ParametrizedAction +): SearchPicker => { + return { + picker: props => ( + + ), + placeholder: parametrizedAction.parameter.placeholder, + }; +}; + +export interface SearchPicker { + picker: React.ComponentType<{ + input: React.ReactElement; + }>; + placeholder: string; +} diff --git a/web/packages/teleterm/src/ui/Search/pickers/useSearchAttempts.ts b/web/packages/teleterm/src/ui/Search/pickers/useSearchAttempts.ts new file mode 100644 index 0000000000000..6c34d683efec5 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/pickers/useSearchAttempts.ts @@ -0,0 +1,113 @@ +/** + * 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. + */ + +import { + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, +} from 'react'; +import { makeEmptyAttempt, mapAttempt, useAsync } from 'shared/hooks/useAsync'; +import { debounce } from 'shared/utils/highbar'; + +import { + sortResults, + useFilterSearch, + useResourceSearch, +} from 'teleterm/ui/Search/useSearch'; +import { mapToActions } from 'teleterm/ui/Search/actions'; +import Logger from 'teleterm/logger'; +import { useAppContext } from 'teleterm/ui/appContextProvider'; +import { useSearchContext } from 'teleterm/ui/Search/SearchContext'; + +export function useSearchAttempts() { + const searchLogger = useRef(new Logger('search')); + const ctx = useAppContext(); + const searchContext = useSearchContext(); + const { inputValue, filters } = searchContext; + + const [resourceSearchAttempt, runResourceSearch, setResourceSearchAttempt] = + useAsync(useResourceSearch()); + const [filterSearchAttempt, runFilterSearch, setFilterSearchAttempt] = + useAsync(useFilterSearch()); + + const runResourceSearchDebounced = useDebounce(runResourceSearch, 200); + + // Both states are used by mapToActions. + ctx.workspacesService.useState(); + ctx.clustersService.useState(); + + const resetAttempts = useCallback(() => { + setResourceSearchAttempt(makeEmptyAttempt()); + setFilterSearchAttempt(makeEmptyAttempt()); + }, [setResourceSearchAttempt, setFilterSearchAttempt]); + + const resourceActionsAttempt = useMemo( + () => + mapAttempt(resourceSearchAttempt, ({ results, search }) => { + const sortedResults = sortResults(results, search); + searchLogger.current.info('results for', search, sortedResults); + + return mapToActions(ctx, searchContext, sortedResults); + }), + [ctx, resourceSearchAttempt, searchContext] + ); + + const filterActionsAttempt = useMemo( + () => + mapAttempt(filterSearchAttempt, ({ results }) => + // TODO(gzdunek): filters are sorted inline, should be done here to align with resource search + mapToActions(ctx, searchContext, results) + ), + [ctx, filterSearchAttempt, searchContext] + ); + + useEffect(() => { + // Reset both attempts as soon as the input changes. If we didn't do that, then the resource + // search attempt would only get updated on debounce. This could lead to the following scenario: + // + // 1. You type in `foo`, wait for the results to show up. + // 2. You clear the input and quickly type in `bar`. + // 3. Now you see the stale results for `foo`, because the debounce didn't kick in yet. + resetAttempts(); + + runFilterSearch(inputValue, filters); + runResourceSearchDebounced(inputValue, filters); + }, [ + inputValue, + filters, + resetAttempts, + runFilterSearch, + runResourceSearchDebounced, + ]); + + return [filterActionsAttempt, resourceActionsAttempt]; +} + +function useDebounce( + callback: (...args: Args) => ReturnValue, + delay: number +) { + const callbackRef = useRef(callback); + useLayoutEffect(() => { + callbackRef.current = callback; + }); + return useMemo( + () => debounce((...args: Args) => callbackRef.current(...args), delay), + [delay] + ); +} diff --git a/web/packages/teleterm/src/ui/Search/searchResult.ts b/web/packages/teleterm/src/ui/Search/searchResult.ts new file mode 100644 index 0000000000000..b8572e7299634 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/searchResult.ts @@ -0,0 +1,114 @@ +/** + * 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. + */ + +import type { ClusterUri } from 'teleterm/ui/uri'; +import type { Cluster } from 'teleterm/services/tshd/types'; + +import type * as resourcesServiceTypes from 'teleterm/ui/services/resources'; +import type { SearchResultResource } from 'teleterm/ui/services/resources'; + +export { SearchResultResource }; + +type ResourceSearchResultBase< + Result extends resourcesServiceTypes.SearchResult +> = Result & { + labelMatches: LabelMatch[]; + resourceMatches: ResourceMatch[]; + score: number; +}; + +export type SearchResultServer = + ResourceSearchResultBase; +export type SearchResultDatabase = + ResourceSearchResultBase; +export type SearchResultKube = + ResourceSearchResultBase; +export type SearchResultCluster = { + kind: 'cluster-filter'; + resource: Cluster; + nameMatch: string; + score: number; +}; +export type SearchResultResourceType = { + kind: 'resource-type-filter'; + resource: 'kubes' | 'servers' | 'databases'; + nameMatch: string; + score: number; +}; + +// TODO(gzdunek): find a better name. +// `ResourcesService` exports `SearchResult` which is then usually imported as `ResourceSearchResult`. +// Having these two thing named almost the same is confusing. +export type ResourceSearchResult = + | SearchResultServer + | SearchResultDatabase + | SearchResultKube; + +export type FilterSearchResult = SearchResultResourceType | SearchResultCluster; + +export type SearchResult = ResourceSearchResult | FilterSearchResult; + +export type LabelMatch = { + kind: 'label-name' | 'label-value'; + labelName: string; + searchTerm: string; + // Individual score of this label match; how much it contributes to the total score. + score: number; +}; + +export type ResourceMatch = { + field: typeof searchableFields[Kind][number]; + searchTerm: string; +}; + +/** + * mainResourceName returns the main identifier for the given resource displayed in the UI. + */ +export function mainResourceName(searchResult: ResourceSearchResult): string { + return searchResult.resource[mainResourceField[searchResult.kind]]; +} + +export const mainResourceField: { + [Kind in ResourceSearchResult['kind']]: keyof SearchResultResource; +} = { + server: 'hostname', + database: 'name', + kube: 'name', +} as const; + +// The usage of Exclude here is a workaround to make sure that the fields in the array point only to +// fields of string type. +export const searchableFields: { + [Kind in ResourceSearchResult['kind']]: ReadonlyArray< + Exclude, 'labelsList'> + >; +} = { + server: ['name', 'hostname', 'addr'], + database: ['name', 'desc', 'protocol', 'type'], + kube: ['name'], +} as const; + +export interface ResourceTypeSearchFilter { + filter: 'resource-type'; + resourceType: 'kubes' | 'servers' | 'databases'; +} + +export interface ClusterSearchFilter { + filter: 'cluster'; + clusterUri: ClusterUri; +} + +export type SearchFilter = ResourceTypeSearchFilter | ClusterSearchFilter; diff --git a/web/packages/teleterm/src/ui/Search/searchResultTestHelpers.ts b/web/packages/teleterm/src/ui/Search/searchResultTestHelpers.ts new file mode 100644 index 0000000000000..af48315d466d1 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/searchResultTestHelpers.ts @@ -0,0 +1,63 @@ +/** + * 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. + */ + +import { ResourceSearchResult } from './searchResult'; + +import type * as tsh from 'teleterm/services/tshd/types'; + +export const makeServer = (props: Partial): tsh.Server => ({ + uri: '/clusters/teleport-local/servers/178ef081-259b-4aa5-a018-449b5ea7e694', + tunnel: false, + name: '178ef081-259b-4aa5-a018-449b5ea7e694', + hostname: 'foo', + addr: '127.0.0.1:3022', + labelsList: [], + ...props, +}); + +export const makeDatabase = (props: Partial): tsh.Database => ({ + uri: '/clusters/teleport-local/dbs/foo', + name: 'foo', + protocol: 'postgres', + type: 'self-hosted', + desc: '', + hostname: '', + addr: '', + labelsList: [], + ...props, +}); + +export const makeKube = (props: Partial): tsh.Kube => ({ + name: 'foo', + labelsList: [], + uri: '/clusters/bar/kubes/foo', + ...props, +}); + +export const makeLabelsList = (labels: Record): tsh.Label[] => + Object.entries(labels).map(([name, value]) => ({ name, value })); + +export const makeResourceResult = ( + props: Partial & { + kind: ResourceSearchResult['kind']; + resource: ResourceSearchResult['resource']; + } +): ResourceSearchResult => ({ + score: 0, + labelMatches: [], + resourceMatches: [], + ...props, +}); diff --git a/web/packages/teleterm/src/ui/Search/useSearch.test.ts b/web/packages/teleterm/src/ui/Search/useSearch.test.ts new file mode 100644 index 0000000000000..eb1a6fdfc46a3 --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/useSearch.test.ts @@ -0,0 +1,66 @@ +/** + * 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. + */ + +import { sortResults } from './useSearch'; +import { + makeResourceResult, + makeServer, + makeKube, + makeLabelsList, +} from './searchResultTestHelpers'; + +describe('sortResults', () => { + it('uses the displayed resource name as the tie breaker if the scores are equal', () => { + const server = makeResourceResult({ + kind: 'server', + resource: makeServer({ hostname: 'z' }), + }); + const kube = makeResourceResult({ + kind: 'kube', + resource: makeKube({ name: 'a' }), + }); + const sortedResults = sortResults([server, kube], ''); + + expect(sortedResults[0]).toEqual(kube); + expect(sortedResults[1]).toEqual(server); + }); + + it('saves individual label match scores', () => { + const server = makeResourceResult({ + kind: 'server', + resource: makeServer({ + labelsList: makeLabelsList({ quux: 'bar-baz', foo: 'bar' }), + }), + }); + + const { labelMatches } = sortResults([server], 'foo bar')[0]; + + labelMatches.forEach(match => { + expect(match.score).toBeGreaterThan(0); + }); + + const quuxMatches = labelMatches.filter( + match => match.labelName === 'quux' + ); + const quuxMatch = quuxMatches[0]; + const fooMatches = labelMatches.filter(match => match.labelName === 'foo'); + + expect(quuxMatches).toHaveLength(1); + expect(fooMatches).toHaveLength(2); + expect(fooMatches[0].score).toBeGreaterThan(quuxMatch.score); + expect(fooMatches[1].score).toBeGreaterThan(quuxMatch.score); + }); +}); diff --git a/web/packages/teleterm/src/ui/Search/useSearch.ts b/web/packages/teleterm/src/ui/Search/useSearch.ts new file mode 100644 index 0000000000000..41095239a972a --- /dev/null +++ b/web/packages/teleterm/src/ui/Search/useSearch.ts @@ -0,0 +1,312 @@ +/** + * 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. + */ + +import { useCallback } from 'react'; + +import { assertUnreachable } from 'teleterm/ui/utils'; +import { useAppContext } from 'teleterm/ui/appContextProvider'; + +import { + ClusterSearchFilter, + ResourceTypeSearchFilter, + SearchFilter, + LabelMatch, + mainResourceField, + mainResourceName, + ResourceMatch, + searchableFields, + ResourceSearchResult, + FilterSearchResult, +} from './searchResult'; + +import type * as resourcesServiceTypes from 'teleterm/ui/services/resources'; + +/** + * useResourceSearch returns a function which searches for the given list of space-separated keywords across + * all root and leaf clusters that the user is currently logged in to. + * + * It does so by issuing a separate request for each resource type to each cluster. It fails if any + * of those requests fail. + */ +export function useResourceSearch() { + const { clustersService, resourcesService } = useAppContext(); + clustersService.useState(); + + return useCallback( + async (search: string, restrictions: SearchFilter[]) => { + // useResourceSearch has to return _something_ when the input is empty. Imagine this scenario: + // + // 1. The user types in 'data' into the search bar. + // 2. The search bar immediately returns filters plus it starts a resource search for 'foo'. + // 3. The user selects one of the filters before the backend response comes back. + // + // The request for 'foo' that was in flight needs to be canceled somehow. We can do that by + // issuing another one for empty input and making `useResourceSearch` return an empty array + // in that scenario. + if (!search) { + return { results: [], search }; + } + + const clusterSearchFilter = restrictions.find( + s => s.filter === 'cluster' + ) as ClusterSearchFilter; + const resourceTypeSearchFilter = restrictions.find( + s => s.filter === 'resource-type' + ) as ResourceTypeSearchFilter; + + const connectedClusters = clustersService + .getClusters() + .filter(c => c.connected); + const clustersToSearch = clusterSearchFilter + ? connectedClusters.filter( + c => clusterSearchFilter.clusterUri === c.uri + ) + : connectedClusters; + + const searchPromises = clustersToSearch.map(cluster => + resourcesService.searchResources( + cluster.uri, + search, + resourceTypeSearchFilter + ) + ); + const results = (await Promise.all(searchPromises)).flat().slice(0, 10); + + return { results, search }; + }, + [clustersService, resourcesService] + ); +} + +/** + * `useFilterSearch` returns a function which searches for clusters or resource types, + * which are later used to narrow down the requests in `useResourceSearch`. + */ +export function useFilterSearch() { + const { clustersService, workspacesService } = useAppContext(); + clustersService.useState(); + workspacesService.useState(); + + return useCallback( + async ( + search: string, + restrictions: SearchFilter[] + ): Promise<{ results: FilterSearchResult[]; search: string }> => { + const getClusters = () => { + let clusters = clustersService.getClusters(); + if (search) { + clusters = clusters.filter(cluster => + cluster.name + .toLocaleLowerCase() + .includes(search.toLocaleLowerCase()) + ); + } + return clusters.map(cluster => { + let score = getLengthScore(search, cluster.name); + if ( + cluster.uri === + workspacesService.getActiveWorkspace()?.localClusterUri + ) { + // put the active cluster first (only when there is a match, otherwise score is 0) + score *= 3; + } + return { + kind: 'cluster-filter' as const, + resource: cluster, + nameMatch: search, + score, + }; + }); + }; + const getResourceType = () => { + let resourceTypes = [ + 'kubes' as const, + 'servers' as const, + 'databases' as const, + ]; + if (search) { + resourceTypes = resourceTypes.filter(resourceType => + resourceType.toLowerCase().includes(search.toLowerCase()) + ); + } + return resourceTypes.map(resourceType => ({ + kind: 'resource-type-filter' as const, + resource: resourceType, + nameMatch: search, + score: getLengthScore(search, resourceType), + })); + }; + + const shouldReturnClusters = !restrictions.some( + r => r.filter === 'cluster' + ); + const shouldReturnResourceTypes = !restrictions.some( + r => r.filter === 'resource-type' + ); + + const results = [ + shouldReturnResourceTypes && getResourceType(), + shouldReturnClusters && getClusters(), + ] + .filter(Boolean) + .flat() + .sort((a, b) => { + // Highest score first. + return b.score - a.score; + }); + + return { results, search }; + }, + [clustersService, workspacesService] + ); +} + +export function sortResults( + searchResults: resourcesServiceTypes.SearchResult[], + search: string +): ResourceSearchResult[] { + const terms = search + .split(' ') + .filter(Boolean) + // We have to match the implementation of the search algorithm as closely as possible. It uses + // strings.ToLower from Go which unfortunately doesn't have a good equivalent in JavaScript. + // + // strings.ToLower uses some kind of a universal map for lowercasing non-ASCII characters such + // as the Turkish İ. JavaScript doesn't have such a function, possibly because it's not possible + // to have universal case mapping. [1] + // + // The closest thing that JS has is toLocaleLowerCase. Since we don't know what locale the + // search string uses, we let the runtime figure it out based on the system settings. + // The assumption is that if someone has a resource with e.g. Turkish characters, their system + // is set to the appropriate locale and the search results will be properly scored. + // + // Highlighting will have problems with some non-ASCII characters anyway because the library we + // use for highlighting uses a regex with the i flag underneath. + // + // [1] https://web.archive.org/web/20190113111936/https://blogs.msdn.microsoft.com/oldnewthing/20030905-00/?p=42643 + .map(term => term.toLocaleLowerCase()); + const collator = new Intl.Collator(); + + return searchResults + .map(searchResult => calculateScore(populateMatches(searchResult, terms))) + .sort( + (a, b) => + // Highest score first. + b.score - a.score || + collator.compare(mainResourceName(a), mainResourceName(b)) + ); +} + +function populateMatches( + searchResult: resourcesServiceTypes.SearchResult, + terms: string[] +): ResourceSearchResult { + const labelMatches: LabelMatch[] = []; + const resourceMatches: ResourceMatch[] = []; + + terms.forEach(term => { + searchResult.resource.labelsList.forEach(label => { + // indexOf is faster on Chrome than includes or regex. + // https://jsbench.me/b7lf9kvrux/1 + const nameIndex = label.name.toLocaleLowerCase().indexOf(term); + const valueIndex = label.value.toLocaleLowerCase().indexOf(term); + + if (nameIndex >= 0) { + labelMatches.push({ + kind: 'label-name', + labelName: label.name, + searchTerm: term, + score: 0, + }); + } + + if (valueIndex >= 0) { + labelMatches.push({ + kind: 'label-value', + labelName: label.name, + searchTerm: term, + score: 0, + }); + } + }); + + searchableFields[searchResult.kind].forEach(field => { + // `String` here is just to satisfy the compiler. + const index = searchResult.resource[field] + .toLocaleLowerCase() + .indexOf(term); + + if (index >= 0) { + resourceMatches.push({ + field, + searchTerm: term, + }); + } + }); + }); + + return { ...searchResult, labelMatches, resourceMatches, score: 0 }; +} + +// TODO(ravicious): Extract the scoring logic to a function to better illustrate different weight +// for different matches. +function calculateScore( + searchResult: ResourceSearchResult +): ResourceSearchResult { + let searchResultScore = 0; + + const labelMatches = searchResult.labelMatches.map(match => { + const label = searchResult.resource.labelsList.find( + label => label.name === match.labelName + ); + let matchedValue: string; + + switch (match.kind) { + case 'label-name': { + matchedValue = label.name; + break; + } + case 'label-value': { + matchedValue = label.value; + break; + } + default: { + assertUnreachable(match.kind); + } + } + + const score = getLengthScore(match.searchTerm, matchedValue); + searchResultScore += score; + + return { ...match, score }; + }); + + for (const match of searchResult.resourceMatches) { + const { searchTerm } = match; + const field = searchResult.resource[match.field]; + const isMainField = mainResourceField[searchResult.kind] === match.field; + const weight = isMainField ? 4 : 2; + + const resourceMatchScore = getLengthScore(searchTerm, field) * weight; + searchResultScore += resourceMatchScore; + } + + return { ...searchResult, labelMatches, score: searchResultScore }; +} + +function getLengthScore(searchTerm: string, matchedValue: string): number { + return Math.floor((searchTerm.length / matchedValue.length) * 100); +} diff --git a/web/packages/teleterm/src/ui/TabHost/index.ts b/web/packages/teleterm/src/ui/TabHost/index.ts index 43d0949a98fd9..5fac6e39fc54a 100644 --- a/web/packages/teleterm/src/ui/TabHost/index.ts +++ b/web/packages/teleterm/src/ui/TabHost/index.ts @@ -15,3 +15,4 @@ limitations under the License. */ export * from './TabHost'; +export { useNewTabOpener } from './useNewTabOpener'; diff --git a/web/packages/teleterm/src/ui/TabHost/useNewTabOpener.ts b/web/packages/teleterm/src/ui/TabHost/useNewTabOpener.ts index 8f7c22dced86d..5623b0a0e118d 100644 --- a/web/packages/teleterm/src/ui/TabHost/useNewTabOpener.ts +++ b/web/packages/teleterm/src/ui/TabHost/useNewTabOpener.ts @@ -17,7 +17,7 @@ import { useCallback } from 'react'; import { DocumentsService } from 'teleterm/ui/services/workspacesService'; -import { ClusterUri } from 'teleterm/ui/uri'; +import { ClusterUri, routing } from 'teleterm/ui/uri'; export function useNewTabOpener({ documentsService, @@ -27,15 +27,29 @@ export function useNewTabOpener({ localClusterUri: ClusterUri; }) { const openClusterTab = useCallback(() => { - if (localClusterUri) { - const clusterDocument = documentsService.createClusterDocument({ - clusterUri: localClusterUri, - }); + if (!localClusterUri) { + return; + } + + const clusterDocument = documentsService.createClusterDocument({ + clusterUri: localClusterUri, + }); - documentsService.add(clusterDocument); - documentsService.open(clusterDocument.uri); + documentsService.add(clusterDocument); + documentsService.open(clusterDocument.uri); + }, [documentsService, localClusterUri]); + + const openTerminalTab = useCallback(() => { + if (!localClusterUri) { + return; } + + const { params } = routing.parseClusterUri(localClusterUri); + documentsService.openNewTerminal({ + rootClusterId: params.rootClusterId, + leafClusterId: params.leafClusterId, + }); }, [documentsService, localClusterUri]); - return { openClusterTab }; + return { openClusterTab, openTerminalTab }; } diff --git a/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.test.tsx b/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.test.tsx index 2960113e6c6ec..4270c4f70fea0 100644 --- a/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.test.tsx +++ b/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.test.tsx @@ -59,6 +59,7 @@ function getMockDocuments(): Document[] { targetUri: '/clusters/bar/dbs/foobar', targetName: 'foobar', targetUser: 'foo', + origin: 'resource_table', }, { kind: 'doc.gateway', @@ -68,6 +69,7 @@ function getMockDocuments(): Document[] { targetUri: '/clusters/bar/dbs/foobar', targetName: 'foobar', targetUser: 'bar', + origin: 'resource_table', }, { kind: 'doc.cluster', diff --git a/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.ts b/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.ts index 74f14b962d80c..c3c2da6c1b405 100644 --- a/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.ts +++ b/web/packages/teleterm/src/ui/TabHost/useTabShortcuts.ts @@ -31,20 +31,21 @@ export function useTabShortcuts({ documentsService: DocumentsService; localClusterUri: ClusterUri; }) { - const { openClusterTab } = useNewTabOpener({ + const { openClusterTab, openTerminalTab } = useNewTabOpener({ documentsService, localClusterUri, }); const tabsShortcuts = useMemo( - () => buildTabsShortcuts(documentsService, openClusterTab), - [documentsService, openClusterTab] + () => buildTabsShortcuts(documentsService, openClusterTab, openTerminalTab), + [documentsService, openClusterTab, openTerminalTab] ); useKeyboardShortcuts(tabsShortcuts); } function buildTabsShortcuts( documentService: DocumentsService, - openClusterTab: () => void + openClusterTab: () => void, + openTerminalTab: () => void ): KeyboardShortcutHandlers { const handleTabIndex = (index: number) => () => { const docs = documentService.getDocuments(); @@ -77,6 +78,7 @@ function buildTabsShortcuts( documentService.open(allDocuments[indexToOpen].uri); }; + return { tab1: handleTabIndex(0), tab2: handleTabIndex(1), @@ -91,5 +93,6 @@ function buildTabsShortcuts( previousTab: handleTabSwitch('previous'), nextTab: handleTabSwitch('next'), newTab: openClusterTab, + newTerminalTab: openTerminalTab, }; } diff --git a/web/packages/teleterm/src/ui/TopBar/Clusters/ClusterSelector/ClusterSelector.tsx b/web/packages/teleterm/src/ui/TopBar/Clusters/ClusterSelector/ClusterSelector.tsx index 2122b7302f137..739f3052bccf2 100644 --- a/web/packages/teleterm/src/ui/TopBar/Clusters/ClusterSelector/ClusterSelector.tsx +++ b/web/packages/teleterm/src/ui/TopBar/Clusters/ClusterSelector/ClusterSelector.tsx @@ -62,16 +62,16 @@ const Container = styled.button` background: inherit; color: inherit; font-family: inherit; - min-width: 0; - width: 100%; + flex: 1; + flex-shrink: 2; + min-width: calc(${props => props.theme.space[7]}px * 2); height: 100%; border: 0.5px ${props => props.theme.colors.action.disabledBackground} solid; border-radius: 4px; display: flex; - flex-grow: 1; justify-content: space-between; align-items: center; - padding: 0 12px; + padding: 0 ${props => props.theme.space[2]}px; opacity: ${props => (props.isClusterSelected ? 1 : 0.6)}; cursor: pointer; diff --git a/web/packages/teleterm/src/ui/TopBar/Connections/useConnections.ts b/web/packages/teleterm/src/ui/TopBar/Connections/useConnections.ts index 843ab007406c0..aee4443f8d0b2 100644 --- a/web/packages/teleterm/src/ui/TopBar/Connections/useConnections.ts +++ b/web/packages/teleterm/src/ui/TopBar/Connections/useConnections.ts @@ -56,7 +56,8 @@ export function useConnections() { return { isAnyConnectionActive: items.some(c => c.connected), removeItem: (id: string) => connectionTracker.removeItem(id), - activateItem: (id: string) => connectionTracker.activateItem(id), + activateItem: (id: string) => + connectionTracker.activateItem(id, { origin: 'connection_list' }), disconnectItem: (id: string) => connectionTracker.disconnectItem(id), updateSorting, items: getSortedItems(), diff --git a/web/packages/teleterm/src/ui/TopBar/MoreOptions.tsx b/web/packages/teleterm/src/ui/TopBar/MoreOptions.tsx new file mode 100644 index 0000000000000..b6a735970910b --- /dev/null +++ b/web/packages/teleterm/src/ui/TopBar/MoreOptions.tsx @@ -0,0 +1,240 @@ +/** + * 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. + */ + +import React, { useState, useRef } from 'react'; +import styled from 'styled-components'; + +import { Flex, Text, Popover } from 'design'; +import * as icons from 'design/Icon'; + +import { useAppContext } from 'teleterm/ui/appContextProvider'; +import { TopBarButton } from 'teleterm/ui/TopBar/TopBarButton'; +import { IAppContext } from 'teleterm/ui/types'; +import { Cluster } from 'teleterm/services/tshd/types'; +import { KeyboardShortcutAction } from 'teleterm/services/config'; +import { useKeyboardShortcutFormatters } from 'teleterm/ui/services/keyboardShortcuts'; +import { ListItem } from 'teleterm/ui/components/ListItem'; +import { useNewTabOpener } from 'teleterm/ui/TabHost'; + +type MenuItem = { + title: string; + isVisible: boolean; + Icon: React.ComponentType<{ fontSize: number }>; + onNavigate: () => void; + prependSeparator?: boolean; + keyboardShortcutAction?: KeyboardShortcutAction; +}; + +function useMenuItems(): MenuItem[] { + const ctx = useAppContext(); + const { + workspacesService, + mainProcessClient, + configService, + notificationsService, + } = ctx; + workspacesService.useState(); + ctx.clustersService.useState(); + const documentsService = + workspacesService.getActiveWorkspaceDocumentService(); + const activeRootCluster = getActiveRootCluster(ctx); + const { openTerminalTab } = useNewTabOpener({ + documentsService, + localClusterUri: workspacesService.getActiveWorkspace()?.localClusterUri, + }); + + const areAccessRequestsSupported = + !!activeRootCluster?.features?.advancedAccessWorkflows; + + const { platform } = mainProcessClient.getRuntimeSettings(); + const isDarwin = platform === 'darwin'; + const isSearchBarEnabled = configService.get('feature.searchBar').value; + + const menuItems: MenuItem[] = [ + { + title: 'Open config file', + isVisible: true, + Icon: icons.Config, + onNavigate: async () => { + const path = await mainProcessClient.openConfigFile(); + notificationsService.notifyInfo(`Opened the config file at ${path}.`); + }, + }, + { + title: 'Open new terminal', + isVisible: isSearchBarEnabled, + Icon: icons.Terminal, + keyboardShortcutAction: 'newTerminalTab', + onNavigate: openTerminalTab, + }, + { + title: 'Install tsh in PATH', + isVisible: isSearchBarEnabled && isDarwin, + Icon: icons.Link, + onNavigate: () => { + ctx.commandLauncher.executeCommand('tsh-install', undefined); + }, + }, + { + title: 'Remove tsh from PATH', + isVisible: isSearchBarEnabled && isDarwin, + Icon: icons.Unlink, + onNavigate: () => { + ctx.commandLauncher.executeCommand('tsh-uninstall', undefined); + }, + }, + { + title: 'New access request', + isVisible: areAccessRequestsSupported, + prependSeparator: true, + Icon: icons.Add, + onNavigate: () => { + const doc = documentsService.createAccessRequestDocument({ + clusterUri: activeRootCluster.uri, + state: 'creating', + title: 'New Access Request', + }); + documentsService.add(doc); + documentsService.open(doc.uri); + }, + }, + { + title: 'Review access requests', + isVisible: areAccessRequestsSupported, + Icon: icons.OpenBox, + onNavigate: () => { + const doc = documentsService.createAccessRequestDocument({ + clusterUri: activeRootCluster.uri, + state: 'browsing', + }); + documentsService.add(doc); + documentsService.open(doc.uri); + }, + }, + ]; + + return menuItems.filter(i => i.isVisible); +} + +function getActiveRootCluster(ctx: IAppContext): Cluster | undefined { + const clusterUri = ctx.workspacesService.getRootClusterUri(); + if (!clusterUri) { + return; + } + return ctx.clustersService.findCluster(clusterUri); +} + +export function MoreOptions() { + const [isPopoverOpened, setIsPopoverOpened] = useState(false); + const selectorRef = useRef(); + + const items = useMenuItems().map(item => { + return ( + + {item.prependSeparator && } + setIsPopoverOpened(false)} /> + + ); + }); + + return ( + <> + setIsPopoverOpened(true)} + > + + + setIsPopoverOpened(false)} + popoverCss={() => `max-width: min(560px, 90%)`} + > + {items} + + + ); +} + +const Menu = styled.menu` + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + min-width: 280px; + background: ${props => props.theme.colors.levels.surface}; +`; + +const Separator = styled.div` + background: ${props => props.theme.colors.levels.elevated}; + height: 1px; +`; + +function MenuItem({ + item, + closeMenu, +}: { + item: MenuItem; + closeMenu: () => void; +}) { + const { getAccelerator } = useKeyboardShortcutFormatters(); + const handleClick = () => { + item.onNavigate(); + closeMenu(); + }; + + return ( + + + + {item.title} + + {item.keyboardShortcutAction && ( + props.theme.space[1]}px + ${props => props.theme.space[1]}px; + `} + bg="primary.main" + > + {getAccelerator(item.keyboardShortcutAction)} + + )} + + + ); +} + +const StyledListItem = styled(ListItem)` + height: 38px; + gap: ${props => props.theme.space[3]}px; + padding: 0 ${props => props.theme.space[3]}px; + border-radius: 0; +`; diff --git a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationItem.tsx b/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationItem.tsx deleted file mode 100644 index 2674572f67e63..0000000000000 --- a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationItem.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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. - */ - -import React from 'react'; -import styled from 'styled-components'; - -import { Text } from 'design'; - -import { ListItem } from 'teleterm/ui/components/ListItem'; - -export function NavigationItem({ item, closeMenu }: NavigationItemProps) { - const handleClick = () => { - item.onNavigate(); - closeMenu(); - }; - - return ( - - - {item.title} - - ); -} - -const StyledListItem = styled(ListItem)` - height: 38px; - gap: 12px; - padding: 0 12px; - border-radius: 0; -`; - -type NavigationItemProps = { - item: { - title: string; - Icon: React.ComponentType<{ fontSize: number }>; - onNavigate: () => void; - }; - closeMenu: () => void; -}; diff --git a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx b/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx deleted file mode 100644 index f6cb70419f47f..0000000000000 --- a/web/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/** - * 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. - */ - -import React, { useState, useRef } from 'react'; -import styled from 'styled-components'; - -import Popover from 'design/Popover'; -import { MoreVert, OpenBox, Add, Config } from 'design/Icon'; - -import { useAppContext } from 'teleterm/ui/appContextProvider'; -import { TopBarButton } from 'teleterm/ui/TopBar/TopBarButton'; -import { IAppContext } from 'teleterm/ui/types'; -import { Cluster } from 'teleterm/services/tshd/types'; - -import { NavigationItem } from './NavigationItem'; - -function useNavigationItems(): ( - | { - title: string; - Icon: React.ComponentType<{ fontSize: number }>; - onNavigate: () => void; - } - | 'separator' -)[] { - const ctx = useAppContext(); - ctx.workspacesService.useState(); - ctx.clustersService.useState(); - - const documentsService = - ctx.workspacesService.getActiveWorkspaceDocumentService(); - const activeRootCluster = getActiveRootCluster(ctx); - const areAccessRequestsSupported = - !!activeRootCluster?.features?.advancedAccessWorkflows; - - return [ - { - title: 'Open Config File', - Icon: Config, - onNavigate: async () => { - const path = await ctx.mainProcessClient.openConfigFile(); - ctx.notificationsService.notifyInfo( - `Opened the config file at ${path}.` - ); - }, - }, - ...(areAccessRequestsSupported - ? [ - 'separator' as const, - { - title: 'New Access Request', - Icon: Add, - onNavigate: () => { - const doc = documentsService.createAccessRequestDocument({ - clusterUri: activeRootCluster.uri, - state: 'creating', - title: 'New Access Request', - }); - documentsService.add(doc); - documentsService.open(doc.uri); - }, - }, - { - title: 'Review Access Requests', - Icon: OpenBox, - onNavigate: () => { - const doc = documentsService.createAccessRequestDocument({ - clusterUri: activeRootCluster.uri, - state: 'browsing', - }); - documentsService.add(doc); - documentsService.open(doc.uri); - }, - }, - ] - : []), - ].filter(Boolean); -} - -function getActiveRootCluster(ctx: IAppContext): Cluster | undefined { - const clusterUri = ctx.workspacesService.getRootClusterUri(); - if (!clusterUri) { - return; - } - return ctx.clustersService.findCluster(clusterUri); -} - -export function NavigationMenu() { - const [isPopoverOpened, setIsPopoverOpened] = useState(false); - const selectorRef = useRef(); - - const items = useNavigationItems().map((item, index) => { - if (item === 'separator') { - return ; - } - return ( - setIsPopoverOpened(false)} - /> - ); - }); - - return ( - <> - setIsPopoverOpened(true)} - > - - - setIsPopoverOpened(false)} - popoverCss={() => `max-width: min(560px, 90%)`} - > - {items} - - - ); -} - -const Menu = styled.menu` - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - min-width: 280px; - background: ${props => props.theme.colors.levels.surface}; -`; - -const Separator = styled.div` - background: ${props => props.theme.colors.levels.elevated}; - height: 1px; -`; diff --git a/web/packages/teleterm/src/ui/TopBar/TopBar.tsx b/web/packages/teleterm/src/ui/TopBar/TopBar.tsx index 383ada9fa3a5d..92ddc1c671ca8 100644 --- a/web/packages/teleterm/src/ui/TopBar/TopBar.tsx +++ b/web/packages/teleterm/src/ui/TopBar/TopBar.tsx @@ -15,17 +15,22 @@ */ import React from 'react'; - import styled from 'styled-components'; +import { Flex } from 'design'; import QuickInput from '../QuickInput'; +import { SearchBar } from '../Search'; +import { useAppContext } from '../appContextProvider'; import { Connections } from './Connections'; import { Clusters } from './Clusters'; import { Identity } from './Identity'; -import { NavigationMenu } from './NavigationMenu'; +import { MoreOptions } from './MoreOptions'; export function TopBar() { + const { configService } = useAppContext(); + const isSearchBarEnabled = configService.get('feature.searchBar').value; + return ( @@ -33,35 +38,30 @@ export function TopBar() { - + {isSearchBarEnabled ? : } - + ); } -const Grid = styled.div` +const Grid = styled(Flex).attrs({ gap: 3, py: 2, px: 3 })` background: ${props => props.theme.colors.levels.surfaceSecondary}; - display: grid; - grid-template-columns: 1fr minmax(0, 700px) 1fr; width: 100%; - padding: 8px 16px; height: 56px; - box-sizing: border-box; align-items: center; + justify-content: space-between; `; -const CentralContainer = styled.div` - display: grid; - column-gap: 12px; - margin: auto 12px; - grid-auto-flow: column; - grid-auto-columns: 2fr 5fr; // 1fr for a single child, 2fr 5fr for two children +const CentralContainer = styled(Flex).attrs({ gap: 3 })` + flex: 1; align-items: center; + justify-content: center; height: 100%; + max-width: calc(${props => props.theme.space[10]}px * 9); `; const JustifyLeft = styled.div` diff --git a/web/packages/teleterm/src/ui/appContext.ts b/web/packages/teleterm/src/ui/appContext.ts index cffee7d02b573..ea1412bd06147 100644 --- a/web/packages/teleterm/src/ui/appContext.ts +++ b/web/packages/teleterm/src/ui/appContext.ts @@ -37,6 +37,7 @@ import { ReloginService } from 'teleterm/services/relogin'; import { TshdNotificationsService } from 'teleterm/services/tshdNotifications'; import { UsageService } from 'teleterm/ui/services/usage'; import { ResourcesService } from 'teleterm/ui/services/resources'; +import { ConfigService } from 'teleterm/services/config'; import { IAppContext } from 'teleterm/ui/types'; import { CommandLauncher } from './commandLauncher'; @@ -72,15 +73,17 @@ export default class AppContext implements IAppContext { reloginService: ReloginService; tshdNotificationsService: TshdNotificationsService; usageService: UsageService; + configService: ConfigService; constructor(config: ElectronGlobals) { const { tshClient, ptyServiceClient, mainProcessClient } = config; this.subscribeToTshdEvent = config.subscribeToTshdEvent; this.mainProcessClient = mainProcessClient; this.notificationsService = new NotificationsService(); + this.configService = this.mainProcessClient.configService; this.usageService = new UsageService( tshClient, - this.mainProcessClient.configService, + this.configService, this.notificationsService, clusterUri => this.clustersService.findCluster(clusterUri), mainProcessClient.getRuntimeSettings() @@ -110,7 +113,7 @@ export default class AppContext implements IAppContext { this.keyboardShortcutsService = new KeyboardShortcutsService( this.mainProcessClient.getRuntimeSettings().platform, - this.mainProcessClient.configService + this.configService ); this.commandLauncher = new CommandLauncher(this); diff --git a/web/packages/teleterm/src/ui/commandLauncher.ts b/web/packages/teleterm/src/ui/commandLauncher.ts index 301d4502bdf4f..d841caa56289e 100644 --- a/web/packages/teleterm/src/ui/commandLauncher.ts +++ b/web/packages/teleterm/src/ui/commandLauncher.ts @@ -18,25 +18,34 @@ import { IAppContext } from 'teleterm/ui/types'; import { ClusterUri, KubeUri, RootClusterUri, routing } from 'teleterm/ui/uri'; import { TrackedKubeConnection } from 'teleterm/ui/services/connectionTracker'; import { Platform } from 'teleterm/mainProcess/types'; +import { DocumentOrigin } from 'teleterm/ui/services/workspacesService'; const commands = { // For handling "tsh ssh" executed from the command bar. 'tsh-ssh': { displayName: '', description: '', - run( + async run( ctx: IAppContext, - args: { loginHost: string; localClusterUri: ClusterUri } + args: { + loginHost: string; + localClusterUri: ClusterUri; + origin: DocumentOrigin; + } ) { - const { loginHost, localClusterUri } = args; + const { loginHost, localClusterUri, origin } = args; const rootClusterUri = routing.ensureRootClusterUri(localClusterUri); const documentsService = ctx.workspacesService.getWorkspaceDocumentService(rootClusterUri); const doc = documentsService.createTshNodeDocumentFromLoginHost( localClusterUri, - loginHost + loginHost, + { origin } ); + + await ctx.workspacesService.setActiveWorkspace(rootClusterUri); + documentsService.add(doc); documentsService.setLocation(doc.uri); }, @@ -89,15 +98,23 @@ const commands = { 'kube-connect': { displayName: '', description: '', - run(ctx: IAppContext, args: { kubeUri: KubeUri }) { + async run( + ctx: IAppContext, + args: { kubeUri: KubeUri; origin: DocumentOrigin } + ) { + const rootClusterUri = routing.ensureRootClusterUri(args.kubeUri); const documentsService = - ctx.workspacesService.getActiveWorkspaceDocumentService(); + ctx.workspacesService.getWorkspaceDocumentService(rootClusterUri); const kubeDoc = documentsService.createTshKubeDocument({ kubeUri: args.kubeUri, + origin: args.origin, }); const connection = ctx.connectionTracker.findConnectionByDocument( kubeDoc ) as TrackedKubeConnection; + + await ctx.workspacesService.setActiveWorkspace(rootClusterUri); + documentsService.add({ ...kubeDoc, kubeConfigRelativePath: @@ -194,6 +211,7 @@ export class CommandLauncher { executeCommand(name: T, args: CommandArgs) { commands[name].run(this.appContext, args as any); + return undefined; } getAutocompleteCommands() { diff --git a/web/packages/teleterm/src/ui/services/clusters/clustersService.ts b/web/packages/teleterm/src/ui/services/clusters/clustersService.ts index e7760a6db96fd..24bcddc01bb88 100644 --- a/web/packages/teleterm/src/ui/services/clusters/clustersService.ts +++ b/web/packages/teleterm/src/ui/services/clusters/clustersService.ts @@ -29,7 +29,6 @@ import { CreateAccessRequestParams, GetRequestableRolesParams, ReviewAccessRequestParams, - ServerSideParams, } from 'teleterm/services/tshd/types'; import { MainProcessClient } from 'teleterm/mainProcess/types'; import { UsageService } from 'teleterm/ui/services/usage'; @@ -196,6 +195,8 @@ export class ClustersService extends ImmutableStore async getRequestableRoles(params: GetRequestableRolesParams) { const cluster = this.state.clusters.get(params.rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. This check should be done earlier in the + // UI rather than be repeated in each ClustersService method. if (!cluster.connected) { return; } @@ -205,6 +206,7 @@ export class ClustersService extends ImmutableStore getAssumedRequests(rootClusterUri: uri.RootClusterUri) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster?.connected) { return {}; } @@ -218,6 +220,7 @@ export class ClustersService extends ImmutableStore async getAccessRequests(rootClusterUri: uri.RootClusterUri) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -230,6 +233,7 @@ export class ClustersService extends ImmutableStore requestId: string ) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -242,6 +246,7 @@ export class ClustersService extends ImmutableStore dropIds: string[] ) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -255,6 +260,7 @@ export class ClustersService extends ImmutableStore requestId: string ) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -267,6 +273,7 @@ export class ClustersService extends ImmutableStore params: ReviewAccessRequestParams ) { const cluster = this.state.clusters.get(rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -281,6 +288,7 @@ export class ClustersService extends ImmutableStore async createAccessRequest(params: CreateAccessRequestParams) { const cluster = this.state.clusters.get(params.rootClusterUri); + // TODO(ravicious): Remove check for cluster.connected. See the comment in getRequestableRoles. if (!cluster.connected) { return; } @@ -320,7 +328,6 @@ export class ClustersService extends ImmutableStore async createGateway(params: tsh.CreateGatewayParams) { const gateway = await this.client.createGateway(params); - this.usageService.captureProtocolUse(params.targetUri, 'db'); this.setState(draft => { draft.gateways.set(gateway.uri, gateway); }); @@ -426,16 +433,6 @@ export class ClustersService extends ImmutableStore return this.getClusters().filter(c => !c.leaf); } - // TODO(ravicious): Use ResourcesService instead. - async fetchKubes(params: ServerSideParams) { - return await this.client.getKubes(params); - } - - // TODO(ravicious): Move to ResourceService. - async getDbUsers(dbUri: uri.DatabaseUri): Promise { - return await this.client.listDatabaseUsers(dbUri); - } - async removeClusterKubeConfigs(clusterUri: string): Promise { const { params: { rootClusterId }, diff --git a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.test.ts b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.test.ts index 135ae9d2d0748..48b4daa1de545 100644 --- a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.test.ts +++ b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.test.ts @@ -105,6 +105,7 @@ it('updates the port of a gateway connection when the underlying doc gets update targetName: 'test', targetSubresourceName: 'pg', port: '12345', + origin: 'resource_table', }; const { connectionTrackerService, workspacesService } = @@ -142,6 +143,7 @@ it('creates a connection for doc.terminal_tsh_node docs with serverUri', () => { rootClusterId: 'localhost', leafClusterId: undefined, login: 'user', + origin: 'resource_table', }; const { connectionTrackerService } = getTestSetupWithMockedDocuments([ @@ -170,6 +172,7 @@ it('ignores doc.terminal_tsh_node docs with no serverUri', () => { loginHost: 'user@foo', rootClusterId: 'test', leafClusterId: undefined, + origin: 'resource_table', }; const { connectionTrackerService } = getTestSetupWithMockedDocuments([ diff --git a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts index 358cb20d79cc3..3343447d78947 100644 --- a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts +++ b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts @@ -19,6 +19,7 @@ import { useStore } from 'shared/libs/stores'; import { ClustersService } from 'teleterm/ui/services/clusters'; import { Document, + DocumentOrigin, isDocumentTshNodeWithLoginHost, WorkspacesService, } from 'teleterm/ui/services/workspacesService'; @@ -81,7 +82,10 @@ export class ConnectionTrackerService extends ImmutableStore { + async activateItem( + id: string, + params: { origin: DocumentOrigin } + ): Promise { const connection = this.state.connections.find(c => c.id === id); const { rootClusterUri, activate } = this._trackedConnectionOperationsFactory.create(connection); @@ -89,7 +93,7 @@ export class ConnectionTrackerService extends ImmutableStore { + activate: params => { let srvDoc = documentsService .getDocuments() .find(getServerDocumentByConnection(connection)); if (!srvDoc) { - srvDoc = documentsService.createTshNodeDocument(connection.serverUri); + srvDoc = documentsService.createTshNodeDocument( + connection.serverUri, + params + ); srvDoc.status = 'connecting'; srvDoc.login = connection.login; srvDoc.title = connection.title; @@ -108,7 +114,7 @@ export class TrackedConnectionOperationsFactory { return { rootClusterUri, leafClusterUri, - activate: () => { + activate: params => { let gwDoc = documentsService .getDocuments() .find(getGatewayDocumentByConnection(connection)); @@ -122,6 +128,7 @@ export class TrackedConnectionOperationsFactory { title: connection.title, gatewayUri: connection.gatewayUri, port: connection.port, + origin: params.origin, }); documentsService.add(gwDoc); @@ -161,7 +168,7 @@ export class TrackedConnectionOperationsFactory { return { rootClusterUri, leafClusterUri, - activate: () => { + activate: params => { let kubeConn = documentsService .getDocuments() .find(getKubeDocumentByConnection(connection)); @@ -170,6 +177,7 @@ export class TrackedConnectionOperationsFactory { kubeConn = documentsService.createTshKubeDocument({ kubeUri: connection.kubeUri, kubeConfigRelativePath: connection.kubeConfigRelativePath, + origin: params.origin, }); documentsService.add(kubeConn); @@ -215,7 +223,7 @@ interface TrackedConnectionOperations { rootClusterUri: RootClusterUri; leafClusterUri: LeafClusterUri; - activate(): void; + activate(params: { origin: DocumentOrigin }): void; disconnect(): Promise; diff --git a/web/packages/teleterm/src/ui/services/keyboardShortcuts/keyboardShortcutsService.ts b/web/packages/teleterm/src/ui/services/keyboardShortcuts/keyboardShortcutsService.ts index 7eb300c67a961..9bc53945f83fd 100644 --- a/web/packages/teleterm/src/ui/services/keyboardShortcuts/keyboardShortcutsService.ts +++ b/web/packages/teleterm/src/ui/services/keyboardShortcuts/keyboardShortcutsService.ts @@ -57,6 +57,7 @@ export class KeyboardShortcutsService { previousTab: this.configService.get('keymap.previousTab').value, nextTab: this.configService.get('keymap.nextTab').value, newTab: this.configService.get('keymap.newTab').value, + newTerminalTab: this.configService.get('keymap.newTerminalTab').value, openCommandBar: this.configService.get('keymap.openCommandBar').value, openConnections: this.configService.get('keymap.openConnections').value, openClusters: this.configService.get('keymap.openClusters').value, diff --git a/web/packages/teleterm/src/ui/services/quickInput/suggesters.ts b/web/packages/teleterm/src/ui/services/quickInput/suggesters.ts index 4c22e93c01922..43ab423bc89fb 100644 --- a/web/packages/teleterm/src/ui/services/quickInput/suggesters.ts +++ b/web/packages/teleterm/src/ui/services/quickInput/suggesters.ts @@ -83,6 +83,7 @@ export class QuickServerSuggester clusterUri: localClusterUri, search: input, limit, + sort: { fieldName: 'hostname', dir: 'ASC' }, }); return servers.agentsList.map(server => ({ @@ -111,6 +112,7 @@ export class QuickDatabaseSuggester clusterUri: localClusterUri, search: input, limit, + sort: { fieldName: 'name', dir: 'ASC' }, }); return databases.agentsList.map(database => ({ diff --git a/web/packages/teleterm/src/ui/services/resources/resourcesService.test.ts b/web/packages/teleterm/src/ui/services/resources/resourcesService.test.ts index ab27d190fcb14..64bda031c21d4 100644 --- a/web/packages/teleterm/src/ui/services/resources/resourcesService.test.ts +++ b/web/packages/teleterm/src/ui/services/resources/resourcesService.test.ts @@ -86,6 +86,7 @@ test.each(getServerByHostnameTests)( clusterUri: '/clusters/bar', query: 'name == "foo"', limit: 2, + sort: null, }); } ); diff --git a/web/packages/teleterm/src/ui/services/resources/resourcesService.ts b/web/packages/teleterm/src/ui/services/resources/resourcesService.ts index c2ac329b8a611..2e8a62ffecdeb 100644 --- a/web/packages/teleterm/src/ui/services/resources/resourcesService.ts +++ b/web/packages/teleterm/src/ui/services/resources/resourcesService.ts @@ -14,16 +14,20 @@ * limitations under the License. */ +import type { ResourceTypeSearchFilter } from 'teleterm/ui/Search/searchResult'; + import type * as types from 'teleterm/services/tshd/types'; import type * as uri from 'teleterm/ui/uri'; export class ResourcesService { constructor(private tshClient: types.TshClient) {} - fetchServers(params: types.ServerSideParams) { + fetchServers(params: types.GetResourcesParams) { return this.tshClient.getServers(params); } + // TODO(ravicious): Refactor it to use logic similar to that in the Web UI. + // https://github.com/gravitational/teleport/blob/2a2b08dbfdaf71706a5af3812d3a7ec843d099b4/lib/web/apiserver.go#L2471 async getServerByHostname( clusterUri: uri.ClusterUri, hostname: string @@ -33,6 +37,7 @@ export class ResourcesService { clusterUri, query, limit: 2, + sort: null, }); if (servers.length > 1) { @@ -42,13 +47,67 @@ export class ResourcesService { return servers[0]; } - fetchDatabases(params: types.ServerSideParams) { + fetchDatabases(params: types.GetResourcesParams) { return this.tshClient.getDatabases(params); } - fetchKubes(params: types.ServerSideParams) { + fetchKubes(params: types.GetResourcesParams) { return this.tshClient.getKubes(params); } + + async getDbUsers(dbUri: uri.DatabaseUri): Promise { + return await this.tshClient.listDatabaseUsers(dbUri); + } + + /** + * searchResources searches for the given list of space-separated keywords across all resource + * types on the given cluster. + * + * It does so by issuing a separate request for each resource type. It fails if any of those + * requests fail. + * + * The results need to be wrapped in SearchResult because if we returned raw types (Server, + * Database, Kube) then there would be no easy way to differentiate between them on type level. + */ + async searchResources( + clusterUri: uri.ClusterUri, + search: string, + searchFilter: ResourceTypeSearchFilter | undefined + ): Promise { + const params = { search, clusterUri, sort: null, limit: 100 }; + + const getServers = () => + this.fetchServers(params).then(res => + res.agentsList.map(resource => ({ + kind: 'server' as const, + resource, + })) + ); + const getDatabases = () => + this.fetchDatabases(params).then(res => + res.agentsList.map(resource => ({ + kind: 'database' as const, + resource, + })) + ); + const getKubes = () => + this.fetchKubes(params).then(res => + res.agentsList.map(resource => ({ + kind: 'kube' as const, + resource, + })) + ); + + const promises = searchFilter + ? [ + searchFilter.resourceType === 'servers' && getServers(), + searchFilter.resourceType === 'databases' && getDatabases(), + searchFilter.resourceType === 'kubes' && getKubes(), + ].filter(Boolean) + : [getServers(), getDatabases(), getKubes()]; + + return (await Promise.all(promises)).flat(); + } } export class AmbiguousHostnameError extends Error { @@ -57,3 +116,24 @@ export class AmbiguousHostnameError extends Error { this.name = 'AmbiguousHostname'; } } + +export type SearchResultServer = { kind: 'server'; resource: types.Server }; +export type SearchResultDatabase = { + kind: 'database'; + resource: types.Database; +}; +export type SearchResultKube = { kind: 'kube'; resource: types.Kube }; + +export type SearchResult = + | SearchResultServer + | SearchResultDatabase + | SearchResultKube; + +export type SearchResultResource = + Kind extends 'server' + ? SearchResultServer['resource'] + : Kind extends 'database' + ? SearchResultDatabase['resource'] + : Kind extends 'kube' + ? SearchResultKube['resource'] + : never; diff --git a/web/packages/teleterm/src/ui/services/usage/usageService.ts b/web/packages/teleterm/src/ui/services/usage/usageService.ts index 03dc0a863482d..121f02c4730e3 100644 --- a/web/packages/teleterm/src/ui/services/usage/usageService.ts +++ b/web/packages/teleterm/src/ui/services/usage/usageService.ts @@ -25,6 +25,7 @@ import { ConfigService } from 'teleterm/services/config'; import Logger from 'teleterm/logger'; import { staticConfig } from 'teleterm/staticConfig'; import { NotificationsService } from 'teleterm/ui/services/notifications'; +import { DocumentOrigin } from 'teleterm/ui/services/workspacesService'; type PrehogEventReq = Omit< ReportUsageEventRequest['prehogReq'], @@ -70,7 +71,8 @@ export class UsageService { captureProtocolUse( uri: ClusterOrResourceUri, - protocol: 'ssh' | 'kube' | 'db' + protocol: 'ssh' | 'kube' | 'db', + origin: DocumentOrigin ): void { const clusterProperties = this.getClusterProperties(uri); if (!clusterProperties) { @@ -84,6 +86,7 @@ export class UsageService { clusterName: clusterProperties.clusterName, userName: clusterProperties.userName, protocol, + origin, }, }); } diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.test.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.test.ts index 8b9bd2c2a6ac9..f9d7a0f1030a7 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.test.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.test.ts @@ -77,6 +77,7 @@ describe('document should be added', () => { targetUri: '/clusters/bar/dbs/quux', targetName: 'quux', targetUser: 'foo', + origin: 'resource_table', }; test('at the specific position', () => { @@ -131,6 +132,7 @@ test('only TSH node documents should be returned', () => { status: 'connecting', rootClusterId: '', leafClusterId: undefined, + origin: 'resource_table', }; service.add(tshNodeDocument); @@ -150,6 +152,7 @@ test('only gateway documents should be returned', () => { targetUri: '/clusters/bar/dbs/quux', targetName: 'quux', targetUser: 'foo', + origin: 'resource_table', }; service.add(gatewayDocument); diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.ts index 2831c33558327..ab809037df5f3 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/documentsService.ts @@ -33,6 +33,7 @@ import { DocumentAccessRequests, DocumentCluster, DocumentGateway, + DocumentOrigin, DocumentTshKube, DocumentTshNode, DocumentTshNodeWithLoginHost, @@ -104,23 +105,28 @@ export class DocumentsService { options.kubeConfigRelativePath || `${params.rootClusterId}/${params.kubeId}-${unique(5)}`, title: params.kubeId, + origin: options.origin, }; } - createTshNodeDocument(serverUri: ServerUri): DocumentTshNodeWithServerId { - const { params } = routing.parseServerUri(serverUri); + createTshNodeDocument( + serverUri: ServerUri, + params: { origin: DocumentOrigin } + ): DocumentTshNodeWithServerId { + const { params: routingParams } = routing.parseServerUri(serverUri); const uri = routing.getDocUri({ docId: unique() }); return { uri, kind: 'doc.terminal_tsh_node', status: 'connecting', - rootClusterId: params.rootClusterId, - leafClusterId: params.leafClusterId, - serverId: params.serverId, + rootClusterId: routingParams.rootClusterId, + leafClusterId: routingParams.leafClusterId, + serverId: routingParams.serverId, serverUri, title: '', login: '', + origin: params.origin, }; } @@ -132,12 +138,15 @@ export class DocumentsService { * the command will succeed only if the given cluster has only a single server with the hostname * matching `host`. * @param loginHost - the "user@host" pair. + * @param params - additional parameters. + * @param params.origin - where the document was opened from. */ createTshNodeDocumentFromLoginHost( clusterUri: ClusterUri, - loginHost: string + loginHost: string, + params: { origin: DocumentOrigin } ): DocumentTshNodeWithLoginHost { - const { params } = routing.parseClusterUri(clusterUri); + const { params: routingParams } = routing.parseClusterUri(clusterUri); const uri = routing.getDocUri({ docId: unique() }); return { @@ -145,9 +154,10 @@ export class DocumentsService { kind: 'doc.terminal_tsh_node', title: loginHost, status: 'connecting', - rootClusterId: params.rootClusterId, - leafClusterId: params.leafClusterId, + rootClusterId: routingParams.rootClusterId, + leafClusterId: routingParams.leafClusterId, loginHost, + origin: params.origin, }; } @@ -162,6 +172,7 @@ export class DocumentsService { targetSubresourceName, port, gatewayUri, + origin, } = opts; const uri = routing.getDocUri({ docId: unique() }); const title = `${targetUser}@${targetName}`; @@ -176,6 +187,7 @@ export class DocumentsService { gatewayUri, title, port, + origin, }; } diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.test.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.test.ts index 82e61d7cf390d..971082a365777 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.test.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.test.ts @@ -31,6 +31,7 @@ const docWithServerId: DocumentTshNodeWithServerId = { rootClusterId: 'test', leafClusterId: undefined, login: 'user', + origin: 'resource_table', }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const { serverId, serverUri, login, ...rest } = docWithServerId; diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts index a7c7f625a1699..a28ed57ba62e3 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts @@ -25,6 +25,12 @@ export type Kind = | 'doc.terminal_tsh_node' | 'doc.terminal_tsh_kube'; +export type DocumentOrigin = + | 'resource_table' + | 'search_bar' + | 'connection_list' + | 'reopened_session'; + interface DocumentBase { uri: uri.DocumentUri; title: string; @@ -45,6 +51,7 @@ interface DocumentTshNodeBase extends DocumentBase { status: '' | 'connecting' | 'connected' | 'error'; rootClusterId: string; leafClusterId: string | undefined; + origin: DocumentOrigin; } export interface DocumentTshNodeWithServerId extends DocumentTshNodeBase { @@ -83,6 +90,7 @@ export interface DocumentTshKube extends DocumentBase { kubeConfigRelativePath: string; rootClusterId: string; leafClusterId?: string; + origin: DocumentOrigin; } export interface DocumentGateway extends DocumentBase { @@ -93,6 +101,7 @@ export interface DocumentGateway extends DocumentBase { targetName: string; targetSubresourceName?: string; port?: string; + origin: DocumentOrigin; } export interface DocumentCluster extends DocumentBase { @@ -151,6 +160,7 @@ export type CreateGatewayDocumentOpts = { targetSubresourceName?: string; title?: string; port?: string; + origin: DocumentOrigin; }; export type CreateClusterDocumentOpts = { @@ -160,6 +170,7 @@ export type CreateClusterDocumentOpts = { export type CreateTshKubeDocumentOptions = { kubeUri: uri.KubeUri; kubeConfigRelativePath?: string; + origin: DocumentOrigin; }; export type CreateAccessRequestDocumentOpts = { diff --git a/web/packages/teleterm/src/ui/services/workspacesService/workspacesService.ts b/web/packages/teleterm/src/ui/services/workspacesService/workspacesService.ts index 1d0262de35afa..e6d5b143aebad 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/workspacesService.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/workspacesService.ts @@ -316,6 +316,14 @@ export class WorkspacesService extends ImmutableStore { return { ...d, status: 'connecting', + origin: 'reopened_session', + }; + } + + if (d.kind === 'doc.gateway') { + return { + ...d, + origin: 'reopened_session', }; } return d; diff --git a/web/packages/teleterm/src/ui/types.ts b/web/packages/teleterm/src/ui/types.ts index a1ac23bc08a9f..f1bac8b6f9ab1 100644 --- a/web/packages/teleterm/src/ui/types.ts +++ b/web/packages/teleterm/src/ui/types.ts @@ -30,6 +30,7 @@ import { ResourcesService } from 'teleterm/ui/services/resources'; import { ReloginService } from 'teleterm/services/relogin'; import { TshdNotificationsService } from 'teleterm/services/tshdNotifications'; import { UsageService } from 'teleterm/ui/services/usage'; +import { ConfigService } from 'teleterm/services/config'; export interface IAppContext { clustersService: ClustersService; @@ -49,6 +50,7 @@ export interface IAppContext { reloginService: ReloginService; tshdNotificationsService: TshdNotificationsService; usageService: UsageService; + configService: ConfigService; init(): Promise; } diff --git a/web/packages/teleterm/src/ui/uri.ts b/web/packages/teleterm/src/ui/uri.ts index 1e9b4fe771b99..8411e49a5ce72 100644 --- a/web/packages/teleterm/src/ui/uri.ts +++ b/web/packages/teleterm/src/ui/uri.ts @@ -109,6 +109,13 @@ export const routing = { return matchPath(path, route); }, + /** + * parseClusterName should be used only when getting the cluster object from ClustersService is + * not possible. + * + * rootClusterId in the URI is not the name of the cluster but rather just the hostname of the + * proxy. These two might be different. + */ parseClusterName(clusterUri: string) { const parsed = routing.parseClusterUri(clusterUri); if (!parsed) { diff --git a/yarn.lock b/yarn.lock index 957465effec8a..bc1cdd32a22dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8426,6 +8426,11 @@ headers-polyfill@^3.1.0: resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.1.2.tgz#9a4dcb545c5b95d9569592ef7ec0708aab763fbe" integrity sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA== +highlight-words-core@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa" + integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"