From 3c95df4434e94c54174bd0568ed5ff203dbf1f42 Mon Sep 17 00:00:00 2001 From: matheus Date: Wed, 5 Jun 2024 09:20:56 -0300 Subject: [PATCH 01/33] Fix EAS CTA condition (#42329) --- .../ExternalAuditStorageCta.test.tsx | 6 ++---- .../ExternalAuditStorageCta/ExternalAuditStorageCta.tsx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.test.tsx b/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.test.tsx index 2fbe0a0be1703..2a6055ce46aa3 100644 --- a/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.test.tsx +++ b/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { MemoryRouter } from 'react-router'; import { render, screen } from 'design/utils/testing'; -import { createTeleportContext } from 'teleport/mocks/contexts'; +import { createTeleportContext, getAcl } from 'teleport/mocks/contexts'; import { ContextProvider } from 'teleport/index'; import cfg from 'teleport/config'; @@ -28,8 +28,6 @@ import { clusters } from 'teleport/Clusters/fixtures'; import { storageService } from 'teleport/services/storageService'; -import { getAcl } from 'teleport/mocks/contexts'; - import { ExternalAuditStorageCta } from './ExternalAuditStorageCta'; describe('externalAuditStorageCta', () => { @@ -51,7 +49,7 @@ describe('externalAuditStorageCta', () => { }); cfg.isCloud = isCloud; - cfg.externalAuditStorage = lockedFeature; + cfg.externalAuditStorage = !lockedFeature; jest .spyOn(storageService, 'getExternalAuditStorageCtaDisabled') diff --git a/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.tsx b/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.tsx index d5ce99e151135..ab3f050452409 100644 --- a/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.tsx +++ b/web/packages/teleport/src/components/ExternalAuditStorageCta/ExternalAuditStorageCta.tsx @@ -40,7 +40,7 @@ import { ButtonLockedFeature } from '../ButtonLockedFeature'; export const ExternalAuditStorageCta = () => { const [showCta, setShowCta] = useState(false); const ctx = useTeleport(); - const featureEnabled = !cfg.externalAuditStorage; + const featureEnabled = cfg.externalAuditStorage; const userHasAccess = ctx.getFeatureFlags().enrollIntegrationsOrPlugins; useEffect(() => { @@ -49,7 +49,7 @@ export const ExternalAuditStorageCta = () => { cfg.isCloud && !storageService.getExternalAuditStorageCtaDisabled() ); - }, [cfg.isCloud]); + }, [ctx.hasExternalAuditStorage]); function handleDismiss() { storageService.disableExternalAuditStorageCta(); From 0342787d95e76911268d9c068f93ad6c9ad70023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Smoli=C5=84ski?= Date: Wed, 5 Jun 2024 16:03:18 +0200 Subject: [PATCH 02/33] docs: OKTA SCIM only integration (#42072) Co-authored-by: Trent Clarke --- docs/cspell.json | 1 + .../okta/scim-provision-existing-users.png | Bin 0 -> 133538 bytes docs/pages/application-access/okta.mdx | 1 + .../application-access/okta/scim-only.mdx | 145 ++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 docs/img/enterprise/plugins/okta/scim-provision-existing-users.png create mode 100644 docs/pages/application-access/okta/scim-only.mdx diff --git a/docs/cspell.json b/docs/cspell.json index 3e6495c0c4e09..58f9dfc5163b2 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -272,6 +272,7 @@ "apimachinery", "apiserver", "appdomain", + "appgroup", "appresources", "appuser", "argoproj", diff --git a/docs/img/enterprise/plugins/okta/scim-provision-existing-users.png b/docs/img/enterprise/plugins/okta/scim-provision-existing-users.png new file mode 100644 index 0000000000000000000000000000000000000000..4cfc21209d33c818fa0f6b611cf554d30dee274d GIT binary patch literal 133538 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 literal 0 HcmV?d00001 diff --git a/docs/pages/application-access/okta.mdx b/docs/pages/application-access/okta.mdx index e7f92c92930ea..6fae905c66b02 100644 --- a/docs/pages/application-access/okta.mdx +++ b/docs/pages/application-access/okta.mdx @@ -7,5 +7,6 @@ layout: tocless-doc Configure Teleport to import and grant access to Okta applications and user groups. - [Configuring Okta integration](./okta/hosted-guide.mdx): A guide for connecting Okta organization to Teleport. +- [Setting up a SCIM-only integration](./okta/scim-only.mdx): A guide for setting up a SCIM-only Okta integration in Teleport. - [Resource Synchronization](./okta/sync-scim.mdx): How synchronized resources are represented in Teleport. - [Reference](./okta/reference.mdx): A reference for the Okta integration resources. diff --git a/docs/pages/application-access/okta/scim-only.mdx b/docs/pages/application-access/okta/scim-only.mdx new file mode 100644 index 0000000000000..41041b80015b8 --- /dev/null +++ b/docs/pages/application-access/okta/scim-only.mdx @@ -0,0 +1,145 @@ +--- +title: Installing the SCIM-Only Okta Integration +description: How to install a SCIM-only Okta integration +--- + +The SCIM (System for Cross-domain Identity Management) integration enables automated user management, +ensuring that user accounts in Teleport are synchronized with the corresponding Okta user profiles. +This integration streamlines the onboarding and offboarding process by automatically creating, updating, and deleting +Teleport user accounts in response to changes within the Okta organization. +SCIM Teleport integration allows immediate locking of users in Teleport when they are deprovisioned in Okta, +stopping all ongoing user Teleport sessions to maintain security and compliance. + +## How it works +User provisioning (and de-provisioning) with SCIM requires two Teleport +components working together: + + - A SAML Connector that provides SSO login to Teleport for upstream Okta users + - A Teleport SCIM plugin integration that provisions and de-provisions Teleport user accounts + in response to changes in the upstream Okta organization + +Both of these Teleport components rely on an Okta SAML application to act as the +interface between Teleport and Okta. For consistency, both of +the Teleport components must use the same Okta application. + +When a user is assigned to the Okta app, either directly or via group +membership, a corresponding Teleport user account will be created. If the Okta +user already has a valid temporary Teleport SAML user account (i.e. they have +logged into the cluster via SAML SSO before SCIM provisioning was enabled), the +temporary account will automatically be adopted by the SAML integration and +promoted to a long-lived SCIM-managed account. + + +Currently none of the SCIM user profile traits are stored in the Teleport user, although this may change in future. + + +When a user is unassigned from the Okta app, or is deactivated by the Okta admin, Teleport will immediately delete the user in question, and create a lock that will both immediately terminate any existing sessions and prevent that user from re-using any credentials that have already been issued. This lock will be automatically revoked if the user is subsequently re-provisioned via SCIM, otherwise the lock is permanent and will have to be explicitly deleted to remove. + + +Okta does not send SCIM updates to Teleport when a user is merely suspended. Even though Okta will prevent a suspended user from logging back into the cluster, any existing sessions will not be terminated, and any pre-issued credentials will be valid for their normal lifetimes. + + +## Prerequisites + +(!docs/pages/includes/commercial-prereqs-tabs.mdx!) +- [Authentication With Okta as an SSO Provider](../../access-controls/sso/okta.mdx) + + +## Step 1/2. Installing the Teleport SCIM integration + +Teleport supports two SCIM integration modes: + - **Without API token** - User traits to role mapping will be propagated when the user logs in to Teleport. + As a side effect, user roles will be visible and updated when users log in to Teleport. + - **With API token** - User traits to role mapping will be propagated immediately during SCIM user provisioning. + +Run the following `tctl` command to install the SCIM integration. + + + + +```bash +$ tctl plugins install okta \ + --org https://trial-12356.okta.com \ + --saml-connector "${SAML_CONNECTOR_NAME}" \ + --no-users-sync \ + --no-accesslist-sync \ + --no-appgroup-sync \ + --scim +Successfully created OKTA plugin "okta" + +SCIM Base URL: https://teleport.example.com:443/v1/webapi/scim/okta +SCIM Identifier field for users: userName +SCIM Bearer Token: 1234567891234567891234567890 +``` + + + + +```bash +$ tctl plugins install okta \ + --org https://trial-12356.okta.com \ + --saml-connector "${SAML_CONNECTOR_NAME}" \ + --no-users-sync \ + --no-accesslist-sync \ + --no-appgroup-sync \ + --scim + --api-token="${OKTA_API_TOKEN}" +Successfully created OKTA plugin "okta" + +SCIM Base URL: https://teleport.example.com:443/v1/webapi/scim/okta +SCIM Identifier field for users: userName +SCIM Bearer Token: 1234567891234567891234567890 +``` + + + + + + +## Step 2/2. Configuring the Okta app + +To leverage the Teleport SCIM integration, you need to enable SCIM provisioning in your Okta app, +which will propagate user management changes to Teleport. +For detailed instructions on configuring SCIM provisioning in the Okta app, see the [Okta Integration *Configuring SCIM provisioning*](./hosted-guide.mdx#configuring-scim-provisioning) guide. + +If your Okta app has assigned users *before* SCIM provisioning is enabled, you +will need to trigger their provisioning explicitly. This can be done by +selecting the *Provision Users* button on the Okta app Assignments page. + +![Provision Existing Users](../../../img/enterprise/plugins/okta/scim-provision-existing-users.png) + + +We have seen some Okta instances that are missing the Provision Users button. +In that case the best way we have found to force user provisioning is to remove +and re-add the users to the app. Triggering a *Force Sync* in the Provisioning/To +App panel may also work, but we have had only intermittent success. + + +### Hiding profile data from Teleport +If you have data in your Okta user profile that you don’t wish to share with +your Teleport cluster, you can edit the Okta application User profile to present Teleport +with a subset and/or mapped version of the full User profile. + +## Deleting the SCIM integration + +You can delete the SCIM integration via the Integrations page in the Teleport UI, +or with tctl like so: + +```bash +$ tctl plugins delete okta +``` + + +Any users provisioned by the SCIM service will be not automatically deleted +along with the SCIM plugin. + + +You can semi-manually delete all SCIM-provisioned users using a combination of +tctl and jq. + +For example: +```bash +tctl get users --format=json \ + | jq '.[] | select(.metadata.labels["teleport.dev/origin"] == "okta") | .metadata.name' -r \ + | xargs -L 1 tctl users rm +``` From a61fc9a7072596e177e7631b67e6934e8ec42a31 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 5 Jun 2024 15:18:56 +0100 Subject: [PATCH 03/33] cleanup: remove `StreamUnstructuredSessionEvents` fallback (#42486) This PR drops the fallback used in `StreamUnstructuredSessionEvents` client call to fallback to `StreamSessionEvents` when the auth server doesn't implement `StreamUnstructuredSessionEvents`. Signed-off-by: Tiago Silva --- api/client/client.go | 84 +------------------------------------------- 1 file changed, 1 insertion(+), 83 deletions(-) diff --git a/api/client/client.go b/api/client/client.go index 629a18e3d9d69..6827923dfcbf6 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -2507,17 +2507,7 @@ func (c *Client) StreamUnstructuredSessionEvents(ctx context.Context, sessionID stream, err := c.grpc.StreamUnstructuredSessionEvents(ctx, request) if err != nil { - if trace.IsNotImplemented(trace.Wrap(err)) { - // If the server does not support the unstructured events API, - // fallback to the legacy API. - // This code patch shouldn't be triggered because the server - // returns the error only if the client calls Recv() on the stream. - // However, we keep this code patch here just in case there is a bug - // on the client grpc side. - c.streamUnstructuredSessionEventsFallback(ctx, sessionID, startIndex, ch, e) - } else { - e <- trace.Wrap(err) - } + e <- trace.Wrap(err) return ch, e } go func() { @@ -2525,20 +2515,6 @@ func (c *Client) StreamUnstructuredSessionEvents(ctx context.Context, sessionID event, err := stream.Recv() if err != nil { if !errors.Is(err, io.EOF) { - // If the server does not support the unstructured events API, it will - // return an error with code Unimplemented. This error is received - // the first time the client calls Recv() on the stream. - // If the client receives this error, it should fallback to the legacy - // API that spins another goroutine to convert the events to the - // unstructured format and sends them to the channel ch. - // Once we decide to spin the goroutine, we can leave this loop without - // reporting any error to the caller. - if trace.IsNotImplemented(trace.Wrap(err)) { - // If the server does not support the unstructured events API, - // fallback to the legacy API. - go c.streamUnstructuredSessionEventsFallback(ctx, sessionID, startIndex, ch, e) - return - } e <- trace.Wrap(err) } else { close(ch) @@ -2558,64 +2534,6 @@ func (c *Client) StreamUnstructuredSessionEvents(ctx context.Context, sessionID return ch, e } -// streamUnstructuredSessionEventsFallback is a fallback implementation of the -// StreamUnstructuredSessionEvents method that is used when the server does not -// support the unstructured events API. This method uses the old API to stream -// events from the server and converts them to the unstructured format. This -// method converts the events at event handler plugin side, which can cause -// the plugin to miss some events if the plugin is not updated to the latest -// version. -// NOTE(tigrato): This code was reintroduced in 15.0.0 because the gRPC method was renamed -// incorrectly in 13.1-14.3 which caused the server to return Unimplemented -// error to the client and the client to fallback to the legacy API. -// TODO(tigrato): DELETE IN 16.0.0 -func (c *Client) streamUnstructuredSessionEventsFallback(ctx context.Context, sessionID string, startIndex int64, ch chan *auditlogpb.EventUnstructured, e chan error) { - request := &proto.StreamSessionEventsRequest{ - SessionID: sessionID, - StartIndex: int32(startIndex), - } - - stream, err := c.grpc.StreamSessionEvents(ctx, request) - if err != nil { - e <- trace.Wrap(err) - return - } - - go func() { - for { - oneOf, err := stream.Recv() - if err != nil { - if !errors.Is(err, io.EOF) { - e <- trace.Wrap(err) - } else { - close(ch) - } - - return - } - - event, err := events.FromOneOf(*oneOf) - if err != nil { - e <- trace.Wrap(err) - return - } - - unstructedEvent, err := events.ToUnstructured(event) - if err != nil { - e <- trace.Wrap(err) - return - } - - select { - case ch <- unstructedEvent: - case <-ctx.Done(): - e <- trace.Wrap(ctx.Err()) - return - } - } - }() -} - // SearchSessionEvents allows searching for session events with a full pagination support. func (c *Client) SearchSessionEvents(ctx context.Context, fromUTC time.Time, toUTC time.Time, limit int, order types.EventOrder, startKey string) ([]events.AuditEvent, string, error) { request := &proto.GetSessionEventsRequest{ From 80a743cf306dbaf0ed8da11bcc2222ac92e94efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Wed, 5 Jun 2024 16:37:51 +0200 Subject: [PATCH 04/33] Check usageReporting.enabled before enabling VNet telemetry (#41641) * Move tshd flags out of runtime settings * Add missing comments to tshd_events_service.proto * tshdEvents: Remove unnecessary response mapping After we migrated to protobuf-ts, it is no longer needed since we are using plain objects all the way through. * Add GetUsageReportingSettings RPC * Check usage reporting setting before starting VNet --- buf.yaml | 1 - .../lib/teleterm/v1/tshd_events_service.pb.go | 318 +++++++++++++++--- .../v1/tshd_events_service_grpc.pb.go | 51 +++ .../v1/tshd_events_service_pb.client.ts | 29 ++ .../v1/tshd_events_service_pb.grpc-server.ts | 24 ++ .../lib/teleterm/v1/tshd_events_service_pb.ts | 179 +++++++++- lib/teleterm/daemon/daemon.go | 13 + lib/teleterm/vnet/service.go | 95 ++++-- lib/teleterm/vnet/service_test.go | 8 +- .../lib/teleterm/v1/tshd_events_service.proto | 42 ++- .../src/mainProcess/fixtures/mocks.ts | 1 - .../teleterm/src/mainProcess/mainProcess.ts | 49 ++- .../src/mainProcess/runtimeSettings.ts | 23 -- .../teleterm/src/mainProcess/types.ts | 1 - .../teleterm/src/services/tshdEvents/index.ts | 26 +- web/packages/teleterm/src/ui/tshdEvents.ts | 8 + 16 files changed, 717 insertions(+), 151 deletions(-) diff --git a/buf.yaml b/buf.yaml index a60eb8d931282..9ad6bb4013935 100644 --- a/buf.yaml +++ b/buf.yaml @@ -46,7 +46,6 @@ lint: - proto/teleport/lib/teleterm/v1/label.proto - proto/teleport/lib/teleterm/v1/server.proto - proto/teleport/lib/teleterm/v1/service.proto - - proto/teleport/lib/teleterm/v1/tshd_events_service.proto - proto/teleport/lib/teleterm/v1/usage_events.proto COMMENT_RPC: - proto/prehog diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go index ba1d4803bedaf..ae22188bbc280 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go @@ -37,6 +37,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Request for Relogin. type ReloginRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -240,6 +241,7 @@ func (x *VnetCertExpired) GetTargetUri() string { return "" } +// Response for Relogin. type ReloginResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -278,6 +280,11 @@ func (*ReloginResponse) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{3} } +// SendNotificationRequest includes details behind a notification. +// +// Rather than including arbitrary text strings, SendNotificationRequest should contain minimal +// details. The Electron app can then consume and format them as needed, without having to change +// what is sent over the wire. type SendNotificationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -483,6 +490,7 @@ func (x *CannotProxyVnetConnection) GetError() string { return "" } +// Response for SendNotification. type SendNotificationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -521,6 +529,7 @@ func (*SendNotificationResponse) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{7} } +// Request for SendPendingHeadlessAuthentication. type SendPendingHeadlessAuthenticationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -584,6 +593,7 @@ func (x *SendPendingHeadlessAuthenticationRequest) GetHeadlessAuthenticationClie return "" } +// Response for SendPendingHeadlessAuthentication. type SendPendingHeadlessAuthenticationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -622,6 +632,7 @@ func (*SendPendingHeadlessAuthenticationResponse) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{9} } +// Request for PromptMFA. type PromptMFARequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -693,6 +704,7 @@ func (x *PromptMFARequest) GetWebauthn() bool { return false } +// Response for PromptMFA. type PromptMFAResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -740,6 +752,142 @@ func (x *PromptMFAResponse) GetTotpCode() string { return "" } +// Request for GetUsageReportingSettings. +type GetUsageReportingSettingsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetUsageReportingSettingsRequest) Reset() { + *x = GetUsageReportingSettingsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUsageReportingSettingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsageReportingSettingsRequest) ProtoMessage() {} + +func (x *GetUsageReportingSettingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUsageReportingSettingsRequest.ProtoReflect.Descriptor instead. +func (*GetUsageReportingSettingsRequest) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{12} +} + +// Response for GetUsageReportingSettings. +type GetUsageReportingSettingsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UsageReportingSettings *UsageReportingSettings `protobuf:"bytes,1,opt,name=usage_reporting_settings,json=usageReportingSettings,proto3" json:"usage_reporting_settings,omitempty"` +} + +func (x *GetUsageReportingSettingsResponse) Reset() { + *x = GetUsageReportingSettingsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUsageReportingSettingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsageReportingSettingsResponse) ProtoMessage() {} + +func (x *GetUsageReportingSettingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[13] + 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 GetUsageReportingSettingsResponse.ProtoReflect.Descriptor instead. +func (*GetUsageReportingSettingsResponse) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{13} +} + +func (x *GetUsageReportingSettingsResponse) GetUsageReportingSettings() *UsageReportingSettings { + if x != nil { + return x.UsageReportingSettings + } + return nil +} + +// UsageReportingSettings contains information about usage reporting as understood by the Electron +// app. +type UsageReportingSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (x *UsageReportingSettings) Reset() { + *x = UsageReportingSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UsageReportingSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UsageReportingSettings) ProtoMessage() {} + +func (x *UsageReportingSettings) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[14] + 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 UsageReportingSettings.ProtoReflect.Descriptor instead. +func (*UsageReportingSettings) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{14} +} + +func (x *UsageReportingSettings) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + var File_teleport_lib_teleterm_v1_tshd_events_service_proto protoreflect.FileDescriptor var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = []byte{ @@ -834,45 +982,69 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = []byte{ 0x74, 0x68, 0x6e, 0x22, 0x30, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, - 0x70, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x83, 0x04, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, - 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x70, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x21, 0x47, 0x65, + 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6a, 0x0a, 0x18, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x32, + 0x9a, 0x05, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, + 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, - 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, + 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x43, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, - 0x46, 0x41, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, - 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, + 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x12, 0x2a, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, + 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -887,7 +1059,7 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP() []byt return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescData } -var file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_teleport_lib_teleterm_v1_tshd_events_service_proto_goTypes = []interface{}{ (*ReloginRequest)(nil), // 0: teleport.lib.teleterm.v1.ReloginRequest (*GatewayCertExpired)(nil), // 1: teleport.lib.teleterm.v1.GatewayCertExpired @@ -901,25 +1073,31 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_goTypes = []interfac (*SendPendingHeadlessAuthenticationResponse)(nil), // 9: teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse (*PromptMFARequest)(nil), // 10: teleport.lib.teleterm.v1.PromptMFARequest (*PromptMFAResponse)(nil), // 11: teleport.lib.teleterm.v1.PromptMFAResponse + (*GetUsageReportingSettingsRequest)(nil), // 12: teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest + (*GetUsageReportingSettingsResponse)(nil), // 13: teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse + (*UsageReportingSettings)(nil), // 14: teleport.lib.teleterm.v1.UsageReportingSettings } var file_teleport_lib_teleterm_v1_tshd_events_service_proto_depIdxs = []int32{ 1, // 0: teleport.lib.teleterm.v1.ReloginRequest.gateway_cert_expired:type_name -> teleport.lib.teleterm.v1.GatewayCertExpired 2, // 1: teleport.lib.teleterm.v1.ReloginRequest.vnet_cert_expired:type_name -> teleport.lib.teleterm.v1.VnetCertExpired 5, // 2: teleport.lib.teleterm.v1.SendNotificationRequest.cannot_proxy_gateway_connection:type_name -> teleport.lib.teleterm.v1.CannotProxyGatewayConnection 6, // 3: teleport.lib.teleterm.v1.SendNotificationRequest.cannot_proxy_vnet_connection:type_name -> teleport.lib.teleterm.v1.CannotProxyVnetConnection - 0, // 4: teleport.lib.teleterm.v1.TshdEventsService.Relogin:input_type -> teleport.lib.teleterm.v1.ReloginRequest - 4, // 5: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:input_type -> teleport.lib.teleterm.v1.SendNotificationRequest - 8, // 6: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:input_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationRequest - 10, // 7: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:input_type -> teleport.lib.teleterm.v1.PromptMFARequest - 3, // 8: teleport.lib.teleterm.v1.TshdEventsService.Relogin:output_type -> teleport.lib.teleterm.v1.ReloginResponse - 7, // 9: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:output_type -> teleport.lib.teleterm.v1.SendNotificationResponse - 9, // 10: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:output_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse - 11, // 11: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:output_type -> teleport.lib.teleterm.v1.PromptMFAResponse - 8, // [8:12] is the sub-list for method output_type - 4, // [4:8] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 14, // 4: teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse.usage_reporting_settings:type_name -> teleport.lib.teleterm.v1.UsageReportingSettings + 0, // 5: teleport.lib.teleterm.v1.TshdEventsService.Relogin:input_type -> teleport.lib.teleterm.v1.ReloginRequest + 4, // 6: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:input_type -> teleport.lib.teleterm.v1.SendNotificationRequest + 8, // 7: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:input_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationRequest + 10, // 8: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:input_type -> teleport.lib.teleterm.v1.PromptMFARequest + 12, // 9: teleport.lib.teleterm.v1.TshdEventsService.GetUsageReportingSettings:input_type -> teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest + 3, // 10: teleport.lib.teleterm.v1.TshdEventsService.Relogin:output_type -> teleport.lib.teleterm.v1.ReloginResponse + 7, // 11: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:output_type -> teleport.lib.teleterm.v1.SendNotificationResponse + 9, // 12: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:output_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse + 11, // 13: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:output_type -> teleport.lib.teleterm.v1.PromptMFAResponse + 13, // 14: teleport.lib.teleterm.v1.TshdEventsService.GetUsageReportingSettings:output_type -> teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse + 10, // [10:15] is the sub-list for method output_type + 5, // [5:10] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_teleport_lib_teleterm_v1_tshd_events_service_proto_init() } @@ -1072,6 +1250,42 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_init() { return nil } } + file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUsageReportingSettingsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUsageReportingSettingsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UsageReportingSettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[0].OneofWrappers = []interface{}{ (*ReloginRequest_GatewayCertExpired)(nil), @@ -1087,7 +1301,7 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go index 457360156f64d..1b01791bc6761 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go @@ -40,6 +40,7 @@ const ( TshdEventsService_SendNotification_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/SendNotification" TshdEventsService_SendPendingHeadlessAuthentication_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/SendPendingHeadlessAuthentication" TshdEventsService_PromptMFA_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/PromptMFA" + TshdEventsService_GetUsageReportingSettings_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/GetUsageReportingSettings" ) // TshdEventsServiceClient is the client API for TshdEventsService service. @@ -57,7 +58,15 @@ type TshdEventsServiceClient interface { // which it can use to initiate headless authentication resolution in the UI. SendPendingHeadlessAuthentication(ctx context.Context, in *SendPendingHeadlessAuthenticationRequest, opts ...grpc.CallOption) (*SendPendingHeadlessAuthenticationResponse, error) // PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + // If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + // If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + // code. PromptMFA(ctx context.Context, in *PromptMFARequest, opts ...grpc.CallOption) (*PromptMFAResponse, error) + // GetUsageReportingSettings returns the current state of usage reporting. + // At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + // with the only exception being the first start of the app when they're prompted about telemetry. + // Hence why this is an RPC and not information passed over argv to tsh daemon. + GetUsageReportingSettings(ctx context.Context, in *GetUsageReportingSettingsRequest, opts ...grpc.CallOption) (*GetUsageReportingSettingsResponse, error) } type tshdEventsServiceClient struct { @@ -104,6 +113,15 @@ func (c *tshdEventsServiceClient) PromptMFA(ctx context.Context, in *PromptMFARe return out, nil } +func (c *tshdEventsServiceClient) GetUsageReportingSettings(ctx context.Context, in *GetUsageReportingSettingsRequest, opts ...grpc.CallOption) (*GetUsageReportingSettingsResponse, error) { + out := new(GetUsageReportingSettingsResponse) + err := c.cc.Invoke(ctx, TshdEventsService_GetUsageReportingSettings_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TshdEventsServiceServer is the server API for TshdEventsService service. // All implementations must embed UnimplementedTshdEventsServiceServer // for forward compatibility @@ -119,7 +137,15 @@ type TshdEventsServiceServer interface { // which it can use to initiate headless authentication resolution in the UI. SendPendingHeadlessAuthentication(context.Context, *SendPendingHeadlessAuthenticationRequest) (*SendPendingHeadlessAuthenticationResponse, error) // PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + // If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + // If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + // code. PromptMFA(context.Context, *PromptMFARequest) (*PromptMFAResponse, error) + // GetUsageReportingSettings returns the current state of usage reporting. + // At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + // with the only exception being the first start of the app when they're prompted about telemetry. + // Hence why this is an RPC and not information passed over argv to tsh daemon. + GetUsageReportingSettings(context.Context, *GetUsageReportingSettingsRequest) (*GetUsageReportingSettingsResponse, error) mustEmbedUnimplementedTshdEventsServiceServer() } @@ -139,6 +165,9 @@ func (UnimplementedTshdEventsServiceServer) SendPendingHeadlessAuthentication(co func (UnimplementedTshdEventsServiceServer) PromptMFA(context.Context, *PromptMFARequest) (*PromptMFAResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method PromptMFA not implemented") } +func (UnimplementedTshdEventsServiceServer) GetUsageReportingSettings(context.Context, *GetUsageReportingSettingsRequest) (*GetUsageReportingSettingsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUsageReportingSettings not implemented") +} func (UnimplementedTshdEventsServiceServer) mustEmbedUnimplementedTshdEventsServiceServer() {} // UnsafeTshdEventsServiceServer may be embedded to opt out of forward compatibility for this service. @@ -224,6 +253,24 @@ func _TshdEventsService_PromptMFA_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _TshdEventsService_GetUsageReportingSettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUsageReportingSettingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TshdEventsServiceServer).GetUsageReportingSettings(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TshdEventsService_GetUsageReportingSettings_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TshdEventsServiceServer).GetUsageReportingSettings(ctx, req.(*GetUsageReportingSettingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TshdEventsService_ServiceDesc is the grpc.ServiceDesc for TshdEventsService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -247,6 +294,10 @@ var TshdEventsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "PromptMFA", Handler: _TshdEventsService_PromptMFA_Handler, }, + { + MethodName: "GetUsageReportingSettings", + Handler: _TshdEventsService_GetUsageReportingSettings_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/lib/teleterm/v1/tshd_events_service.proto", diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts index 236311ffa0a9d..bbe5f29260b7e 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts @@ -24,6 +24,8 @@ import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; import { TshdEventsService } from "./tshd_events_service_pb"; +import type { GetUsageReportingSettingsResponse } from "./tshd_events_service_pb"; +import type { GetUsageReportingSettingsRequest } from "./tshd_events_service_pb"; import type { PromptMFAResponse } from "./tshd_events_service_pb"; import type { PromptMFARequest } from "./tshd_events_service_pb"; import type { SendPendingHeadlessAuthenticationResponse } from "./tshd_events_service_pb"; @@ -66,10 +68,22 @@ export interface ITshdEventsServiceClient { sendPendingHeadlessAuthentication(input: SendPendingHeadlessAuthenticationRequest, options?: RpcOptions): UnaryCall; /** * PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + * If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + * If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + * code. * * @generated from protobuf rpc: PromptMFA(teleport.lib.teleterm.v1.PromptMFARequest) returns (teleport.lib.teleterm.v1.PromptMFAResponse); */ promptMFA(input: PromptMFARequest, options?: RpcOptions): UnaryCall; + /** + * GetUsageReportingSettings returns the current state of usage reporting. + * At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + * with the only exception being the first start of the app when they're prompted about telemetry. + * Hence why this is an RPC and not information passed over argv to tsh daemon. + * + * @generated from protobuf rpc: GetUsageReportingSettings(teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest) returns (teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse); + */ + getUsageReportingSettings(input: GetUsageReportingSettingsRequest, options?: RpcOptions): UnaryCall; } /** * TshdEventsService is served by the Electron app. The tsh daemon calls this service to notify the @@ -116,6 +130,9 @@ export class TshdEventsServiceClient implements ITshdEventsServiceClient, Servic } /** * PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + * If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + * If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + * code. * * @generated from protobuf rpc: PromptMFA(teleport.lib.teleterm.v1.PromptMFARequest) returns (teleport.lib.teleterm.v1.PromptMFAResponse); */ @@ -123,4 +140,16 @@ export class TshdEventsServiceClient implements ITshdEventsServiceClient, Servic const method = this.methods[3], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * GetUsageReportingSettings returns the current state of usage reporting. + * At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + * with the only exception being the first start of the app when they're prompted about telemetry. + * Hence why this is an RPC and not information passed over argv to tsh daemon. + * + * @generated from protobuf rpc: GetUsageReportingSettings(teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest) returns (teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse); + */ + getUsageReportingSettings(input: GetUsageReportingSettingsRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[4], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } } diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts index bc6b5b5c6ea78..544679c3967b4 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts @@ -21,6 +21,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // +import { GetUsageReportingSettingsResponse } from "./tshd_events_service_pb"; +import { GetUsageReportingSettingsRequest } from "./tshd_events_service_pb"; import { PromptMFAResponse } from "./tshd_events_service_pb"; import { PromptMFARequest } from "./tshd_events_service_pb"; import { SendPendingHeadlessAuthenticationResponse } from "./tshd_events_service_pb"; @@ -61,10 +63,22 @@ export interface ITshdEventsService extends grpc.UntypedServiceImplementation { sendPendingHeadlessAuthentication: grpc.handleUnaryCall; /** * PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + * If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + * If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + * code. * * @generated from protobuf rpc: PromptMFA(teleport.lib.teleterm.v1.PromptMFARequest) returns (teleport.lib.teleterm.v1.PromptMFAResponse); */ promptMFA: grpc.handleUnaryCall; + /** + * GetUsageReportingSettings returns the current state of usage reporting. + * At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + * with the only exception being the first start of the app when they're prompted about telemetry. + * Hence why this is an RPC and not information passed over argv to tsh daemon. + * + * @generated from protobuf rpc: GetUsageReportingSettings(teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest) returns (teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse); + */ + getUsageReportingSettings: grpc.handleUnaryCall; } /** * @grpc/grpc-js definition for the protobuf service teleport.lib.teleterm.v1.TshdEventsService. @@ -117,5 +131,15 @@ export const tshdEventsServiceDefinition: grpc.ServiceDefinition PromptMFARequest.fromBinary(bytes), responseSerialize: value => Buffer.from(PromptMFAResponse.toBinary(value)), requestSerialize: value => Buffer.from(PromptMFARequest.toBinary(value)) + }, + getUsageReportingSettings: { + path: "/teleport.lib.teleterm.v1.TshdEventsService/GetUsageReportingSettings", + originalName: "GetUsageReportingSettings", + requestStream: false, + responseStream: false, + responseDeserialize: bytes => GetUsageReportingSettingsResponse.fromBinary(bytes), + requestDeserialize: bytes => GetUsageReportingSettingsRequest.fromBinary(bytes), + responseSerialize: value => Buffer.from(GetUsageReportingSettingsResponse.toBinary(value)), + requestSerialize: value => Buffer.from(GetUsageReportingSettingsRequest.toBinary(value)) } }; diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts index 4df3471f48fe2..522488ec64ca0 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts @@ -31,9 +31,9 @@ import { UnknownFieldHandler } from "@protobuf-ts/runtime"; import type { PartialMessage } from "@protobuf-ts/runtime"; import { reflectionMergePartial } from "@protobuf-ts/runtime"; import { MessageType } from "@protobuf-ts/runtime"; -// Relogin - /** + * Request for Relogin. + * * @generated from protobuf message teleport.lib.teleterm.v1.ReloginRequest */ export interface ReloginRequest { @@ -98,13 +98,19 @@ export interface VnetCertExpired { targetUri: string; } /** + * Response for Relogin. + * * @generated from protobuf message teleport.lib.teleterm.v1.ReloginResponse */ export interface ReloginResponse { } -// SendNotification - /** + * SendNotificationRequest includes details behind a notification. + * + * Rather than including arbitrary text strings, SendNotificationRequest should contain minimal + * details. The Electron app can then consume and format them as needed, without having to change + * what is sent over the wire. + * * @generated from protobuf message teleport.lib.teleterm.v1.SendNotificationRequest */ export interface SendNotificationRequest { @@ -166,13 +172,15 @@ export interface CannotProxyVnetConnection { error: string; } /** + * Response for SendNotification. + * * @generated from protobuf message teleport.lib.teleterm.v1.SendNotificationResponse */ export interface SendNotificationResponse { } -// SendPendingHeadlessAuthentication - /** + * Request for SendPendingHeadlessAuthentication. + * * @generated from protobuf message teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationRequest */ export interface SendPendingHeadlessAuthenticationRequest { @@ -190,13 +198,15 @@ export interface SendPendingHeadlessAuthenticationRequest { headlessAuthenticationClientIp: string; } /** + * Response for SendPendingHeadlessAuthentication. + * * @generated from protobuf message teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse */ export interface SendPendingHeadlessAuthenticationResponse { } -// PromptMFA - /** + * Request for PromptMFA. + * * @generated from protobuf message teleport.lib.teleterm.v1.PromptMFARequest */ export interface PromptMFARequest { @@ -218,6 +228,8 @@ export interface PromptMFARequest { webauthn: boolean; } /** + * Response for PromptMFA. + * * @generated from protobuf message teleport.lib.teleterm.v1.PromptMFAResponse */ export interface PromptMFAResponse { @@ -226,6 +238,36 @@ export interface PromptMFAResponse { */ totpCode: string; } +/** + * Request for GetUsageReportingSettings. + * + * @generated from protobuf message teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest + */ +export interface GetUsageReportingSettingsRequest { +} +/** + * Response for GetUsageReportingSettings. + * + * @generated from protobuf message teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse + */ +export interface GetUsageReportingSettingsResponse { + /** + * @generated from protobuf field: teleport.lib.teleterm.v1.UsageReportingSettings usage_reporting_settings = 1; + */ + usageReportingSettings?: UsageReportingSettings; +} +/** + * UsageReportingSettings contains information about usage reporting as understood by the Electron + * app. + * + * @generated from protobuf message teleport.lib.teleterm.v1.UsageReportingSettings + */ +export interface UsageReportingSettings { + /** + * @generated from protobuf field: bool enabled = 1; + */ + enabled: boolean; +} // @generated message type with reflection information, may provide speed optimized methods class ReloginRequest$Type extends MessageType { constructor() { @@ -830,6 +872,124 @@ class PromptMFAResponse$Type extends MessageType { * @generated MessageType for protobuf message teleport.lib.teleterm.v1.PromptMFAResponse */ export const PromptMFAResponse = new PromptMFAResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetUsageReportingSettingsRequest$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest", []); + } + create(value?: PartialMessage): GetUsageReportingSettingsRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetUsageReportingSettingsRequest): GetUsageReportingSettingsRequest { + return target ?? this.create(); + } + internalBinaryWrite(message: GetUsageReportingSettingsRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest + */ +export const GetUsageReportingSettingsRequest = new GetUsageReportingSettingsRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetUsageReportingSettingsResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse", [ + { no: 1, name: "usage_reporting_settings", kind: "message", T: () => UsageReportingSettings } + ]); + } + create(value?: PartialMessage): GetUsageReportingSettingsResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetUsageReportingSettingsResponse): GetUsageReportingSettingsResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* teleport.lib.teleterm.v1.UsageReportingSettings usage_reporting_settings */ 1: + message.usageReportingSettings = UsageReportingSettings.internalBinaryRead(reader, reader.uint32(), options, message.usageReportingSettings); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetUsageReportingSettingsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* teleport.lib.teleterm.v1.UsageReportingSettings usage_reporting_settings = 1; */ + if (message.usageReportingSettings) + UsageReportingSettings.internalBinaryWrite(message.usageReportingSettings, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse + */ +export const GetUsageReportingSettingsResponse = new GetUsageReportingSettingsResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class UsageReportingSettings$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.UsageReportingSettings", [ + { no: 1, name: "enabled", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + ]); + } + create(value?: PartialMessage): UsageReportingSettings { + const message = globalThis.Object.create((this.messagePrototype!)); + message.enabled = false; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UsageReportingSettings): UsageReportingSettings { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool enabled */ 1: + message.enabled = reader.bool(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: UsageReportingSettings, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bool enabled = 1; */ + if (message.enabled !== false) + writer.tag(1, WireType.Varint).bool(message.enabled); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.UsageReportingSettings + */ +export const UsageReportingSettings = new UsageReportingSettings$Type(); /** * @generated ServiceType for protobuf service teleport.lib.teleterm.v1.TshdEventsService */ @@ -837,5 +997,6 @@ export const TshdEventsService = new ServiceType("teleport.lib.teleterm.v1.TshdE { name: "Relogin", options: {}, I: ReloginRequest, O: ReloginResponse }, { name: "SendNotification", options: {}, I: SendNotificationRequest, O: SendNotificationResponse }, { name: "SendPendingHeadlessAuthentication", options: {}, I: SendPendingHeadlessAuthenticationRequest, O: SendPendingHeadlessAuthenticationResponse }, - { name: "PromptMFA", options: {}, I: PromptMFARequest, O: PromptMFAResponse } + { name: "PromptMFA", options: {}, I: PromptMFARequest, O: PromptMFAResponse }, + { name: "GetUsageReportingSettings", options: {}, I: GetUsageReportingSettingsRequest, O: GetUsageReportingSettingsResponse } ]); diff --git a/lib/teleterm/daemon/daemon.go b/lib/teleterm/daemon/daemon.go index 497dffb7437db..af8ab870be825 100644 --- a/lib/teleterm/daemon/daemon.go +++ b/lib/teleterm/daemon/daemon.go @@ -880,6 +880,19 @@ func (s *Service) UpdateAndDialTshdEventsServerAddress(serverAddress string) err return nil } +// TshdEventsClient returns the client if it was initialized earlied by calling +// UpdateAndDialTshdEventsServerAddress, otherwise it returns an error. +// +// The startup of Connect is orchestrated in a way that makes it safe to call this method from any +// RPC. Code inside daemon.Service should just use s.tshdEventsClient directly. +func (s *Service) TshdEventsClient() (api.TshdEventsServiceClient, error) { + if s.tshdEventsClient == nil { + return nil, trace.NotFound("tshd events client has not been initialized yet") + } + + return s.tshdEventsClient, nil +} + // NotifyApp sends a notification (usually an error) to the Electron App. func (s *Service) NotifyApp(ctx context.Context, notification *api.SendNotificationRequest) error { tshdEventsCtx, cancelTshdEventsCtx := context.WithTimeout(ctx, tshdEventsTimeout) diff --git a/lib/teleterm/vnet/service.go b/lib/teleterm/vnet/service.go index 4404f32991c18..7cce77f7f22d2 100644 --- a/lib/teleterm/vnet/service.go +++ b/lib/teleterm/vnet/service.go @@ -59,7 +59,7 @@ type Service struct { mu sync.Mutex status status processManager *vnet.ProcessManager - usageReporter *usageReporter + usageReporter usageReporter } // New creates an instance of Service. @@ -116,25 +116,38 @@ func (s *Service) Start(ctx context.Context, req *api.StartRequest) (*api.StartR return &api.StartResponse{}, nil } - usageReporter, err := NewUsageReporter(UsageReporterConfig{ - ClientCache: s.cfg.DaemonService, - EventConsumer: s.cfg.DaemonService, - ClusterIDCache: s.cfg.ClusterIDCache, - InstallationID: s.cfg.InstallationID, - }) - if err != nil { - return nil, trace.Wrap(err) - } - defer func() { - if s.status != statusRunning { - usageReporter.Stop() - } - }() - appProvider := &appProvider{ daemonService: s.cfg.DaemonService, insecureSkipVerify: s.cfg.InsecureSkipVerify, - usageReporter: usageReporter, + usageReporter: &disabledTelemetryUsageReporter{}, + } + + // Generally, the usage reporting setting cannot be changed without restarting the app, so + // technically this information could have been passed through argv to tsh daemon. + // However, there is one exception: during the first launch of the app, the user is asked if they + // want to enable telemetry. Agreeing to that changes the setting without restarting the app. + // As such, this service needs to ask for this setting on every launch. + isUsageReportingEnabled, err := s.isUsageReportingEnabled(ctx) + if err != nil { + return nil, trace.Wrap(err, "getting usage reporting settings") + } + + if isUsageReportingEnabled { + usageReporter, err := newDaemonUsageReporter(daemonUsageReporterConfig{ + ClientCache: s.cfg.DaemonService, + EventConsumer: s.cfg.DaemonService, + ClusterIDCache: s.cfg.ClusterIDCache, + InstallationID: s.cfg.InstallationID, + }) + if err != nil { + return nil, trace.Wrap(err) + } + defer func() { + if s.status != statusRunning { + usageReporter.Stop() + } + }() + appProvider.usageReporter = usageReporter } processManager, err := vnet.SetupAndRun(ctx, appProvider) @@ -163,7 +176,7 @@ func (s *Service) Start(ctx context.Context, req *api.StartRequest) (*api.StartR }() s.processManager = processManager - s.usageReporter = usageReporter + s.usageReporter = appProvider.usageReporter s.status = statusRunning return &api.StartResponse{}, nil } @@ -216,9 +229,23 @@ func (s *Service) Close() error { return nil } +func (s *Service) isUsageReportingEnabled(ctx context.Context) (bool, error) { + tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient() + if err != nil { + return false, trace.Wrap(err) + } + + resp, err := tshdEventsClient.GetUsageReportingSettings(ctx, &apiteleterm.GetUsageReportingSettingsRequest{}) + if err != nil { + return false, trace.Wrap(err) + } + + return resp.UsageReportingSettings.Enabled, nil +} + type appProvider struct { daemonService *daemon.Service - usageReporter *usageReporter + usageReporter usageReporter insecureSkipVerify bool } @@ -339,8 +366,13 @@ func (p *appProvider) OnNewConnection(ctx context.Context, profileName, leafClus return nil } -type usageReporter struct { - cfg UsageReporterConfig +type usageReporter interface { + ReportApp(uri.ResourceURI) error + Stop() +} + +type daemonUsageReporter struct { + cfg daemonUsageReporterConfig // reportedApps contains a set of URIs for apps which usage has been already reported. // App gateways (local proxies) in Connect report a single event per gateway created per app. VNet // needs to replicate this behavior, hence why it keeps track of reported apps to report only one @@ -363,7 +395,7 @@ type eventConsumer interface { ReportUsageEvent(*apiteleterm.ReportUsageEventRequest) error } -type UsageReporterConfig struct { +type daemonUsageReporterConfig struct { ClientCache clientCache EventConsumer eventConsumer // clusterIDCache stores cluster ID that needs to be included with each usage event. It's updated @@ -373,7 +405,7 @@ type UsageReporterConfig struct { InstallationID string } -func (c *UsageReporterConfig) CheckAndSetDefaults() error { +func (c *daemonUsageReporterConfig) CheckAndSetDefaults() error { if c.ClientCache == nil { return trace.BadParameter("missing ClientCache") } @@ -393,12 +425,12 @@ func (c *UsageReporterConfig) CheckAndSetDefaults() error { return nil } -func NewUsageReporter(cfg UsageReporterConfig) (*usageReporter, error) { +func newDaemonUsageReporter(cfg daemonUsageReporterConfig) (*daemonUsageReporter, error) { if err := cfg.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } - return &usageReporter{ + return &daemonUsageReporter{ cfg: cfg, reportedApps: make(map[string]struct{}), close: make(chan struct{}), @@ -407,7 +439,7 @@ func NewUsageReporter(cfg UsageReporterConfig) (*usageReporter, error) { // ReportApp adds an event related to the given app to the events queue, if the app wasn't reported // already. Only one invocation of ReportApp can be in flight at a time. -func (r *usageReporter) ReportApp(appURI uri.ResourceURI) error { +func (r *daemonUsageReporter) ReportApp(appURI uri.ResourceURI) error { r.mu.Lock() defer r.mu.Unlock() @@ -475,7 +507,7 @@ func (r *usageReporter) ReportApp(appURI uri.ResourceURI) error { // Stop aborts the reporting of an event that's currently in progress and prevents further events // from being reported. It blocks until the current ReportApp call aborts. -func (r *usageReporter) Stop() { +func (r *daemonUsageReporter) Stop() { if r.closed.Load() { return } @@ -488,3 +520,12 @@ func (r *usageReporter) Stop() { r.mu.Lock() defer r.mu.Unlock() } + +type disabledTelemetryUsageReporter struct{} + +func (r *disabledTelemetryUsageReporter) ReportApp(appURI uri.ResourceURI) error { + log.DebugContext(context.Background(), "Skipping usage event, usage reporting is turned off", "app", appURI.String()) + return nil +} + +func (r *disabledTelemetryUsageReporter) Stop() {} diff --git a/lib/teleterm/vnet/service_test.go b/lib/teleterm/vnet/service_test.go index 9fe1d8ea9da74..58fba75e2afe9 100644 --- a/lib/teleterm/vnet/service_test.go +++ b/lib/teleterm/vnet/service_test.go @@ -32,7 +32,7 @@ import ( "github.com/gravitational/teleport/lib/teleterm/clusters" ) -func TestUsageReporter(t *testing.T) { +func TestDaemonUsageReporter(t *testing.T) { eventConsumer := fakeEventConsumer{} validCluster := uri.NewClusterURI("foo") @@ -51,7 +51,7 @@ func TestUsageReporter(t *testing.T) { clusterIDcache := clusteridcache.Cache{} clusterIDcache.Store(uri.NewClusterURI("foo"), "1234") - usageReporter, err := NewUsageReporter(UsageReporterConfig{ + usageReporter, err := newDaemonUsageReporter(daemonUsageReporterConfig{ EventConsumer: &eventConsumer, ClientCache: &clientCache, ClusterIDCache: &clusterIDcache, @@ -79,14 +79,14 @@ func TestUsageReporter(t *testing.T) { require.Equal(t, 1, eventConsumer.EventCount()) } -func TestUsageReporter_Stop(t *testing.T) { +func TestDaemonUsageReporter_Stop(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) t.Cleanup(cancel) eventConsumer := fakeEventConsumer{} clientCache := fakeClientCache{blockingOnCtxC: make(chan struct{}, 1)} clusterIDCache := clusteridcache.Cache{} - usageReporter, err := NewUsageReporter(UsageReporterConfig{ + usageReporter, err := newDaemonUsageReporter(daemonUsageReporterConfig{ EventConsumer: &eventConsumer, ClientCache: &clientCache, ClusterIDCache: &clusterIDCache, diff --git a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto index 8b57fc2c0f5c2..b64594a16b40f 100644 --- a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto +++ b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto @@ -36,11 +36,18 @@ service TshdEventsService { // which it can use to initiate headless authentication resolution in the UI. rpc SendPendingHeadlessAuthentication(SendPendingHeadlessAuthenticationRequest) returns (SendPendingHeadlessAuthenticationResponse); // PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. + // If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. + // If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the + // code. rpc PromptMFA(PromptMFARequest) returns (PromptMFAResponse); + // GetUsageReportingSettings returns the current state of usage reporting. + // At the moment, the user cannot toggle usage reporting on and off without shutting down the app, + // with the only exception being the first start of the app when they're prompted about telemetry. + // Hence why this is an RPC and not information passed over argv to tsh daemon. + rpc GetUsageReportingSettings(GetUsageReportingSettingsRequest) returns (GetUsageReportingSettingsResponse); } -// Relogin - +// Request for Relogin. message ReloginRequest { string root_cluster_uri = 1; oneof reason { @@ -70,10 +77,14 @@ message VnetCertExpired { string target_uri = 1; } +// Response for Relogin. message ReloginResponse {} -// SendNotification - +// SendNotificationRequest includes details behind a notification. +// +// Rather than including arbitrary text strings, SendNotificationRequest should contain minimal +// details. The Electron app can then consume and format them as needed, without having to change +// what is sent over the wire. message SendNotificationRequest { oneof subject { CannotProxyGatewayConnection cannot_proxy_gateway_connection = 1; @@ -98,20 +109,20 @@ message CannotProxyVnetConnection { string error = 2; } +// Response for SendNotification. message SendNotificationResponse {} -// SendPendingHeadlessAuthentication - +// Request for SendPendingHeadlessAuthentication. message SendPendingHeadlessAuthenticationRequest { string root_cluster_uri = 1; string headless_authentication_id = 2; string headless_authentication_client_ip = 3; } +// Response for SendPendingHeadlessAuthentication. message SendPendingHeadlessAuthenticationResponse {} -// PromptMFA - +// Request for PromptMFA. message PromptMFARequest { string root_cluster_uri = 1; string reason = 2; @@ -119,6 +130,21 @@ message PromptMFARequest { bool webauthn = 4; } +// Response for PromptMFA. message PromptMFAResponse { string totp_code = 1; } + +// Request for GetUsageReportingSettings. +message GetUsageReportingSettingsRequest {} + +// Response for GetUsageReportingSettings. +message GetUsageReportingSettingsResponse { + UsageReportingSettings usage_reporting_settings = 1; +} + +// UsageReportingSettings contains information about usage reporting as understood by the Electron +// app. +message UsageReportingSettings { + bool enabled = 1; +} diff --git a/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts b/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts index 22bb9d3e007ec..f6d288931f31a 100644 --- a/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts +++ b/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts @@ -155,7 +155,6 @@ export const makeRuntimeSettings = ( requestedNetworkAddress: '', binaryPath: '', homeDir: '', - flags: [], }, sharedProcess: { requestedNetworkAddress: '', diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index 42dcb2f7b44b6..5924723d8a16c 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -51,6 +51,7 @@ import Logger from 'teleterm/logger'; import * as grpcCreds from 'teleterm/services/grpcCredentials'; import { createTshdClient, TshdClient } from 'teleterm/services/tshd'; import { loggingInterceptor } from 'teleterm/services/tshd/interceptors'; +import { staticConfig } from 'teleterm/staticConfig'; import { ConfigService, @@ -63,6 +64,7 @@ import { resolveNetworkAddress, ResolveError } from './resolveNetworkAddress'; import { WindowsManager } from './windowsManager'; import { downloadAgent, verifyAgent, FileDownloader } from './agentDownloader'; import { + getAgentsDir, createAgentConfigFile, isAgentConfigFileCreated, removeAgentDirectory, @@ -169,17 +171,21 @@ export default class MainProcess { } private initTshd() { - const { binaryPath, flags, homeDir } = this.settings.tshd; + const { binaryPath, homeDir } = this.settings.tshd; this.logger.info(`Starting tsh daemon from ${binaryPath}`); - this.tshdProcess = spawn(binaryPath, flags, { - stdio: 'pipe', // stdio must be set to `pipe` as the gRPC server address is read from stdout - windowsHide: true, - env: { - ...process.env, - TELEPORT_HOME: homeDir, - }, - }); + this.tshdProcess = spawn( + binaryPath, + ['daemon', 'start', ...this.getTshdFlags()], + { + stdio: 'pipe', // stdio must be set to `pipe` as the gRPC server address is read from stdout + windowsHide: true, + env: { + ...process.env, + TELEPORT_HOME: homeDir, + }, + } + ); this.logProcessExitAndError('tshd', this.tshdProcess); @@ -198,6 +204,31 @@ export default class MainProcess { ); } + private getTshdFlags(): string[] { + const settings = this.settings; + const agentsDir = getAgentsDir(settings.userDataDir); + + const flags = [ + // grpc-js requires us to pass localhost:port for TCP connections, + // for tshd we have to specify the protocol as well. + `--addr=${settings.tshd.requestedNetworkAddress}`, + `--certs-dir=${settings.certsDir}`, + `--prehog-addr=${staticConfig.prehogAddress}`, + `--kubeconfigs-dir=${settings.kubeConfigsDir}`, + `--agents-dir=${agentsDir}`, + `--installation-id=${settings.installationId}`, + ]; + + if (settings.insecure) { + flags.unshift('--insecure'); + } + if (settings.debug) { + flags.unshift('--debug'); + } + + return flags; + } + private initSharedProcess() { this.sharedProcess = fork( path.join(__dirname, 'sharedProcess.js'), diff --git a/web/packages/teleterm/src/mainProcess/runtimeSettings.ts b/web/packages/teleterm/src/mainProcess/runtimeSettings.ts index e93cdaabea4dd..aa76de529d7ff 100644 --- a/web/packages/teleterm/src/mainProcess/runtimeSettings.ts +++ b/web/packages/teleterm/src/mainProcess/runtimeSettings.ts @@ -23,11 +23,9 @@ import path from 'path'; import { app } from 'electron'; import Logger from 'teleterm/logger'; -import { staticConfig } from 'teleterm/staticConfig'; import { GrpcServerAddresses, RuntimeSettings } from './types'; import { loadInstallationId } from './loadInstallationId'; -import { getAgentsDir } from './createAgentConfigFile'; const { argv, env } = process; @@ -76,8 +74,6 @@ export function getRuntimeSettings(): RuntimeSettings { // Before switching to the recommended path, we need to investigate the impact of this change. // https://www.electronjs.org/docs/latest/api/app#appgetpathname const logsDir = path.join(userDataDir, 'logs'); - // DO NOT expose agentsDir through RuntimeSettings. See the comment in getAgentsDir. - const agentsDir = getAgentsDir(userDataDir); const installationId = loadInstallationId( path.resolve(app.getPath('userData'), 'installation_id') ); @@ -86,18 +82,6 @@ export function getRuntimeSettings(): RuntimeSettings { binaryPath: tshBinPath, homeDir: getTshHomeDir(), requestedNetworkAddress: tshAddress, - flags: [ - 'daemon', - 'start', - // grpc-js requires us to pass localhost:port for TCP connections, - // for tshd we have to specify the protocol as well. - `--addr=${tshAddress}`, - `--certs-dir=${getCertsDir()}`, - `--prehog-addr=${staticConfig.prehogAddress}`, - `--kubeconfigs-dir=${kubeConfigsDir}`, - `--agents-dir=${agentsDir}`, - `--installation-id=${installationId}`, - ], }; const sharedProcess = { requestedNetworkAddress: sharedAddress, @@ -115,13 +99,6 @@ export function getRuntimeSettings(): RuntimeSettings { // A workaround is to read the version from `process.env.npm_package_version`. const appVersion = dev ? process.env.npm_package_version : app.getVersion(); - if (insecure) { - tshd.flags.unshift('--insecure'); - } - if (debug) { - tshd.flags.unshift('--debug'); - } - return { dev, debug, diff --git a/web/packages/teleterm/src/mainProcess/types.ts b/web/packages/teleterm/src/mainProcess/types.ts index ff17c7967e30e..78e73e9fe1e90 100644 --- a/web/packages/teleterm/src/mainProcess/types.ts +++ b/web/packages/teleterm/src/mainProcess/types.ts @@ -67,7 +67,6 @@ export type RuntimeSettings = { requestedNetworkAddress: string; binaryPath: string; homeDir: string; - flags: string[]; }; sharedProcess: { requestedNetworkAddress: string; diff --git a/web/packages/teleterm/src/services/tshdEvents/index.ts b/web/packages/teleterm/src/services/tshdEvents/index.ts index b52eeb082c2e5..84c2df2d0fd52 100644 --- a/web/packages/teleterm/src/services/tshdEvents/index.ts +++ b/web/packages/teleterm/src/services/tshdEvents/index.ts @@ -144,8 +144,7 @@ function createService(logger: Logger): { >( rpcName: RpcName, call: grpc.ServerUnaryCall, - callback: (error: Error | null, response: Response | null) => void, - mapResponseObjectToResponseInstance: (responseObject: Response) => Response + callback: (error: Error | null, response: Response | null) => void ) { const request = call.request; @@ -177,7 +176,7 @@ function createService(logger: Logger): { return; } - callback(null, mapResponseObjectToResponseInstance(response)); + callback(null, response); logger.info( `replied to ${rpcName}`, @@ -209,25 +208,20 @@ function createService(logger: Logger): { } const service: apiService.ITshdEventsService = { - relogin: (call, callback) => - processEvent('relogin', call, callback, () => - api.ReloginResponse.create() - ), + relogin: (call, callback) => processEvent('relogin', call, callback), sendNotification: (call, callback) => - processEvent('sendNotification', call, callback, () => - api.SendNotificationResponse.create() - ), + processEvent('sendNotification', call, callback), sendPendingHeadlessAuthentication: (call, callback) => - processEvent('sendPendingHeadlessAuthentication', call, callback, () => - api.SendPendingHeadlessAuthenticationResponse.create() - ), + processEvent('sendPendingHeadlessAuthentication', call, callback), promptMFA: (call, callback) => { - processEvent('promptMFA', call, callback, response => - api.PromptMFAResponse.create({ totpCode: response?.totpCode }) - ); + processEvent('promptMFA', call, callback); + }, + + getUsageReportingSettings: (call, callback) => { + processEvent('getUsageReportingSettings', call, callback); }, }; diff --git a/web/packages/teleterm/src/ui/tshdEvents.ts b/web/packages/teleterm/src/ui/tshdEvents.ts index 853f5096a7d25..3817982d76c0c 100644 --- a/web/packages/teleterm/src/ui/tshdEvents.ts +++ b/web/packages/teleterm/src/ui/tshdEvents.ts @@ -79,5 +79,13 @@ export function createTshdEventsContextBridgeService( return { totpCode }; }, + + getUsageReportingSettings: async () => { + return { + usageReportingSettings: { + enabled: ctx.configService.get('usageReporting.enabled').value, + }, + }; + }, }; } From 203f9e8c469e3829d86f5352a56affc03592b06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Wed, 5 Jun 2024 18:12:37 +0200 Subject: [PATCH 05/33] Report unexpected VNet shutdown from tsh daemon to Electron app (#42070) * Refactor VnetStatus to allow for third value with error In one of the subsequent commits, we're going to add the following member to the discriminated union. Defining the union this way will make it easier. { value: 'unexpected-shutdown'; errorMessage: string } * Notify Electron app about unexpected VNet shutdown * Handle unexpected shutdown in Electron app --- .../lib/teleterm/v1/tshd_events_service.pb.go | 247 ++++++++++++++---- .../v1/tshd_events_service_grpc.pb.go | 43 +++ .../v1/tshd_events_service_pb.client.ts | 21 ++ .../v1/tshd_events_service_pb.grpc-server.ts | 20 ++ .../lib/teleterm/v1/tshd_events_service_pb.ts | 96 ++++++- lib/teleterm/vnet/service.go | 33 ++- .../lib/teleterm/v1/tshd_events_service.proto | 19 ++ .../teleterm/src/services/tshdEvents/index.ts | 4 + .../TopBar/Connections/Connections.story.tsx | 31 +++ .../src/ui/TopBar/Connections/Connections.tsx | 2 +- .../src/ui/Vnet/VnetConnectionItem.tsx | 15 +- .../teleterm/src/ui/Vnet/VnetSliderStep.tsx | 18 +- .../teleterm/src/ui/Vnet/useVnetLauncher.tsx | 2 +- .../teleterm/src/ui/Vnet/vnetContext.test.tsx | 52 +++- .../teleterm/src/ui/Vnet/vnetContext.tsx | 50 +++- web/packages/teleterm/src/ui/appContext.ts | 33 ++- web/packages/teleterm/src/ui/tshdEvents.ts | 14 + web/packages/teleterm/src/ui/types.ts | 21 ++ 18 files changed, 637 insertions(+), 84 deletions(-) diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go index ae22188bbc280..ca1dc1a52d3c6 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go @@ -888,6 +888,95 @@ func (x *UsageReportingSettings) GetEnabled() bool { return false } +// Request for ReportUnexpectedVnetShutdown. +type ReportUnexpectedVnetShutdownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // error is the error message with which VNet was shut down. Technically it can be empty, so + // consumers should account for that. + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *ReportUnexpectedVnetShutdownRequest) Reset() { + *x = ReportUnexpectedVnetShutdownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReportUnexpectedVnetShutdownRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportUnexpectedVnetShutdownRequest) ProtoMessage() {} + +func (x *ReportUnexpectedVnetShutdownRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[15] + 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 ReportUnexpectedVnetShutdownRequest.ProtoReflect.Descriptor instead. +func (*ReportUnexpectedVnetShutdownRequest) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{15} +} + +func (x *ReportUnexpectedVnetShutdownRequest) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +// Response for ReportUnexpectedVnetShutdown. +type ReportUnexpectedVnetShutdownResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ReportUnexpectedVnetShutdownResponse) Reset() { + *x = ReportUnexpectedVnetShutdownResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReportUnexpectedVnetShutdownResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportUnexpectedVnetShutdownResponse) ProtoMessage() {} + +func (x *ReportUnexpectedVnetShutdownResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[16] + 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 ReportUnexpectedVnetShutdownResponse.ProtoReflect.Descriptor instead. +func (*ReportUnexpectedVnetShutdownResponse) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{16} +} + var File_teleport_lib_teleterm_v1_tshd_events_service_proto protoreflect.FileDescriptor var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = []byte{ @@ -996,55 +1085,71 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = []byte{ 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x32, - 0x9a, 0x05, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, - 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, - 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x12, 0x2a, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, - 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, + 0x3b, 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x24, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xba, 0x06, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, + 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, + 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x43, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, + 0x41, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, + 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, + 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, + 0x77, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, + 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, + 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, + 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, + 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1059,7 +1164,7 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP() []byt return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescData } -var file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_teleport_lib_teleterm_v1_tshd_events_service_proto_goTypes = []interface{}{ (*ReloginRequest)(nil), // 0: teleport.lib.teleterm.v1.ReloginRequest (*GatewayCertExpired)(nil), // 1: teleport.lib.teleterm.v1.GatewayCertExpired @@ -1076,6 +1181,8 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_goTypes = []interfac (*GetUsageReportingSettingsRequest)(nil), // 12: teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest (*GetUsageReportingSettingsResponse)(nil), // 13: teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse (*UsageReportingSettings)(nil), // 14: teleport.lib.teleterm.v1.UsageReportingSettings + (*ReportUnexpectedVnetShutdownRequest)(nil), // 15: teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest + (*ReportUnexpectedVnetShutdownResponse)(nil), // 16: teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse } var file_teleport_lib_teleterm_v1_tshd_events_service_proto_depIdxs = []int32{ 1, // 0: teleport.lib.teleterm.v1.ReloginRequest.gateway_cert_expired:type_name -> teleport.lib.teleterm.v1.GatewayCertExpired @@ -1088,13 +1195,15 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_depIdxs = []int32{ 8, // 7: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:input_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationRequest 10, // 8: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:input_type -> teleport.lib.teleterm.v1.PromptMFARequest 12, // 9: teleport.lib.teleterm.v1.TshdEventsService.GetUsageReportingSettings:input_type -> teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest - 3, // 10: teleport.lib.teleterm.v1.TshdEventsService.Relogin:output_type -> teleport.lib.teleterm.v1.ReloginResponse - 7, // 11: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:output_type -> teleport.lib.teleterm.v1.SendNotificationResponse - 9, // 12: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:output_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse - 11, // 13: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:output_type -> teleport.lib.teleterm.v1.PromptMFAResponse - 13, // 14: teleport.lib.teleterm.v1.TshdEventsService.GetUsageReportingSettings:output_type -> teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse - 10, // [10:15] is the sub-list for method output_type - 5, // [5:10] is the sub-list for method input_type + 15, // 10: teleport.lib.teleterm.v1.TshdEventsService.ReportUnexpectedVnetShutdown:input_type -> teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest + 3, // 11: teleport.lib.teleterm.v1.TshdEventsService.Relogin:output_type -> teleport.lib.teleterm.v1.ReloginResponse + 7, // 12: teleport.lib.teleterm.v1.TshdEventsService.SendNotification:output_type -> teleport.lib.teleterm.v1.SendNotificationResponse + 9, // 13: teleport.lib.teleterm.v1.TshdEventsService.SendPendingHeadlessAuthentication:output_type -> teleport.lib.teleterm.v1.SendPendingHeadlessAuthenticationResponse + 11, // 14: teleport.lib.teleterm.v1.TshdEventsService.PromptMFA:output_type -> teleport.lib.teleterm.v1.PromptMFAResponse + 13, // 15: teleport.lib.teleterm.v1.TshdEventsService.GetUsageReportingSettings:output_type -> teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse + 16, // 16: teleport.lib.teleterm.v1.TshdEventsService.ReportUnexpectedVnetShutdown:output_type -> teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse + 11, // [11:17] is the sub-list for method output_type + 5, // [5:11] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name @@ -1286,6 +1395,30 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_init() { return nil } } + file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReportUnexpectedVnetShutdownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReportUnexpectedVnetShutdownResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_teleport_lib_teleterm_v1_tshd_events_service_proto_msgTypes[0].OneofWrappers = []interface{}{ (*ReloginRequest_GatewayCertExpired)(nil), @@ -1301,7 +1434,7 @@ func file_teleport_lib_teleterm_v1_tshd_events_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 17, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go index 1b01791bc6761..c4fb9657f6d10 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go @@ -41,6 +41,7 @@ const ( TshdEventsService_SendPendingHeadlessAuthentication_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/SendPendingHeadlessAuthentication" TshdEventsService_PromptMFA_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/PromptMFA" TshdEventsService_GetUsageReportingSettings_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/GetUsageReportingSettings" + TshdEventsService_ReportUnexpectedVnetShutdown_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/ReportUnexpectedVnetShutdown" ) // TshdEventsServiceClient is the client API for TshdEventsService service. @@ -67,6 +68,10 @@ type TshdEventsServiceClient interface { // with the only exception being the first start of the app when they're prompted about telemetry. // Hence why this is an RPC and not information passed over argv to tsh daemon. GetUsageReportingSettings(ctx context.Context, in *GetUsageReportingSettingsRequest, opts ...grpc.CallOption) (*GetUsageReportingSettingsResponse, error) + // ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + // request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + // update the state of VNet in the UI. + ReportUnexpectedVnetShutdown(ctx context.Context, in *ReportUnexpectedVnetShutdownRequest, opts ...grpc.CallOption) (*ReportUnexpectedVnetShutdownResponse, error) } type tshdEventsServiceClient struct { @@ -122,6 +127,15 @@ func (c *tshdEventsServiceClient) GetUsageReportingSettings(ctx context.Context, return out, nil } +func (c *tshdEventsServiceClient) ReportUnexpectedVnetShutdown(ctx context.Context, in *ReportUnexpectedVnetShutdownRequest, opts ...grpc.CallOption) (*ReportUnexpectedVnetShutdownResponse, error) { + out := new(ReportUnexpectedVnetShutdownResponse) + err := c.cc.Invoke(ctx, TshdEventsService_ReportUnexpectedVnetShutdown_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TshdEventsServiceServer is the server API for TshdEventsService service. // All implementations must embed UnimplementedTshdEventsServiceServer // for forward compatibility @@ -146,6 +160,10 @@ type TshdEventsServiceServer interface { // with the only exception being the first start of the app when they're prompted about telemetry. // Hence why this is an RPC and not information passed over argv to tsh daemon. GetUsageReportingSettings(context.Context, *GetUsageReportingSettingsRequest) (*GetUsageReportingSettingsResponse, error) + // ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + // request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + // update the state of VNet in the UI. + ReportUnexpectedVnetShutdown(context.Context, *ReportUnexpectedVnetShutdownRequest) (*ReportUnexpectedVnetShutdownResponse, error) mustEmbedUnimplementedTshdEventsServiceServer() } @@ -168,6 +186,9 @@ func (UnimplementedTshdEventsServiceServer) PromptMFA(context.Context, *PromptMF func (UnimplementedTshdEventsServiceServer) GetUsageReportingSettings(context.Context, *GetUsageReportingSettingsRequest) (*GetUsageReportingSettingsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUsageReportingSettings not implemented") } +func (UnimplementedTshdEventsServiceServer) ReportUnexpectedVnetShutdown(context.Context, *ReportUnexpectedVnetShutdownRequest) (*ReportUnexpectedVnetShutdownResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReportUnexpectedVnetShutdown not implemented") +} func (UnimplementedTshdEventsServiceServer) mustEmbedUnimplementedTshdEventsServiceServer() {} // UnsafeTshdEventsServiceServer may be embedded to opt out of forward compatibility for this service. @@ -271,6 +292,24 @@ func _TshdEventsService_GetUsageReportingSettings_Handler(srv interface{}, ctx c return interceptor(ctx, in, info, handler) } +func _TshdEventsService_ReportUnexpectedVnetShutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReportUnexpectedVnetShutdownRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TshdEventsServiceServer).ReportUnexpectedVnetShutdown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TshdEventsService_ReportUnexpectedVnetShutdown_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TshdEventsServiceServer).ReportUnexpectedVnetShutdown(ctx, req.(*ReportUnexpectedVnetShutdownRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TshdEventsService_ServiceDesc is the grpc.ServiceDesc for TshdEventsService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -298,6 +337,10 @@ var TshdEventsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetUsageReportingSettings", Handler: _TshdEventsService_GetUsageReportingSettings_Handler, }, + { + MethodName: "ReportUnexpectedVnetShutdown", + Handler: _TshdEventsService_ReportUnexpectedVnetShutdown_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/lib/teleterm/v1/tshd_events_service.proto", diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts index bbe5f29260b7e..762ad049fc740 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.client.ts @@ -24,6 +24,8 @@ import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; import { TshdEventsService } from "./tshd_events_service_pb"; +import type { ReportUnexpectedVnetShutdownResponse } from "./tshd_events_service_pb"; +import type { ReportUnexpectedVnetShutdownRequest } from "./tshd_events_service_pb"; import type { GetUsageReportingSettingsResponse } from "./tshd_events_service_pb"; import type { GetUsageReportingSettingsRequest } from "./tshd_events_service_pb"; import type { PromptMFAResponse } from "./tshd_events_service_pb"; @@ -84,6 +86,14 @@ export interface ITshdEventsServiceClient { * @generated from protobuf rpc: GetUsageReportingSettings(teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest) returns (teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse); */ getUsageReportingSettings(input: GetUsageReportingSettingsRequest, options?: RpcOptions): UnaryCall; + /** + * ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + * request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + * update the state of VNet in the UI. + * + * @generated from protobuf rpc: ReportUnexpectedVnetShutdown(teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest) returns (teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse); + */ + reportUnexpectedVnetShutdown(input: ReportUnexpectedVnetShutdownRequest, options?: RpcOptions): UnaryCall; } /** * TshdEventsService is served by the Electron app. The tsh daemon calls this service to notify the @@ -152,4 +162,15 @@ export class TshdEventsServiceClient implements ITshdEventsServiceClient, Servic const method = this.methods[4], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + * request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + * update the state of VNet in the UI. + * + * @generated from protobuf rpc: ReportUnexpectedVnetShutdown(teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest) returns (teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse); + */ + reportUnexpectedVnetShutdown(input: ReportUnexpectedVnetShutdownRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[5], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } } diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts index 544679c3967b4..7f71d9adf0d4a 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server.ts @@ -21,6 +21,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // +import { ReportUnexpectedVnetShutdownResponse } from "./tshd_events_service_pb"; +import { ReportUnexpectedVnetShutdownRequest } from "./tshd_events_service_pb"; import { GetUsageReportingSettingsResponse } from "./tshd_events_service_pb"; import { GetUsageReportingSettingsRequest } from "./tshd_events_service_pb"; import { PromptMFAResponse } from "./tshd_events_service_pb"; @@ -79,6 +81,14 @@ export interface ITshdEventsService extends grpc.UntypedServiceImplementation { * @generated from protobuf rpc: GetUsageReportingSettings(teleport.lib.teleterm.v1.GetUsageReportingSettingsRequest) returns (teleport.lib.teleterm.v1.GetUsageReportingSettingsResponse); */ getUsageReportingSettings: grpc.handleUnaryCall; + /** + * ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + * request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + * update the state of VNet in the UI. + * + * @generated from protobuf rpc: ReportUnexpectedVnetShutdown(teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest) returns (teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse); + */ + reportUnexpectedVnetShutdown: grpc.handleUnaryCall; } /** * @grpc/grpc-js definition for the protobuf service teleport.lib.teleterm.v1.TshdEventsService. @@ -141,5 +151,15 @@ export const tshdEventsServiceDefinition: grpc.ServiceDefinition GetUsageReportingSettingsRequest.fromBinary(bytes), responseSerialize: value => Buffer.from(GetUsageReportingSettingsResponse.toBinary(value)), requestSerialize: value => Buffer.from(GetUsageReportingSettingsRequest.toBinary(value)) + }, + reportUnexpectedVnetShutdown: { + path: "/teleport.lib.teleterm.v1.TshdEventsService/ReportUnexpectedVnetShutdown", + originalName: "ReportUnexpectedVnetShutdown", + requestStream: false, + responseStream: false, + responseDeserialize: bytes => ReportUnexpectedVnetShutdownResponse.fromBinary(bytes), + requestDeserialize: bytes => ReportUnexpectedVnetShutdownRequest.fromBinary(bytes), + responseSerialize: value => Buffer.from(ReportUnexpectedVnetShutdownResponse.toBinary(value)), + requestSerialize: value => Buffer.from(ReportUnexpectedVnetShutdownRequest.toBinary(value)) } }; diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts index 522488ec64ca0..5d85580f82e0c 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts @@ -268,6 +268,27 @@ export interface UsageReportingSettings { */ enabled: boolean; } +/** + * Request for ReportUnexpectedVnetShutdown. + * + * @generated from protobuf message teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest + */ +export interface ReportUnexpectedVnetShutdownRequest { + /** + * error is the error message with which VNet was shut down. Technically it can be empty, so + * consumers should account for that. + * + * @generated from protobuf field: string error = 1; + */ + error: string; +} +/** + * Response for ReportUnexpectedVnetShutdown. + * + * @generated from protobuf message teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse + */ +export interface ReportUnexpectedVnetShutdownResponse { +} // @generated message type with reflection information, may provide speed optimized methods class ReloginRequest$Type extends MessageType { constructor() { @@ -990,6 +1011,78 @@ class UsageReportingSettings$Type extends MessageType { * @generated MessageType for protobuf message teleport.lib.teleterm.v1.UsageReportingSettings */ export const UsageReportingSettings = new UsageReportingSettings$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ReportUnexpectedVnetShutdownRequest$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest", [ + { no: 1, name: "error", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): ReportUnexpectedVnetShutdownRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + message.error = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ReportUnexpectedVnetShutdownRequest): ReportUnexpectedVnetShutdownRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string error */ 1: + message.error = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ReportUnexpectedVnetShutdownRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string error = 1; */ + if (message.error !== "") + writer.tag(1, WireType.LengthDelimited).string(message.error); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownRequest + */ +export const ReportUnexpectedVnetShutdownRequest = new ReportUnexpectedVnetShutdownRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ReportUnexpectedVnetShutdownResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse", []); + } + create(value?: PartialMessage): ReportUnexpectedVnetShutdownResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ReportUnexpectedVnetShutdownResponse): ReportUnexpectedVnetShutdownResponse { + return target ?? this.create(); + } + internalBinaryWrite(message: ReportUnexpectedVnetShutdownResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.v1.ReportUnexpectedVnetShutdownResponse + */ +export const ReportUnexpectedVnetShutdownResponse = new ReportUnexpectedVnetShutdownResponse$Type(); /** * @generated ServiceType for protobuf service teleport.lib.teleterm.v1.TshdEventsService */ @@ -998,5 +1091,6 @@ export const TshdEventsService = new ServiceType("teleport.lib.teleterm.v1.TshdE { name: "SendNotification", options: {}, I: SendNotificationRequest, O: SendNotificationResponse }, { name: "SendPendingHeadlessAuthentication", options: {}, I: SendPendingHeadlessAuthenticationRequest, O: SendPendingHeadlessAuthenticationResponse }, { name: "PromptMFA", options: {}, I: PromptMFARequest, O: PromptMFAResponse }, - { name: "GetUsageReportingSettings", options: {}, I: GetUsageReportingSettingsRequest, O: GetUsageReportingSettingsResponse } + { name: "GetUsageReportingSettings", options: {}, I: GetUsageReportingSettingsRequest, O: GetUsageReportingSettingsResponse }, + { name: "ReportUnexpectedVnetShutdown", options: {}, I: ReportUnexpectedVnetShutdownRequest, O: ReportUnexpectedVnetShutdownResponse } ]); diff --git a/lib/teleterm/vnet/service.go b/lib/teleterm/vnet/service.go index 7cce77f7f22d2..df45207ff4835 100644 --- a/lib/teleterm/vnet/service.go +++ b/lib/teleterm/vnet/service.go @@ -22,6 +22,7 @@ import ( "errors" "sync" "sync/atomic" + "time" "github.com/gravitational/trace" "google.golang.org/protobuf/types/known/timestamppb" @@ -163,15 +164,22 @@ func (s *Service) Start(ctx context.Context, req *api.StartRequest) (*api.StartR log.DebugContext(ctx, "VNet closed") } - // TODO(ravicious): Notify the Electron app about change of VNet state, but only if it's - // running. If it's not running, then the Start RPC has already failed and forwarded the error - // to the user. - s.mu.Lock() defer s.mu.Unlock() + // Handle unexpected shutdown. + // If processManager.Wait has returned but status is stil "running", then it means that VNet + // unexpectedly shut down rather than stopped through the Stop RPC. if s.status == statusRunning { s.status = statusNotRunning + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + reportErr := s.reportUnexpectedShutdown(ctx, err) + if reportErr != nil { + log.ErrorContext(ctx, "Could not notify the Electron app about unexpected VNet shutdown", + "shutdown_error", err, "notify_error", reportErr) + } } }() @@ -243,6 +251,23 @@ func (s *Service) isUsageReportingEnabled(ctx context.Context) (bool, error) { return resp.UsageReportingSettings.Enabled, nil } +func (s *Service) reportUnexpectedShutdown(ctx context.Context, shutdownErr error) error { + tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient() + if err != nil { + return trace.Wrap(err, "obtaining tshd events client") + } + + var shutdownErrorMsg string + if shutdownErr != nil { + shutdownErrorMsg = shutdownErr.Error() + } + + _, err = tshdEventsClient.ReportUnexpectedVnetShutdown(ctx, &apiteleterm.ReportUnexpectedVnetShutdownRequest{ + Error: shutdownErrorMsg, + }) + return trace.Wrap(err, "sending shutdown report") +} + type appProvider struct { daemonService *daemon.Service usageReporter usageReporter diff --git a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto index b64594a16b40f..0663f68146566 100644 --- a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto +++ b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto @@ -28,23 +28,32 @@ service TshdEventsService { // Relogin makes the Electron app display a login modal for the specific root cluster. The request // returns a response after the relogin procedure has been successfully finished. rpc Relogin(ReloginRequest) returns (ReloginResponse); + // SendNotification causes the Electron app to display a notification in the UI. The request // accepts a specific message rather than a generic string so that the Electron is in control as // to what message is displayed and how exactly it looks. rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse); + // SendPendingHeadlessAuthentication notifies the Electron app of a pending headless authentication, // which it can use to initiate headless authentication resolution in the UI. rpc SendPendingHeadlessAuthentication(SendPendingHeadlessAuthenticationRequest) returns (SendPendingHeadlessAuthenticationResponse); + // PromptMFA notifies the Electron app that the daemon is waiting for the user to answer an MFA prompt. // If Webauthn is supported, tsh daemon starts another goroutine which readies the hardware key. // If TOTP is supported, tsh daemon expects that the Electron app responds to this RPC with the // code. rpc PromptMFA(PromptMFARequest) returns (PromptMFAResponse); + // GetUsageReportingSettings returns the current state of usage reporting. // At the moment, the user cannot toggle usage reporting on and off without shutting down the app, // with the only exception being the first start of the app when they're prompted about telemetry. // Hence why this is an RPC and not information passed over argv to tsh daemon. rpc GetUsageReportingSettings(GetUsageReportingSettingsRequest) returns (GetUsageReportingSettingsResponse); + + // ReportUnexpectedVnetShutdown is sent by tsh daemon when VNet exits outside of the + // request-response cycle of Start and Stop RPCs of VnetService. The Electron app is then able to + // update the state of VNet in the UI. + rpc ReportUnexpectedVnetShutdown(ReportUnexpectedVnetShutdownRequest) returns (ReportUnexpectedVnetShutdownResponse); } // Request for Relogin. @@ -148,3 +157,13 @@ message GetUsageReportingSettingsResponse { message UsageReportingSettings { bool enabled = 1; } + +// Request for ReportUnexpectedVnetShutdown. +message ReportUnexpectedVnetShutdownRequest { + // error is the error message with which VNet was shut down. Technically it can be empty, so + // consumers should account for that. + string error = 1; +} + +// Response for ReportUnexpectedVnetShutdown. +message ReportUnexpectedVnetShutdownResponse {} diff --git a/web/packages/teleterm/src/services/tshdEvents/index.ts b/web/packages/teleterm/src/services/tshdEvents/index.ts index 84c2df2d0fd52..6b6d5a2edb60d 100644 --- a/web/packages/teleterm/src/services/tshdEvents/index.ts +++ b/web/packages/teleterm/src/services/tshdEvents/index.ts @@ -223,6 +223,10 @@ function createService(logger: Logger): { getUsageReportingSettings: (call, callback) => { processEvent('getUsageReportingSettings', call, callback); }, + + reportUnexpectedVnetShutdown: (call, callback) => { + processEvent('reportUnexpectedVnetShutdown', call, callback); + }, }; return { service, setupTshdEventContextBridgeService }; diff --git a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx index 47fe727ab873f..21b2654f8e32f 100644 --- a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx +++ b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx @@ -150,6 +150,37 @@ export function VnetError() { ); } +export function VnetUnexpectedShutdown() { + const appContext = new MockAppContext(); + prepareAppContext(appContext); + + appContext.statePersistenceService.putState({ + ...appContext.statePersistenceService.getState(), + vnet: { autoStart: true }, + }); + appContext.workspacesService.setState(draft => { + draft.isInitialized = true; + }); + appContext.vnet.start = () => { + setTimeout(() => { + appContext.unexpectedVnetShutdownListener({ + error: 'lorem ipsum dolor sit amet', + }); + }, 0); + return new MockedUnaryCall({}); + }; + + return ( + + + + + + + + ); +} + export function WithScroll() { const appContext = new MockAppContext(); prepareAppContext(appContext); diff --git a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.tsx b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.tsx index 388ca1ff9c0e5..52422d8e7008e 100644 --- a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.tsx +++ b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.tsx @@ -36,7 +36,7 @@ export function Connections() { const { status: vnetStatus } = useVnetContext(); const isAnyConnectionActive = connectionTracker.getConnections().some(c => c.connected) || - vnetStatus === 'running'; + vnetStatus.value === 'running'; useKeyboardShortcuts( useMemo( diff --git a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx index 744f46c4e390b..50d626ce8ffc6 100644 --- a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx +++ b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx @@ -86,9 +86,12 @@ const VnetConnectionItemBase = forwardRef( startAttempt.status === 'processing' || stopAttempt.status === 'processing'; const indicatorStatus = - startAttempt.status === 'error' || stopAttempt.status === 'error' + startAttempt.status === 'error' || + stopAttempt.status === 'error' || + (status.value === 'stopped' && + status.reason.value === 'unexpected-shutdown') ? 'error' - : status === 'running' + : status.value === 'running' ? 'on' : 'off'; @@ -186,7 +189,9 @@ const VnetConnectionItemBase = forwardRef( // transitions and the test won't be able to catch this. { e.stopPropagation(); }} @@ -201,7 +206,7 @@ const VnetConnectionItemBase = forwardRef( /> )} - {!isProcessing && status === 'running' && ( + {!isProcessing && status.value === 'running' && ( )} - {!isProcessing && status === 'stopped' && ( + {!isProcessing && status.value === 'stopped' && ( { Could not stop VNet: {stopAttempt.statusText} )} - {status === 'stopped' && ( - VNet automatically authenticates connections to TCP apps. - )} + {status.value === 'stopped' && + (status.reason.value === 'unexpected-shutdown' ? ( + + VNet unexpectedly shut down:{' '} + {status.reason.errorMessage || + 'no direct reason was given, please check logs'} + . + + ) : ( + + VNet automatically authenticates connections to TCP apps. + + ))} - {status === 'running' && + {status.value === 'running' && (rootClusters.length === 0 ? ( No clusters connected yet, VNet is not proxying any connections. diff --git a/web/packages/teleterm/src/ui/Vnet/useVnetLauncher.tsx b/web/packages/teleterm/src/ui/Vnet/useVnetLauncher.tsx index 0e67745619951..abe0e1c0d99e4 100644 --- a/web/packages/teleterm/src/ui/Vnet/useVnetLauncher.tsx +++ b/web/packages/teleterm/src/ui/Vnet/useVnetLauncher.tsx @@ -27,7 +27,7 @@ export const useVnetLauncher = (): (() => Promise<[void, Error]>) => { const { open } = useConnectionsContext(); return useCallback(() => { - if (status === 'running' || startAttempt.status === 'processing') { + if (status.value === 'running' || startAttempt.status === 'processing') { return Promise.resolve([undefined, undefined]); } diff --git a/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx b/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx index bf42c27fd3120..21360ee5e1872 100644 --- a/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx +++ b/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx @@ -24,7 +24,12 @@ import { IAppContext } from 'teleterm/ui/types'; import { MockAppContext } from 'teleterm/ui/fixtures/mocks'; import { MockedUnaryCall } from 'teleterm/services/tshd/cloneableClient'; -import { VnetContextProvider, useVnetContext } from './vnetContext'; +import { + VnetContextProvider, + VnetStatus, + VnetStoppedReason, + useVnetContext, +} from './vnetContext'; describe('autostart', () => { it('starts VNet if turned on', async () => { @@ -143,6 +148,51 @@ describe('autostart', () => { }); }); +it('registers a callback for unexpected shutdown', async () => { + const appContext = new MockAppContext(); + appContext.workspacesService.setState(draft => { + draft.isInitialized = true; + }); + appContext.statePersistenceService.putState({ + ...appContext.statePersistenceService.getState(), + vnet: { autoStart: true }, + }); + + const { result } = renderHook(() => useVnetContext(), { + wrapper: createWrapper(Wrapper, { appContext }), + }); + + await waitFor( + () => expect(result.current.startAttempt.status).toEqual('success'), + { interval: 5 } + ); + + // Trigger unexpected shutdown. + act(() => { + appContext.unexpectedVnetShutdownListener({ + error: 'lorem ipsum dolor sit amet', + }); + }); + + await waitFor( + () => { + expect(result.current.status.value).toEqual('stopped'); + }, + { interval: 5 } + ); + + const status = result.current.status as Extract< + VnetStatus, + { value: 'stopped' } + >; + expect(status.reason.value).toEqual('unexpected-shutdown'); + const reason = status.reason as Extract< + VnetStoppedReason, + { value: 'unexpected-shutdown' } + >; + expect(reason.errorMessage).toEqual('lorem ipsum dolor sit amet'); +}); + const Wrapper = (props: PropsWithChildren<{ appContext: IAppContext }>) => ( {props.children} diff --git a/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx b/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx index 32898fd197553..4db7891fc3310 100644 --- a/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx +++ b/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx @@ -49,13 +49,23 @@ export type VnetContext = { stopAttempt: Attempt; }; -export type VnetStatus = 'running' | 'stopped'; +export type VnetStatus = + | { value: 'running' } + | { value: 'stopped'; reason: VnetStoppedReason }; + +export type VnetStoppedReason = + | { value: 'regular-shutdown-or-not-started' } + | { value: 'unexpected-shutdown'; errorMessage: string }; export const VnetContext = createContext(null); export const VnetContextProvider: FC = props => { - const [status, setStatus] = useState('stopped'); - const { vnet, mainProcessClient } = useAppContext(); + const [status, setStatus] = useState({ + value: 'stopped', + reason: { value: 'regular-shutdown-or-not-started' }, + }); + const appCtx = useAppContext(); + const { vnet, mainProcessClient, notificationsService } = appCtx; const isWorkspaceStateInitialized = useStoreSelector( 'workspacesService', useCallback(state => state.isInitialized, []) @@ -71,12 +81,8 @@ export const VnetContextProvider: FC = props => { const [startAttempt, start] = useAsync( useCallback(async () => { - // TODO(ravicious): If the osascript dialog was canceled, do not throw an error and instead - // just don't update status. Perhaps even revert back attempt status if possible. - // - // Reconsider this only once the VNet daemon gets added. await vnet.start({}); - setStatus('running'); + setStatus({ value: 'running' }); setAppState({ autoStart: true }); }, [vnet, setAppState]) ); @@ -84,7 +90,10 @@ export const VnetContextProvider: FC = props => { const [stopAttempt, stop] = useAsync( useCallback(async () => { await vnet.stop({}); - setStatus('stopped'); + setStatus({ + value: 'stopped', + reason: { value: 'regular-shutdown-or-not-started' }, + }); setAppState({ autoStart: false }); }, [vnet, setAppState]) ); @@ -110,6 +119,29 @@ export const VnetContextProvider: FC = props => { handleAutoStart(); }, [isWorkspaceStateInitialized]); + useEffect( + function handleUnexpectedShutdown() { + const removeListener = appCtx.addUnexpectedVnetShutdownListener( + ({ error }) => { + setStatus({ + value: 'stopped', + reason: { value: 'unexpected-shutdown', errorMessage: error }, + }); + + notificationsService.notifyError({ + title: 'VNet has unexpectedly shut down', + description: error + ? `Reason: ${error}` + : 'No reason was given, check the logs for more details.', + }); + } + ); + + return removeListener; + }, + [appCtx, notificationsService] + ); + return ( void { + this._unexpectedVnetShutdownListener = listener; + + return () => { + this._unexpectedVnetShutdownListener = undefined; + }; + } + + /** + * unexpectedVnetShutdownListener gets called by tshd events service when it gets a report about + * said shutdown from tsh daemon. + * + * The communication between tshd events service and VnetContext is done through a callback on + * AppContext. That's because tshd events service lives outside of React but within the same + * process (renderer). + */ + // To force callsites to use addUnexpectedVnetShutdownListener instead of setting the property + // directly on appContext, we use a getter which exposes a private property. + get unexpectedVnetShutdownListener(): UnexpectedVnetShutdownListener { + return this._unexpectedVnetShutdownListener; + } + private subscribeToDeepLinkLaunch() { this.mainProcessClient.subscribeToDeepLinkLaunch(result => { this.deepLinksService.launchDeepLink(result).catch(error => { diff --git a/web/packages/teleterm/src/ui/tshdEvents.ts b/web/packages/teleterm/src/ui/tshdEvents.ts index 3817982d76c0c..c48e48faa1481 100644 --- a/web/packages/teleterm/src/ui/tshdEvents.ts +++ b/web/packages/teleterm/src/ui/tshdEvents.ts @@ -18,10 +18,13 @@ import { TshdEventContextBridgeService } from 'teleterm/types'; import { IAppContext } from 'teleterm/ui/types'; +import Logger from 'teleterm/logger'; export function createTshdEventsContextBridgeService( ctx: IAppContext ): TshdEventContextBridgeService { + const logger = new Logger('tshd events UI'); + return { relogin: async ({ request, onRequestCancelled }) => { await ctx.reloginService.relogin(request, onRequestCancelled); @@ -87,5 +90,16 @@ export function createTshdEventsContextBridgeService( }, }; }, + + reportUnexpectedVnetShutdown: async ({ request }) => { + if (!ctx.unexpectedVnetShutdownListener) { + logger.warn( + `Dropping unexpected VNet shutdown event, no listener present; error: ${request.error}` + ); + } else { + ctx.unexpectedVnetShutdownListener(request); + } + return {}; + }, }; } diff --git a/web/packages/teleterm/src/ui/types.ts b/web/packages/teleterm/src/ui/types.ts index ae1c243007c07..e68c0412c5372 100644 --- a/web/packages/teleterm/src/ui/types.ts +++ b/web/packages/teleterm/src/ui/types.ts @@ -15,6 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import * as tshdEventsApi from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb'; import { MainProcessClient, @@ -65,4 +66,24 @@ export interface IAppContext { vnet: VnetClient; pullInitialState(): Promise; + + /** + * addUnexpectedVnetShutdownListener sets the listener and returns a cleanup function. + */ + addUnexpectedVnetShutdownListener: ( + listener: UnexpectedVnetShutdownListener + ) => () => void; + /** + * unexpectedVnetShutdownListener gets called by tshd events service when it gets a report about + * said shutdown from tsh daemon. + * + * The communication between tshd events service and VnetContext is done through a callback on + * AppContext. That's because tshd events service lives outside of React but within the same + * process (renderer). + */ + unexpectedVnetShutdownListener: UnexpectedVnetShutdownListener | undefined; } + +export type UnexpectedVnetShutdownListener = ( + request: tshdEventsApi.ReportUnexpectedVnetShutdownRequest +) => void; From 6019c41f1b8628858d112215e0995bebe4410a8d Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Wed, 5 Jun 2024 10:53:39 -0700 Subject: [PATCH 06/33] Web UI tweaks (#42500) - remove disabling auto discover toggle for RDS when theres nothing in table - use the correct default value for css pointer-events field - fix reading previous data while loading next data for locks table --- .../EnrollRdsDatabase/EnrollRdsDatabase.tsx | 9 +-------- .../teleport/src/Integrations/Enroll/common.tsx | 2 +- .../ServerSideSupportedList.tsx | 15 +++++++-------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx index 69ee1c3647860..92162014307d8 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx @@ -379,7 +379,6 @@ export function EnrollRdsDatabase() { setWantAutoDiscover(b => !b)} - isDisabled={tableData.items.length === 0} discoveryGroupName={discoveryGroupName} setDiscoveryGroupName={setDiscoveryGroupName} clusterPublicUrl={ctx.storeUser.state.cluster.publicURL} @@ -443,13 +442,11 @@ function getRdsEngineIdentifier(engine: DatabaseEngine): RdsEngineIdentifier { function ToggleSection({ wantAutoDiscover, toggleWantAutoDiscover, - isDisabled, discoveryGroupName, setDiscoveryGroupName, clusterPublicUrl, }: { wantAutoDiscover: boolean; - isDisabled: boolean; toggleWantAutoDiscover(): void; discoveryGroupName: string; setDiscoveryGroupName(n: string): void; @@ -457,11 +454,7 @@ function ToggleSection({ }) { return ( - + Auto-enroll all databases for selected region diff --git a/web/packages/teleport/src/Integrations/Enroll/common.tsx b/web/packages/teleport/src/Integrations/Enroll/common.tsx index 32d0fe1db7a15..f505638ca434e 100644 --- a/web/packages/teleport/src/Integrations/Enroll/common.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/common.tsx @@ -34,7 +34,7 @@ export const IntegrationTile = styled(Flex)` cursor: pointer; ${props => { - const pointerEvents = props.disabled || props.$exists ? 'none' : null; + const pointerEvents = props.disabled || props.$exists ? 'none' : 'auto'; if (props.$exists) { return { pointerEvents }; } diff --git a/web/packages/teleport/src/LocksV2/NewLock/ResourceList/ServerSideSupportedList/ServerSideSupportedList.tsx b/web/packages/teleport/src/LocksV2/NewLock/ResourceList/ServerSideSupportedList/ServerSideSupportedList.tsx index d4306fe44194d..fc1f99b0af958 100644 --- a/web/packages/teleport/src/LocksV2/NewLock/ResourceList/ServerSideSupportedList/ServerSideSupportedList.tsx +++ b/web/packages/teleport/src/LocksV2/NewLock/ResourceList/ServerSideSupportedList/ServerSideSupportedList.tsx @@ -106,6 +106,9 @@ export function ServerSideSupportedList(props: CommonListProps) { } const table = useMemo(() => { + // If there is a fetchStatus, a fetching is going on. + // Show the loading indicator instead of trying to process previous data. + const resources = fetchStatus === 'loading' ? [] : fetchedData.agents; const listProps: ServerSideListProps = { fetchStatus, customSort: { @@ -120,21 +123,17 @@ export function ServerSideSupportedList(props: CommonListProps) { switch (props.selectedResourceKind) { case 'role': - return ( - - ); + return ; case 'node': - return ; + return ; case 'windows_desktop': - return ( - - ); + return ; default: console.error( `[ServerSideSupportedList.tsx] table not defined for resource kind ${props.selectedResourceKind}` ); } - }, [props.attempt, fetchedData, fetchStatus, props.selectedResources]); + }, [fetchedData, fetchStatus, props.selectedResources]); return ( Date: Wed, 5 Jun 2024 14:30:55 -0400 Subject: [PATCH 07/33] add support for access list notifications (#41662) --- .../src/Navigation/NavigationItem.test.tsx | 4 +- .../src/Navigation/NavigationItem.tsx | 4 +- .../src/Notifications/Notification.story.tsx | 2 + .../src/Notifications/Notification.tsx | 92 ++++---- .../src/Notifications/Notifications.test.tsx | 122 +++++++++++ .../src/Notifications/Notifications.tsx | 201 ++++++++++++++++-- .../notificationContentFactory.tsx | 5 +- .../Notifications/Notifications.test.tsx | 127 ----------- .../TopBar/Notifications/Notifications.tsx | 175 --------------- .../src/TopBar/Notifications/index.ts | 19 -- .../teleport/src/TopBar/TopBar.story.tsx | 8 +- .../teleport/src/TopBar/TopBar.test.tsx | 76 +++++-- .../src/services/notifications/types.ts | 24 ++- .../src/services/storageService/types.ts | 2 + .../src/stores/storeNotifications.test.ts | 24 +-- .../teleport/src/stores/storeNotifications.ts | 146 +++++++++++-- 16 files changed, 596 insertions(+), 435 deletions(-) create mode 100644 web/packages/teleport/src/Notifications/Notifications.test.tsx delete mode 100644 web/packages/teleport/src/TopBar/Notifications/Notifications.test.tsx delete mode 100644 web/packages/teleport/src/TopBar/Notifications/Notifications.tsx delete mode 100644 web/packages/teleport/src/TopBar/Notifications/index.ts diff --git a/web/packages/teleport/src/Navigation/NavigationItem.test.tsx b/web/packages/teleport/src/Navigation/NavigationItem.test.tsx index fbeb92bbd4e2c..6f812da3eba84 100644 --- a/web/packages/teleport/src/Navigation/NavigationItem.test.tsx +++ b/web/packages/teleport/src/Navigation/NavigationItem.test.tsx @@ -36,7 +36,7 @@ import { NavigationCategory } from 'teleport/Navigation/categories'; import { NavigationItem } from 'teleport/Navigation/NavigationItem'; import { NavigationItemSize } from 'teleport/Navigation/common'; import { makeUserContext } from 'teleport/services/user'; -import { NotificationKind } from 'teleport/stores/storeNotifications'; +import { LocalNotificationKind } from 'teleport/services/notifications'; class MockUserFeature implements TeleportFeature { category = NavigationCategory.Resources; @@ -142,7 +142,7 @@ describe('navigation items', () => { ctx.storeNotifications.setNotifications([ { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'banana', route: '', }, diff --git a/web/packages/teleport/src/Navigation/NavigationItem.tsx b/web/packages/teleport/src/Navigation/NavigationItem.tsx index 861de1b49805a..a95c7e78491ba 100644 --- a/web/packages/teleport/src/Navigation/NavigationItem.tsx +++ b/web/packages/teleport/src/Navigation/NavigationItem.tsx @@ -32,10 +32,10 @@ import { } from 'teleport/Navigation/common'; import useStickyClusterId from 'teleport/useStickyClusterId'; import { storageService } from 'teleport/services/storageService'; +import { LocalNotificationKind } from 'teleport/services/notifications'; import { useTeleport } from 'teleport'; import { NavTitle, RecommendationStatus } from 'teleport/types'; -import { NotificationKind } from 'teleport/stores/storeNotifications'; import type { TeleportFeature, @@ -196,7 +196,7 @@ export function NavigationItem(props: NavigationItemProps) { function renderHighlightFeature(featureName: NavTitle): JSX.Element { if (featureName === NavTitle.AccessLists) { const hasNotifications = ctx.storeNotifications.hasNotificationsByKind( - NotificationKind.AccessList + LocalNotificationKind.AccessList ); if (hasNotifications) { diff --git a/web/packages/teleport/src/Notifications/Notification.story.tsx b/web/packages/teleport/src/Notifications/Notification.story.tsx index 810b16352508e..39f0b0fcc8f74 100644 --- a/web/packages/teleport/src/Notifications/Notification.story.tsx +++ b/web/packages/teleport/src/Notifications/Notification.story.tsx @@ -69,6 +69,8 @@ export const NotificationTypes = () => { notification={notification} key={notification.id} closeNotificationsList={() => null} + markNotificationAsClicked={() => null} + removeNotification={() => null} /> ); })} diff --git a/web/packages/teleport/src/Notifications/Notification.tsx b/web/packages/teleport/src/Notifications/Notification.tsx index 8b6badf76d88f..8398b2b01442d 100644 --- a/web/packages/teleport/src/Notifications/Notification.tsx +++ b/web/packages/teleport/src/Notifications/Notification.tsx @@ -48,21 +48,23 @@ import useStickyClusterId from 'teleport/useStickyClusterId'; import { useTeleport } from '..'; import { NotificationContent } from './notificationContentFactory'; - import { View } from './Notifications'; export function Notification({ notification, view = 'All', closeNotificationsList, + removeNotification, + markNotificationAsClicked, }: { notification: NotificationType; view?: View; closeNotificationsList: () => void; + removeNotification: (notificationId: string) => void; + markNotificationAsClicked: (notificationId: string) => void; }) { const ctx = useTeleport(); const { clusterId } = useStickyClusterId(); - const [clicked, setClicked] = useState(notification.clicked); const content = ctx.notificationContentFactory(notification); @@ -73,29 +75,38 @@ export function Notification({ notificationState: NotificationState.CLICKED, }) .then(res => { - setClicked(true); + markNotificationAsClicked(notification.id); return res; }) ); const [hideNotificationAttempt, hideNotification] = useAsync(() => { - return ctx.notificationService.upsertNotificationState(clusterId, { - notificationId: notification.id, - notificationState: NotificationState.DISMISSED, - }); + return ctx.notificationService + .upsertNotificationState(clusterId, { + notificationId: notification.id, + notificationState: NotificationState.DISMISSED, + }) + .then(() => { + removeNotification(notification.id); + }); }); + + function onMarkAsClicked() { + if (notification.localNotification) { + ctx.storeNotifications.markNotificationAsClicked(notification.id); + markNotificationAsClicked(notification.id); + return; + } + markAsClicked(); + } + // Whether to show the text content dialog. This is only ever used for user-created notifications which only contain informational text // and don't redirect to any page. const [showTextContentDialog, setShowTextContentDialog] = useState(false); // If the notification is unsupported or hidden, or if the view is "Unread" and the notification has been read, // it should not be shown. - if ( - !content || - hideNotificationAttempt.status === 'success' || - hideNotificationAttempt.status === 'processing' || - (view === 'Unread' && clicked) - ) { + if (!content || (view === 'Unread' && notification.clicked)) { return null; } @@ -119,7 +130,6 @@ export function Notification({ const formattedDate = formatDate(notification.createdDate); function onNotificationClick(e: React.MouseEvent) { - markAsClicked(); // Prevents this from being triggered when the user is just clicking away from // an open "mark as read/hide this notification" menu popover. if (e.currentTarget.contains(e.target as HTMLElement)) { @@ -127,21 +137,19 @@ export function Notification({ setShowTextContentDialog(true); return; } + onMarkAsClicked(); closeNotificationsList(); history.push(content.redirectRoute); } } const isClicked = - clicked || - markAsClickedAttempt.status === 'processing' || - (markAsClickedAttempt.status === 'success' && - markAsClickedAttempt.data.notificationState === - NotificationState.CLICKED); + notification.clicked || markAsClickedAttempt.status === 'processing'; return ( <> {content.title} {content.kind === 'redirect' && content.QuickAction && ( - + )} {hideNotificationAttempt.status === 'error' && ( @@ -174,30 +182,34 @@ export function Notification({ )} - {formattedDate} - - {!isClicked && ( + {!content?.hideDate && ( + {formattedDate} + )} + {!notification.localNotification && ( + + {!isClicked && ( + + Mark as read + + )} - Mark as read + Hide this notification - )} - - Hide this notification - - + + )} diff --git a/web/packages/teleport/src/Notifications/Notifications.test.tsx b/web/packages/teleport/src/Notifications/Notifications.test.tsx new file mode 100644 index 0000000000000..35f4dbb778ca4 --- /dev/null +++ b/web/packages/teleport/src/Notifications/Notifications.test.tsx @@ -0,0 +1,122 @@ +/** + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; +import { subMinutes, subSeconds } from 'date-fns'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router'; +import { render, screen } from 'design/utils/testing'; + +import { createTeleportContext } from 'teleport/mocks/contexts'; +import { LayoutContextProvider } from 'teleport/Main/LayoutContext'; + +import { FeaturesContextProvider } from 'teleport/FeaturesContext'; +import { getOSSFeatures } from 'teleport/features'; +import TeleportContextProvider from 'teleport/TeleportContextProvider'; +import TeleportContext from 'teleport/teleportContext'; +import { NotificationSubKind } from 'teleport/services/notifications'; + +import { Notifications } from './Notifications'; + +test('notification bell with notifications', async () => { + const ctx = createTeleportContext(); + + jest.spyOn(ctx.notificationService, 'fetchNotifications').mockResolvedValue({ + nextKey: '', + userLastSeenNotification: subMinutes(Date.now(), 12), // 12 minutes ago + notifications: [ + { + id: '1', + title: 'Example notification 1', + subKind: NotificationSubKind.UserCreatedInformational, + createdDate: subSeconds(Date.now(), 15), // 15 seconds ago + clicked: false, + labels: [ + { + name: 'text-content', + value: 'This is the text content of the notification.', + }, + ], + }, + { + id: '2', + title: 'Example notification 2', + subKind: NotificationSubKind.UserCreatedInformational, + createdDate: subSeconds(Date.now(), 50), // 50 seconds ago + clicked: false, + labels: [ + { + name: 'text-content', + value: 'This is the text content of the notification.', + }, + ], + }, + ], + }); + + jest + .spyOn(ctx.notificationService, 'upsertLastSeenNotificationTime') + .mockResolvedValue({ + time: new Date(), + }); + + render(renderNotifications(ctx)); + + await screen.findByTestId('tb-notifications-badge'); + + expect(screen.getByTestId('tb-notifications')).toBeInTheDocument(); + + // Expect there to be 2 notifications. + expect(screen.getByTestId('tb-notifications-badge')).toHaveTextContent('2'); + expect(screen.queryAllByTestId('notification-item')).toHaveLength(2); +}); + +test('notification bell with no notifications', async () => { + const ctx = createTeleportContext(); + jest.spyOn(ctx.notificationService, 'fetchNotifications').mockResolvedValue({ + nextKey: '', + userLastSeenNotification: subMinutes(Date.now(), 12), // 12 minutes ago + notifications: [], + }); + + jest + .spyOn(ctx.notificationService, 'upsertLastSeenNotificationTime') + .mockResolvedValue({ + time: new Date(), + }); + + render(renderNotifications(ctx)); + + await screen.findByText(/you currently have no notifications/i); + + expect(screen.queryByTestId('notification-item')).not.toBeInTheDocument(); +}); + +const renderNotifications = (ctx: TeleportContext) => { + return ( + + + + + + + + + + ); +}; diff --git a/web/packages/teleport/src/Notifications/Notifications.tsx b/web/packages/teleport/src/Notifications/Notifications.tsx index 709484b462be5..70e73bbd6a471 100644 --- a/web/packages/teleport/src/Notifications/Notifications.tsx +++ b/web/packages/teleport/src/Notifications/Notifications.tsx @@ -17,7 +17,7 @@ */ import React, { useState, useEffect, useCallback } from 'react'; -import { isBefore } from 'date-fns'; +import { isBefore, isAfter, formatDistanceToNowStrict } from 'date-fns'; import styled from 'styled-components'; import { Alert, Box, Flex, Indicator, Text } from 'design'; @@ -32,12 +32,25 @@ import { } from 'shared/hooks/useInfiniteScroll'; import { IGNORE_CLICK_CLASSNAME } from 'shared/hooks/useRefClickOutside/useRefClickOutside'; +import { useStore } from 'shared/libs/stores'; + import { useTeleport } from 'teleport'; import useStickyClusterId from 'teleport/useStickyClusterId'; import { Dropdown } from 'teleport/components/Dropdown'; import { ButtonIconContainer } from 'teleport/TopBar/Shared'; +import { + LocalNotificationGroupedKind, + LocalNotificationKind, + Notification as NotificationType, +} from 'teleport/services/notifications'; + +import { + Notification as AccessListNotification, + LocalNotificationStates, +} from 'teleport/stores/storeNotifications'; + import { Notification } from './Notification'; const PAGE_SIZE = 15; @@ -47,10 +60,27 @@ const logger = Logger.create('Notifications'); export function Notifications({ iconSize = 24 }: { iconSize?: number }) { const ctx = useTeleport(); const { clusterId } = useStickyClusterId(); - + const store = useStore(ctx.storeNotifications); const [userLastSeenNotification, setUserLastSeenNotification] = useState(); + const [combinedNotifications, setCombinedNotifications] = useState< + NotificationType[] + >([]); + + // Access list review reminder notifications aren't currently supported by the native notifications + // system, and so they won't be returned by the prior request. They are instead generated by the frontend + // and stored in a local store on the frontend. We retrieve these separately and then combine them with + // the "real" notifications from the backend. + const localNotifications = React.useMemo( + () => + accessListStoreNotificationsToNotifications( + store.getNotifications(), + store.getNotificationStates() + ), + [store.state] + ); + const { resources: notifications, fetch, @@ -67,6 +97,7 @@ export function Notifications({ iconSize = 24 }: { iconSize?: number }) { }); setUserLastSeenNotification(response.userLastSeenNotification); + return { agents: response.notifications, startKey: response.nextKey, @@ -81,6 +112,10 @@ export function Notifications({ iconSize = 24 }: { iconSize?: number }) { fetch(); }, []); + useEffect(() => { + setCombinedNotifications([...localNotifications, ...notifications]); + }, [localNotifications, notifications]); + const { setTrigger } = useInfiniteScroll({ fetch, }); @@ -94,6 +129,12 @@ export function Notifications({ iconSize = 24 }: { iconSize?: number }) { if (!open) { setOpen(true); + if (localNotifications.length) { + store.markNotificationsAsSeen( + localNotifications.map(notif => notif.id) + ); + } + if (notifications.length) { const latestNotificationTime = notifications[0].createdDate; // If the current userLastSeenNotification is already set to the most recent notification's time, don't do anything. @@ -121,14 +162,40 @@ export function Notifications({ iconSize = 24 }: { iconSize?: number }) { } } - const unseenNotifsCount = notifications.filter(notif => - isBefore(userLastSeenNotification, notif.createdDate) - ).length; + const unseenNotifsCount = combinedNotifications.filter(notif => { + if (notif.localNotification) { + const seenNotifications = store.getNotificationStates().seen; + + return !seenNotifications.includes(notif.id); + } + + return isBefore(userLastSeenNotification, notif.createdDate); + }).length; + + function removeNotification(notificationId: string) { + const notificationsCopy = [...combinedNotifications]; + const index = notificationsCopy.findIndex( + notif => notif.id == notificationId + ); + notificationsCopy.splice(index, 1); + + setCombinedNotifications(notificationsCopy); + } + + function markNotificationAsClicked(notificationId: string) { + const newNotifications = combinedNotifications.map(notification => { + return notification.id === notificationId + ? { ...notification, clicked: true } + : notification; + }); + + setCombinedNotifications(newNotifications); + } return ( {unseenNotifsCount > 0 && ( - + {unseenNotifsCount >= 9 ? '9+' : unseenNotifsCount} )} @@ -156,25 +223,30 @@ export function Notifications({ iconSize = 24 }: { iconSize?: number }) { - +
{attempt.status === 'failed' && ( Could not load notifications: {attempt.statusText} )} - {attempt.status === 'success' && notifications.length === 0 && ( + {attempt.status === 'success' && combinedNotifications.length === 0 && ( )} <> - {!!notifications.length && - notifications.map(notif => ( + {!!combinedNotifications.length && + combinedNotifications.map(notif => ( setOpen(false)} + markNotificationAsClicked={markNotificationAsClicked} + removeNotification={removeNotification} /> ))} {open &&
} @@ -276,6 +348,111 @@ function EmptyState() { ); } +/** accessListStoreNotificationsToNotifications converts a list of access list notifications from the notifications store into the primary + * Notification type used by the notifications list. + */ +function accessListStoreNotificationsToNotifications( + accessListNotifs: AccessListNotification[], + notificationStates: LocalNotificationStates +): NotificationType[] { + const today = new Date(); + + /** dueNotifications are the notifications for access lists which are due for review in the future. */ + const dueNotifications = accessListNotifs + .filter(notif => isAfter(notif.date, today)) + // Sort by earliest dates. + .sort((a, b) => { + return a.date.getTime() - b.date.getTime(); + }); + + /** overdueNotifications are the notifications for access lists which are overdue for review. */ + const overdueNotifications = accessListNotifs + .filter(notif => isBefore(notif.date, today)) + .sort((a, b) => { + return a.date.getTime() - b.date.getTime(); + }); + + const processedDueNotifications: NotificationType[] = []; + const processedOverdueNotifications: NotificationType[] = []; + + // If there are 2 or less access list notifications due for review, then we will return a notification per access list. + // If there are more than 2, then we return one "grouped" notification for all of them. This is to prevent clutter in the notifications list + // in case there are many access lists due for review. + if (dueNotifications.length <= 2) { + // Process and add them to the final processed array. + dueNotifications.forEach(notif => { + const numDays = formatDistanceToNowStrict(notif.date); + const titleText = `Access list '${notif.item.resourceName}' needs your review within ${numDays}.`; + + processedDueNotifications.push({ + localNotification: true, + title: titleText, + id: notif.id, + subKind: LocalNotificationKind.AccessList, + clicked: notif.clicked, + createdDate: today, + labels: [{ name: 'redirect-route', value: notif.item.route }], + }); + }); + } else { + const mostUrgentNotif = dueNotifications[0]; + const numDays = formatDistanceToNowStrict(mostUrgentNotif.date); + const titleText = `${dueNotifications.length} of your access lists require review, the most urgent of which is due in ${numDays}.`; + + // The ID for this combined notification is --. + const id = `${dueNotifications[0].id}-${dueNotifications[dueNotifications.length - 1].id}-${dueNotifications.length}`; + + const clicked = notificationStates.clicked.includes(id); + + processedDueNotifications.push({ + localNotification: true, + title: titleText, + id, + subKind: LocalNotificationGroupedKind.AccessListGrouping, + clicked, + createdDate: today, + labels: [], + }); + } + + if (overdueNotifications.length <= 2) { + // Process and add them to the final processed array. + overdueNotifications.forEach(notif => { + const numDays = formatDistanceToNowStrict(notif.date); + const titleText = `Your review of access list '${notif.item.resourceName}' is overdue by ${numDays}.`; + + processedOverdueNotifications.push({ + localNotification: true, + title: titleText, + id: notif.id, + subKind: LocalNotificationKind.AccessList, + clicked: notif.clicked, + createdDate: today, + labels: [{ name: 'redirect-route', value: notif.item.route }], + }); + }); + } else { + const titleText = `${overdueNotifications.length} of your access lists are overdue for review.`; + + // The ID for this combined notification is --. + const id = `${overdueNotifications[0].id}-${overdueNotifications[overdueNotifications.length - 1].id}-${overdueNotifications.length}`; + + const clicked = notificationStates.clicked.includes(id); + + processedOverdueNotifications.push({ + localNotification: true, + title: titleText, + id, + subKind: LocalNotificationGroupedKind.AccessListGrouping, + clicked, + createdDate: today, + labels: [], + }); + } + + return [...processedOverdueNotifications, ...processedDueNotifications]; +} + const NotificationsDropdown = styled(Dropdown)` width: 450px; padding: 0px; diff --git a/web/packages/teleport/src/Notifications/notificationContentFactory.tsx b/web/packages/teleport/src/Notifications/notificationContentFactory.tsx index ca26ed4056f75..bebf8ae862bf8 100644 --- a/web/packages/teleport/src/Notifications/notificationContentFactory.tsx +++ b/web/packages/teleport/src/Notifications/notificationContentFactory.tsx @@ -62,6 +62,7 @@ export function notificationContentFactory({ }; break; } + default: return null; } @@ -76,6 +77,8 @@ type NotificationContentBase = { type: 'success' | 'success-alt' | 'informational' | 'warning' | 'failure'; /** icon is the icon to render for this notification. This should be an icon from `design/Icon`. */ icon: React.FC; + /** hideDate is whether to not display how old the notification is in the top right corner of the notification. */ + hideDate?: boolean; }; /** For notifications that are clickable and redirect you to a page, and may also optionally include a quick action. */ @@ -87,7 +90,7 @@ type NotificationContentRedirect = NotificationContentBase & { QuickAction?: (props: QuickActionProps) => JSX.Element; }; -export type QuickActionProps = { markAsClicked: () => Promise }; +export type QuickActionProps = { markAsClicked: () => void }; /** For notifications that only contain text and are not interactive in any other way. This is used for user-created notifications. */ type NotificationContentText = NotificationContentBase & { diff --git a/web/packages/teleport/src/TopBar/Notifications/Notifications.test.tsx b/web/packages/teleport/src/TopBar/Notifications/Notifications.test.tsx deleted file mode 100644 index 8d67bc1f86ddc..0000000000000 --- a/web/packages/teleport/src/TopBar/Notifications/Notifications.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import React from 'react'; -import { MemoryRouter } from 'react-router'; -import { render, screen } from 'design/utils/testing'; - -import { createTeleportContext } from 'teleport/mocks/contexts'; -import { LayoutContextProvider } from 'teleport/Main/LayoutContext'; -import { ContextProvider } from 'teleport'; - -import { - StoreNotifications, - NotificationKind, -} from 'teleport/stores/storeNotifications'; - -import { Notifications } from './Notifications'; - -beforeAll(() => { - jest.useFakeTimers(); - jest.setSystemTime(new Date('2023-01-20')); -}); - -afterAll(() => { - jest.useRealTimers(); -}); - -test('due dates and overdue dates', async () => { - const ctx = createTeleportContext(); - const store = new StoreNotifications(); - - store.setNotifications([ - { - item: { - kind: NotificationKind.AccessList, - resourceName: 'carrot', - route: '', - }, - id: '1', - date: new Date('2023-01-25'), - }, - // overdue 10 days - { - item: { - kind: NotificationKind.AccessList, - resourceName: 'carrot', - route: '', - }, - id: '2', - date: new Date('2023-01-10'), - }, - // overdue month - { - item: { - kind: NotificationKind.AccessList, - resourceName: 'carrot', - route: '', - }, - id: '3', - date: new Date('2022-12-20'), - }, - ]); - - ctx.storeNotifications = store; - - render( - - - - - - - - ); - - // no need to click on button for render. - // it's already in the dom but hidden. - - expect(screen.queryAllByTestId('note-item')).toHaveLength(3); - - expect( - screen.getByText(/overdue for a review 10 days ago/i) - ).toBeInTheDocument(); - - expect( - screen.getByText(/overdue for a review about 1 month ago/i) - ).toBeInTheDocument(); - - expect( - screen.getByText(/needs your review within 5 days/i) - ).toBeInTheDocument(); -}); - -test('no notes', async () => { - const ctx = createTeleportContext(); - - render( - - - - - - - - ); - - // no need to click on button for render. - // it's already in the dom but hidden. - - expect(screen.queryByTestId('note-item')).not.toBeInTheDocument(); - expect(screen.getByText(/no notifications/i)).toBeInTheDocument(); -}); diff --git a/web/packages/teleport/src/TopBar/Notifications/Notifications.tsx b/web/packages/teleport/src/TopBar/Notifications/Notifications.tsx deleted file mode 100644 index 7d45bb9f353fe..0000000000000 --- a/web/packages/teleport/src/TopBar/Notifications/Notifications.tsx +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import React, { useState } from 'react'; -import { formatDistanceToNow } from 'date-fns'; -import styled from 'styled-components'; -import { Text } from 'design'; - -import { Notification as NotificationIcon, UserList } from 'design/Icon'; -import { useRefClickOutside } from 'shared/hooks/useRefClickOutside'; -import { useStore } from 'shared/libs/stores'; -import { assertUnreachable } from 'shared/utils/assertUnreachable'; -import { HoverTooltip } from 'shared/components/ToolTip'; - -import { - Dropdown, - DropdownItem, - DropdownItemButton, - DropdownItemIcon, - STARTING_TRANSITION_DELAY, - INCREMENT_TRANSITION_DELAY, - DropdownItemLink, -} from 'teleport/components/Dropdown'; -import useTeleport from 'teleport/useTeleport'; -import { - Notification, - NotificationKind, -} from 'teleport/stores/storeNotifications'; - -import { ButtonIconContainer } from '../Shared'; - -export function Notifications({ iconSize = 24 }: { iconSize?: number }) { - const ctx = useTeleport(); - useStore(ctx.storeNotifications); - - const notices = ctx.storeNotifications.getNotifications(); - - const [open, setOpen] = useState(false); - - const ref = useRefClickOutside({ open, setOpen }); - - let transitionDelay = STARTING_TRANSITION_DELAY; - const items = notices.map(notice => { - const currentTransitionDelay = transitionDelay; - transitionDelay += INCREMENT_TRANSITION_DELAY; - - return ( - - setOpen(false)} /> - - ); - }); - - return ( - - - setOpen(!open)} - data-testid="tb-note-button" - > - {items.length > 0 && } - - - - - {items.length ? ( - items - ) : ( - - No notifications - - )} - - - - ); -} - -function NotificationItem({ - notice, - close, -}: { - notice: Notification; - close(): void; -}) { - const today = new Date(); - const numDays = formatDistanceToNow(notice.date); - - let dueText; - if (notice.date <= today) { - dueText = `was overdue for a review ${numDays} ago`; - } else { - dueText = `needs your review within ${numDays}`; - } - switch (notice.item.kind) { - case NotificationKind.AccessList: - return ( - - - - - - - Access list {notice.item.resourceName} {dueText}. - - - - ); - default: - assertUnreachable(notice.item.kind); - } -} - -const NotificationButtonContainer = styled.div` - position: relative; - height: 100%; -`; - -const AttentionDot = styled.div` - position: absolute; - width: 7px; - height: 7px; - border-radius: 100px; - background-color: ${p => p.theme.colors.buttons.warning.default}; - top: 10px; - right: 15px; - @media screen and (min-width: ${p => p.theme.breakpoints.large}px) { - top: 20px; - right: 25px; - } -`; - -const NotificationItemButton = styled(DropdownItemButton)` - align-items: flex-start; - line-height: 20px; -`; - -const NotificationLink = styled(DropdownItemLink)` - padding: 0; - z-index: 999; -`; diff --git a/web/packages/teleport/src/TopBar/Notifications/index.ts b/web/packages/teleport/src/TopBar/Notifications/index.ts deleted file mode 100644 index 904c210616f13..0000000000000 --- a/web/packages/teleport/src/TopBar/Notifications/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -export { Notifications } from './Notifications'; diff --git a/web/packages/teleport/src/TopBar/TopBar.story.tsx b/web/packages/teleport/src/TopBar/TopBar.story.tsx index 5125b7d2c5f8b..18c633db54f03 100644 --- a/web/packages/teleport/src/TopBar/TopBar.story.tsx +++ b/web/packages/teleport/src/TopBar/TopBar.story.tsx @@ -24,9 +24,9 @@ import { FeaturesContextProvider } from 'teleport/FeaturesContext'; import { getOSSFeatures } from 'teleport/features'; import TeleportContext from 'teleport/teleportContext'; import { makeUserContext } from 'teleport/services/user'; +import { LocalNotificationKind } from 'teleport/services/notifications'; import TeleportContextProvider from 'teleport/TeleportContextProvider'; import { LayoutContextProvider } from 'teleport/Main/LayoutContext'; -import { NotificationKind } from 'teleport/stores/storeNotifications'; import { TopBar } from './TopBar'; @@ -74,7 +74,7 @@ export function TopBarWithNotifications() { notifications: [ { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'banana', route: '', }, @@ -83,7 +83,7 @@ export function TopBarWithNotifications() { }, { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'apple', route: '', }, @@ -92,7 +92,7 @@ export function TopBarWithNotifications() { }, { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'carrot', route: '', }, diff --git a/web/packages/teleport/src/TopBar/TopBar.test.tsx b/web/packages/teleport/src/TopBar/TopBar.test.tsx index 8e2a5380b8d70..91e65e413b00a 100644 --- a/web/packages/teleport/src/TopBar/TopBar.test.tsx +++ b/web/packages/teleport/src/TopBar/TopBar.test.tsx @@ -17,9 +17,12 @@ */ import React from 'react'; +import { act } from '@testing-library/react'; +import { subSeconds, subMinutes } from 'date-fns'; import { render, screen, userEvent } from 'design/utils/testing'; import { Router } from 'react-router'; import { createMemoryHistory } from 'history'; +import { mockIntersectionObserver } from 'jsdom-testing-mocks'; import { LayoutContextProvider } from 'teleport/Main/LayoutContext'; import TeleportContextProvider from 'teleport/TeleportContextProvider'; @@ -31,14 +34,19 @@ import TeleportContext, { import { makeUserContext } from 'teleport/services/user'; import { mockUserContextProviderWith } from 'teleport/User/testHelpers/mockUserContextWith'; import { makeTestUserContext } from 'teleport/User/testHelpers/makeTestUserContext'; -import { NotificationKind } from 'teleport/stores/storeNotifications'; import { clusters } from 'teleport/Clusters/fixtures'; +import { NotificationSubKind } from 'teleport/services/notifications'; + import { TopBar } from './TopBar'; let ctx: TeleportContext; +const mio = mockIntersectionObserver(); + +beforeEach(() => jest.resetAllMocks()); + function setup(): void { ctx = new TeleportContext(); jest @@ -54,48 +62,70 @@ function setup(): void { lastConnected: Date.now(), }, }); + mockUserContextProviderWith(makeTestUserContext()); } -// TODO(rudream): adapt access list notifications to new notifications system -test.skip('notification bell without notification', async () => { +test('notification bell without notification', async () => { setup(); + jest.spyOn(ctx.notificationService, 'fetchNotifications').mockResolvedValue({ + nextKey: '', + userLastSeenNotification: subMinutes(Date.now(), 12), // 12 minutes ago + notifications: [], + }); + render(getTopBar()); - await screen.findByTestId('tb-note'); + await screen.findByTestId('tb-notifications'); - expect(screen.getByTestId('tb-note')).toBeInTheDocument(); - expect(screen.queryByTestId('tb-note-attention')).not.toBeInTheDocument(); + expect(screen.getByTestId('tb-notifications')).toBeInTheDocument(); + expect( + screen.queryByTestId('tb-notifications-badge') + ).not.toBeInTheDocument(); }); -// TODO(rudream): adapt access list notifications to new notifications system -test.skip('notification bell with notification', async () => { +test('notification bell with notification', async () => { setup(); - ctx.storeNotifications.state = { + + jest.spyOn(ctx.notificationService, 'fetchNotifications').mockResolvedValue({ + nextKey: '', + userLastSeenNotification: subMinutes(Date.now(), 12), // 12 minutes ago notifications: [ { - item: { - kind: NotificationKind.AccessList, - resourceName: 'banana', - route: '', - }, - id: 'abc', - date: new Date(), + id: '1', + title: 'Example notification 1', + subKind: NotificationSubKind.UserCreatedInformational, + createdDate: subSeconds(Date.now(), 15), // 15 seconds ago + clicked: false, + labels: [ + { + name: 'text-content', + value: 'This is the text content of the notification.', + }, + ], }, ], - }; + }); + + jest + .spyOn(ctx.notificationService, 'upsertLastSeenNotificationTime') + .mockResolvedValue({ + time: new Date(), + }); render(getTopBar()); - await screen.findByTestId('tb-note'); + await screen.findByTestId('tb-notifications-badge'); - expect(screen.getByTestId('tb-note')).toBeInTheDocument(); - expect(screen.getByTestId('tb-note-attention')).toBeInTheDocument(); + expect(screen.getByTestId('tb-notifications')).toBeInTheDocument(); + expect(screen.getByTestId('tb-notifications-badge')).toHaveTextContent('1'); // Test clicking and rendering of dropdown. - expect(screen.getByTestId('tb-note-dropdown')).not.toBeVisible(); + expect(screen.getByTestId('tb-notifications-dropdown')).not.toBeVisible(); + + act(mio.enterAll); - await userEvent.click(screen.getByTestId('tb-note-button')); - expect(screen.getByTestId('tb-note-dropdown')).toBeVisible(); + await userEvent.click(screen.getByTestId('tb-notifications-button')); + expect(screen.getByTestId('tb-notifications-dropdown')).toBeVisible(); }); const getTopBar = () => { diff --git a/web/packages/teleport/src/services/notifications/types.ts b/web/packages/teleport/src/services/notifications/types.ts index 29d2838d76f5d..d0c3bc6bb5a65 100644 --- a/web/packages/teleport/src/services/notifications/types.ts +++ b/web/packages/teleport/src/services/notifications/types.ts @@ -64,8 +64,11 @@ export type UpsertNotificationStateRequest = { export type Notification = { /** id is the uuid of this notification */ id: string; - /* subKind is a string which represents which type of notification this is, ie. "access-request-approved" */ - subKind: NotificationSubKind; + /* subKind is a string which represents which type of notification this is, ie. "access-request-approved"*/ + subKind: + | NotificationSubKind + | LocalNotificationKind + | LocalNotificationGroupedKind; /** createdDate is when the notification was created. */ createdDate: Date; /** clicked is whether this notification has been clicked on by this user. */ @@ -74,6 +77,11 @@ export type Notification = { labels: Label[]; /** title is the title of this notification. This can be overwritten in notificationContentFactory if needed. */ title: string; + /** localNotification is whether this is a notification stored in a frontend store as opposed to a "real" notification + * from the notifications system. The reason for this is that some notification types (such as access lists) are not supported + * by the backend notifications system, and are instead generated entirely on the frontend. + */ + localNotification?: boolean; }; /** NotificationSubKind is the subkind of notifications, these should be kept in sync with the values in api/types/constants.go */ @@ -87,6 +95,18 @@ export enum NotificationSubKind { AccessRequestDenied = 'access-request-denied', } +/** LocalNotificationKind is the kind of local notifications which are generated on the frontend and not stored in the backend. These do not need to be kept in sync with the backend. */ +export enum LocalNotificationKind { + /** AccessList is a notification for an access list reminder. */ + AccessList = 'access-list', +} + +/** LocalNotificationGroupedKind is the kind of groupings of local notifications. These do not need to be kept in sync with the backend. */ +export enum LocalNotificationGroupedKind { + /** AccessListGrouping is a notification which combines the notifications for multiple access list reminders into one to reduce clutter. */ + AccessListGrouping = 'access-list-grouping', +} + /** * NotificationState the state of a notification for a user. This can represent either "clicked" or "dismissed". * diff --git a/web/packages/teleport/src/services/storageService/types.ts b/web/packages/teleport/src/services/storageService/types.ts index a7b66321dc947..c335ca2f49df8 100644 --- a/web/packages/teleport/src/services/storageService/types.ts +++ b/web/packages/teleport/src/services/storageService/types.ts @@ -35,6 +35,8 @@ export const KeysEnum = { EXTERNAL_AUDIT_STORAGE_CTA_DISABLED: 'grv_teleport_external_audit_storage_disabled', LICENSE_ACKNOWLEDGED: 'grv_teleport_license_acknowledged', + + LOCAL_NOTIFICATION_STATES: 'grv_teleport_notification_states', }; // SurveyRequest is the request for sending data to the back end diff --git a/web/packages/teleport/src/stores/storeNotifications.test.ts b/web/packages/teleport/src/stores/storeNotifications.test.ts index c7c5e03d39766..c68b10dda8e80 100644 --- a/web/packages/teleport/src/stores/storeNotifications.test.ts +++ b/web/packages/teleport/src/stores/storeNotifications.test.ts @@ -16,22 +16,22 @@ * along with this program. If not, see . */ -import { - Notification, - NotificationKind, - StoreNotifications, -} from './storeNotifications'; +import { LocalNotificationKind } from 'teleport/services/notifications'; + +import { Notification, StoreNotifications } from './storeNotifications'; test('get/set/update notifications', async () => { const store = new StoreNotifications(); expect(store.getNotifications()).toStrictEqual([]); - expect(store.hasNotificationsByKind(NotificationKind.AccessList)).toBeFalsy(); + expect( + store.hasNotificationsByKind(LocalNotificationKind.AccessList) + ).toBeFalsy(); // set some notifications, sorted by earliest date. const newerNote: Notification = { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'apple', route: '', }, @@ -40,7 +40,7 @@ test('get/set/update notifications', async () => { }; const olderNote: Notification = { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'banana', route: '', }, @@ -54,7 +54,7 @@ test('get/set/update notifications', async () => { // Update notes, sorted by earliest date. const newestNote: Notification = { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'carrot', route: '', }, @@ -63,7 +63,7 @@ test('get/set/update notifications', async () => { }; const newestOlderNote: Notification = { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'carrot', route: '', }, @@ -72,7 +72,7 @@ test('get/set/update notifications', async () => { }; const newestOldestNote: Notification = { item: { - kind: NotificationKind.AccessList, + kind: LocalNotificationKind.AccessList, resourceName: 'carrot', route: '', }, @@ -88,6 +88,6 @@ test('get/set/update notifications', async () => { // Test has notifications expect( - store.hasNotificationsByKind(NotificationKind.AccessList) + store.hasNotificationsByKind(LocalNotificationKind.AccessList) ).toBeTruthy(); }); diff --git a/web/packages/teleport/src/stores/storeNotifications.ts b/web/packages/teleport/src/stores/storeNotifications.ts index 3068a75095d5e..ac81d3ac5c2d8 100644 --- a/web/packages/teleport/src/stores/storeNotifications.ts +++ b/web/packages/teleport/src/stores/storeNotifications.ts @@ -19,12 +19,11 @@ import { Store } from 'shared/libs/stores'; import { assertUnreachable } from 'shared/utils/assertUnreachable'; -export enum NotificationKind { - AccessList = 'access-list', -} +import { LocalNotificationKind } from 'teleport/services/notifications'; +import { KeysEnum } from 'teleport/services/storageService'; type AccessListNotification = { - kind: NotificationKind.AccessList; + kind: LocalNotificationKind.AccessList; resourceName: string; route: string; }; @@ -33,25 +32,57 @@ export type Notification = { item: AccessListNotification; id: string; date: Date; + clicked?: boolean; }; // TODO?: based on a feedback, consider representing // notifications as a collection of maps indexed by id // which is then converted to a sorted list as needed // (may be easier to work with) -export type NotificationState = { +export type NotificationStoreState = { notifications: Notification[]; }; -const defaultNotificationState: NotificationState = { +const defaultNotificationStoreState: NotificationStoreState = { notifications: [], }; -export class StoreNotifications extends Store { - state: NotificationState = defaultNotificationState; +export type LocalNotificationStates = { + clicked: string[]; + seen: string[]; +}; + +const defaultLocalNotificationStates: LocalNotificationStates = { + /** clicked contains the IDs of notifications which have been clicked on. */ + clicked: [], + /** seen contains the IDs of the notifications which have been seen in the notifications list, even if they were never clicked on. + * Opening the notifications list marks all notifications within it as seen. + */ + seen: [], +}; + +export class StoreNotifications extends Store { + state: NotificationStoreState = defaultNotificationStoreState; - getNotifications() { - return this.state.notifications; + getNotifications(): Notification[] { + const allNotifs = this.state.notifications; + const notifStates = this.getNotificationStates(); + + if (allNotifs.length === 0) { + localStorage.removeItem(KeysEnum.LOCAL_NOTIFICATION_STATES); + return []; + } + + return allNotifs.map(notification => { + // Mark clicked notifications as clicked. + if (notifStates.clicked.indexOf(notification.id) !== -1) { + return { + ...notification, + clicked: true, + }; + } + return notification; + }); } setNotifications(notices: Notification[]) { @@ -62,11 +93,14 @@ export class StoreNotifications extends Store { this.setState({ notifications: [...sortedNotices] }); } - updateNotificationsByKind(notices: Notification[], kind: NotificationKind) { + updateNotificationsByKind( + notices: Notification[], + kind: LocalNotificationKind + ) { switch (kind) { - case NotificationKind.AccessList: + case LocalNotificationKind.AccessList: const filtered = this.state.notifications.filter( - n => n.item.kind !== NotificationKind.AccessList + n => n.item.kind !== LocalNotificationKind.AccessList ); this.setNotifications([...filtered, ...notices]); return; @@ -75,14 +109,94 @@ export class StoreNotifications extends Store { } } - hasNotificationsByKind(kind: NotificationKind) { + hasNotificationsByKind(kind: LocalNotificationKind) { switch (kind) { - case NotificationKind.AccessList: + case LocalNotificationKind.AccessList: return this.getNotifications().some( - n => n.item.kind === NotificationKind.AccessList + n => n.item.kind === LocalNotificationKind.AccessList ); default: assertUnreachable(kind); } } + + getNotificationStates(): LocalNotificationStates { + const value = window.localStorage.getItem( + KeysEnum.LOCAL_NOTIFICATION_STATES + ); + + if (!value) { + return defaultLocalNotificationStates; + } + + try { + return JSON.parse(value) as LocalNotificationStates; + } catch (err) { + return defaultLocalNotificationStates; + } + } + + markNotificationAsClicked(id: string) { + const currentStates = this.getNotificationStates(); + + // If the notification is already marked as clicked, do nothing. + if (currentStates.clicked.includes(id)) { + return; + } + + const updatedStates: LocalNotificationStates = { + clicked: [...currentStates.clicked, id], + seen: currentStates.seen, + }; + + localStorage.setItem( + KeysEnum.LOCAL_NOTIFICATION_STATES, + JSON.stringify(updatedStates) + ); + } + + markNotificationsAsSeen(notificationIds: string[]) { + const currentStates = this.getNotificationStates(); + + // Only add new seen states that aren't already in the state, to prevent duplicates. + const newSeenStates = notificationIds.filter( + id => !currentStates.seen.includes(id) + ); + + const updatedStates: LocalNotificationStates = { + clicked: currentStates.clicked, + seen: [...currentStates.seen, ...newSeenStates], + }; + + localStorage.setItem( + KeysEnum.LOCAL_NOTIFICATION_STATES, + JSON.stringify(updatedStates) + ); + } + + resetStatesForNotification(notificationId: string) { + const currentStates = this.getNotificationStates(); + + const updatedStates = { ...currentStates }; + + // If there is a clicked state for this notification, remove it. + if (currentStates.clicked.includes(notificationId)) { + updatedStates.clicked.splice( + currentStates.clicked.indexOf(notificationId), + 1 + ); + } + + // If there is a seen state for this notification, remove it. + if (currentStates.seen.includes(notificationId)) { + updatedStates.seen.splice(currentStates.seen.indexOf(notificationId), 1); + } + + console.log(updatedStates); + + localStorage.setItem( + KeysEnum.LOCAL_NOTIFICATION_STATES, + JSON.stringify(updatedStates) + ); + } } From 1444b55a7b5367f3a94c9b446b34d31d990a107f Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Wed, 5 Jun 2024 14:36:11 -0400 Subject: [PATCH 08/33] Support CockroachDB in `pgevents` (#42023) * detect cockraochDB and change audit index * fix after rebase * fixup! fix after rebase * lint --- lib/backend/pgbk/common/utils.go | 56 ++++++++++++++++ lib/events/pgevents/pgevents.go | 97 ++++++++++++++++++++++------ lib/events/pgevents/pgevents_test.go | 43 ++++++++++++ 3 files changed, 177 insertions(+), 19 deletions(-) diff --git a/lib/backend/pgbk/common/utils.go b/lib/backend/pgbk/common/utils.go index 44d22fb1f02f5..fb4e19867b236 100644 --- a/lib/backend/pgbk/common/utils.go +++ b/lib/backend/pgbk/common/utils.go @@ -232,9 +232,25 @@ func IsCode(err error, code string) bool { return pgErr != nil && pgErr.Code == code } +// SchemasBuilder returns the desired table schemas based on the postgres connection. +// This allows adapting the schemas based on the postgres flavor (Postgres, +// CockroachDB, etc.) or version. +// Each schema entry is a schema version, each version is applied once. +// The builder also returns a modifier which is always applied, even if the database +// has the latest version. This can be used to update dynamic properties such as +// the retention. +type SchemasBuilder func(conn *pgx.Conn) (schemas []string, err error) + // SetupAndMigrate sets up the database schema, applying the migrations in the // schemas slice in order, starting from the first non-applied one. tableName is // the name of a table used to hold schema version numbers. +// +// WARNING: Editing schemas is not supported in a transaction in CockroachDB, +// except for CREATE TABLE and CREATE INDEX. +// As the current schemas mechanism runs all migrations in a single transaction, +// the schemas must exclusively contain CREATE TABLE and CREATE INDEX statements, +// else you will have undefined non-atomic behaviors. +// See https://www.cockroachlabs.com/docs/stable/online-schema-changes#schema-changes-within-transactions func SetupAndMigrate( ctx context.Context, log *slog.Logger, @@ -244,6 +260,35 @@ func SetupAndMigrate( }, tableName string, schemas []string, +) error { + staticSchemasBuilder := func(*pgx.Conn) ([]string, error) { + return schemas, nil + } + return SetupAndMigrateDynamic(ctx, log, db, tableName, staticSchemasBuilder) +} + +// SetupAndMigrateDynamic sets up the database schema, applying the migrations in the +// schemas slice in order, starting from the first non-applied one. The modifier +// is always applied, even if the schema is already up to date. tableName is +// the name of a table used to hold schema version numbers. It takes a function +// dynamically building schemas based on connection properties. If you only need +// to set up a static schema, call SetupAndMigrate instead. +// +// WARNING: Editing schemas is not supported in a transaction in CockroachDB, +// except for CREATE TABLE and CREATE INDEX. +// As the current schemas mechanism runs all migrations in a single transaction, +// the schemasBuilder must exclusively return CREATE TABLE and CREATE INDEX statements, +// else you will have undefined non-atomic behaviors. +// See https://www.cockroachlabs.com/docs/stable/online-schema-changes#schema-changes-within-transactions +func SetupAndMigrateDynamic( + ctx context.Context, + log *slog.Logger, + db interface { + BeginTx(context.Context, pgx.TxOptions) (pgx.Tx, error) + Exec(context.Context, string, ...any) (pgconn.CommandTag, error) + }, + tableName string, + schemasBuilder SchemasBuilder, ) error { tableName = pgx.Identifier{tableName}.Sanitize() @@ -269,6 +314,7 @@ func SetupAndMigrate( ) } + var schemas []string const idempotent = true if err := RetryTx(ctx, log, db, pgx.TxOptions{ IsoLevel: pgx.Serializable, @@ -281,12 +327,21 @@ func SetupAndMigrate( return trace.Wrap(err) } + // Get schemas and modifier + var err error + schemas, err = schemasBuilder(tx.Conn()) + if err != nil { + migrateErr = trace.Wrap(err, "building schemas") + return nil + } + if int(version) > len(schemas) { migrateErr = trace.BadParameter("unsupported schema version %v", version) // the transaction succeeded, the error is outside of the transaction return nil } + // Run unapplied migrations if int(version) == len(schemas) { return nil } @@ -305,6 +360,7 @@ func SetupAndMigrate( } return nil + }); err != nil { return trace.Wrap(err) } diff --git a/lib/events/pgevents/pgevents.go b/lib/events/pgevents/pgevents.go index 7711d584ae063..0a5baac7bae80 100644 --- a/lib/events/pgevents/pgevents.go +++ b/lib/events/pgevents/pgevents.go @@ -67,6 +67,24 @@ const ( retentionPeriodParam = "retention_period" ) +const ( + schemaV1Table = `CREATE TABLE events ( + event_time timestamptz NOT NULL, + event_id uuid NOT NULL, + event_type text NOT NULL, + session_id uuid NOT NULL, + event_data json NOT NULL, + creation_time timestamptz NOT NULL DEFAULT now(), + CONSTRAINT events_pkey PRIMARY KEY (event_time, event_id) + ); + CREATE INDEX events_search_session_events_idx ON events (session_id, event_time, event_id) + WHERE session_id != '00000000-0000-0000-0000-000000000000';` + dateIndex = "CREATE INDEX events_creation_time_idx ON events USING brin (creation_time);" + schemaV1TableWithDateIndex = schemaV1Table + "\n" + dateIndex + schemaV1CockroachSetRowExpiry = "ALTER TABLE events SET (ttl_expiration_expression = '((creation_time AT TIME ZONE ''UTC'') + (%d * INTERVAL ''1 microsecond'')) AT TIME ZONE ''UTC'' ');" + schemaV1CockroachUnsetRowExpiry = "ALTER TABLE events RESET (ttl_expiration_expression);" +) + // Config is the configuration struct to pass to New. type Config struct { pgcommon.AuthConfig @@ -188,7 +206,19 @@ func New(ctx context.Context, cfg Config) (*Log, error) { return nil, trace.Wrap(err) } - if err := pgcommon.SetupAndMigrate(ctx, cfg.Log, pool, "audit_version", schemas); err != nil { + var isCockroach bool + + // We're a bit hacky here, we must not start the cleanup job if we're + // running on cockroach (rows have TTLs). To avoid running another query, + // the builder function detects and reports if it's a cockroach via a shared + // variable. This works because everything is synchronous. + schemaBuilder := func(conn *pgx.Conn) ([]string, error) { + isCockroach = conn.PgConn().ParameterStatus("crdb_version") != "" + + return buildSchema(isCockroach, &cfg) + } + + if err := pgcommon.SetupAndMigrateDynamic(ctx, cfg.Log, pool, "audit_version", schemaBuilder); err != nil { pool.Close() return nil, trace.Wrap(err) } @@ -200,9 +230,22 @@ func New(ctx context.Context, cfg Config) (*Log, error) { cancel: cancel, } - if !cfg.DisableCleanup { - l.wg.Add(1) - go l.periodicCleanup(periodicCtx, cfg.CleanupInterval, cfg.RetentionPeriod) + if isCockroach { + err = configureCockroachDBRetention(ctx, &cfg, pool) + if err != nil { + return nil, trace.Wrap(err, "configuring CockroachDB retention") + } + } else { + // Regular PostgreSQL that doesn't support expiring rows, we must run a + // periodic cleanup job. + if !cfg.DisableCleanup { + cfg.Log.DebugContext( + ctx, "Starting periodic cleanup background worker.", + "retention", cfg.RetentionPeriod.String(), + "cleanup_interval", cfg.CleanupInterval) + l.wg.Add(1) + go l.periodicCleanup(periodicCtx, cfg.CleanupInterval, cfg.RetentionPeriod) + } } l.log.InfoContext(ctx, "Started events backend.") @@ -210,6 +253,37 @@ func New(ctx context.Context, cfg Config) (*Log, error) { return l, nil } +func configureCockroachDBRetention(ctx context.Context, cfg *Config, pool *pgxpool.Pool) error { + // Arbitrary timeout to make sure we don't end up hanging for some reason + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + var expiryQuery string + if cfg.DisableCleanup { + cfg.Log.DebugContext(ctx, "Disabling CockroachDB native row expiry") + expiryQuery = schemaV1CockroachUnsetRowExpiry + } else { + cfg.Log.DebugContext(ctx, "Configuring CockroachDB native row expiry") + expiryQuery = fmt.Sprintf(schemaV1CockroachSetRowExpiry, cfg.RetentionPeriod) + } + _, err := pool.Exec(ctx, expiryQuery, pgx.QueryExecModeExec) + return trace.Wrap(err) + +} + +func buildSchema(isCockroach bool, cfg *Config) (schemas []string, err error) { + // If this is a real postgres, we cannot use self-expiring rows and we need + // to create an index for the deletion job to run. This index type is not + // supported by CockroachDB at the time of writing + // (see https://github.com/cockroachdb/cockroach/issues/41293) + if !isCockroach { + return []string{schemaV1TableWithDateIndex}, nil + } + + cfg.Log.DebugContext(context.TODO(), "CockroachDB detected.") + return []string{schemaV1Table}, nil +} + // Log is an external [events.AuditLogger] backed by a PostgreSQL database. type Log struct { log *slog.Logger @@ -227,21 +301,6 @@ func (l *Log) Close() error { return nil } -var schemas = []string{ - `CREATE TABLE events ( - event_time timestamptz NOT NULL, - event_id uuid NOT NULL, - event_type text NOT NULL, - session_id uuid NOT NULL, - event_data json NOT NULL, - creation_time timestamptz NOT NULL DEFAULT now(), - CONSTRAINT events_pkey PRIMARY KEY (event_time, event_id) - ); - CREATE INDEX events_creation_time_idx ON events USING brin (creation_time); - CREATE INDEX events_search_session_events_idx ON events (session_id, event_time, event_id) - WHERE session_id != '00000000-0000-0000-0000-000000000000';`, -} - // periodicCleanup removes events past the retention period from the table, // periodically. Returns after the context is done. func (l *Log) periodicCleanup(ctx context.Context, cleanupInterval, retentionPeriod time.Duration) { diff --git a/lib/events/pgevents/pgevents_test.go b/lib/events/pgevents/pgevents_test.go index 49fb005ef2170..05c4934d7b7ad 100644 --- a/lib/events/pgevents/pgevents_test.go +++ b/lib/events/pgevents/pgevents_test.go @@ -137,3 +137,46 @@ func TestConfig(t *testing.T) { require.Equal(t, expectedConfig, &actualConfig) } } + +func TestBuildSchema(t *testing.T) { + testLog := utils.NewSlogLoggerForTests() + + testConfig := &Config{ + Log: testLog, + } + + hasDateIndex := func(t require.TestingT, schemasRaw any, args ...any) { + require.IsType(t, []string(nil), schemasRaw) + schemas := schemasRaw.([]string) + require.NotEmpty(t, schemas) + require.Contains(t, schemas[0], dateIndex, args...) + } + hasNoDateIndex := func(t require.TestingT, schemasRaw any, args ...any) { + require.IsType(t, []string(nil), schemasRaw) + schemas := schemasRaw.([]string) + require.NotContains(t, schemas[0], dateIndex, args...) + } + + tests := []struct { + name string + isCockroach bool + assertSchema require.ValueAssertionFunc + }{ + { + name: "postgres", + isCockroach: false, + assertSchema: hasDateIndex, + }, + { + name: "cockroach", + isCockroach: true, + assertSchema: hasNoDateIndex, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + schemas, _ := buildSchema(tt.isCockroach, testConfig) + tt.assertSchema(t, schemas) + }) + } +} From a7aef61b122e5bfe9ce911afb0ac98b607f39ceb Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Wed, 5 Jun 2024 12:15:58 -0700 Subject: [PATCH 09/33] Update e (#42506) --- e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e b/e index 828d9d8316bfc..879b6ccd831a6 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 828d9d8316bfc608faad2a7f657552b42825b83c +Subproject commit 879b6ccd831a640fc10eacfbd9cdc7204bab3962 From cefa81161ccc58592988b5c46575af5433ea1c85 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 5 Jun 2024 16:34:25 -0300 Subject: [PATCH 10/33] fix: Address macOS build deprecation warning (#42505) --- lib/auth/touchid/register.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/auth/touchid/register.m b/lib/auth/touchid/register.m index 7889bdc545465..9b48b9a588f41 100644 --- a/lib/auth/touchid/register.m +++ b/lib/auth/touchid/register.m @@ -31,10 +31,9 @@ int Register(CredentialInfo req, char **pubKeyB64Out, char **errOut) { CFErrorRef error = NULL; - // kSecAccessControlTouchIDAny is used for compatibility with macOS 10.12. SecAccessControlRef access = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, - kSecAccessControlPrivateKeyUsage | kSecAccessControlTouchIDAny, &error); + kSecAccessControlPrivateKeyUsage | kSecAccessControlBiometryAny, &error); if (error) { NSError *nsError = CFBridgingRelease(error); *errOut = CopyNSString([nsError localizedDescription]); From 282f6f05530784e814ed0e0775e8f369a011ebe7 Mon Sep 17 00:00:00 2001 From: Grzegorz Zdunek Date: Wed, 5 Jun 2024 21:47:07 +0200 Subject: [PATCH 11/33] Always issue relogin and per-session mfa requests for the root cluster URI (#42355) * Always issue per-session MFA requests for a root cluster URI * Always issue relogin requests for a root cluster URI * Check if cluster URI is root cluster URI in login handlers * Pass clusterUri instead of rootClusterUri to the ReAuthenticate dialog * Reserve `root_cluster_uri` field too * Resolve conflicts --- .../lib/teleterm/v1/tshd_events_service.pb.go | 191 +++++++++--------- .../lib/teleterm/v1/tshd_events_service_pb.ts | 26 +-- .../apiserver/handler/handler_auth.go | 10 + lib/teleterm/daemon/daemon.go | 6 +- lib/teleterm/daemon/mfaprompt.go | 23 ++- .../lib/teleterm/v1/tshd_events_service.proto | 4 +- .../teleterm/src/services/tshdEvents/index.ts | 4 - .../ReAuthenticate/ReAuthenticate.story.tsx | 24 ++- .../modals/ReAuthenticate/ReAuthenticate.tsx | 20 +- .../src/ui/services/modals/modalsService.ts | 4 +- 10 files changed, 173 insertions(+), 139 deletions(-) diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go index ca1dc1a52d3c6..21b6c8135ae0d 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go @@ -638,10 +638,10 @@ type PromptMFARequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` - Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` - Totp bool `protobuf:"varint,3,opt,name=totp,proto3" json:"totp,omitempty"` - Webauthn bool `protobuf:"varint,4,opt,name=webauthn,proto3" json:"webauthn,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` + Totp bool `protobuf:"varint,3,opt,name=totp,proto3" json:"totp,omitempty"` + Webauthn bool `protobuf:"varint,4,opt,name=webauthn,proto3" json:"webauthn,omitempty"` + ClusterUri string `protobuf:"bytes,5,opt,name=cluster_uri,json=clusterUri,proto3" json:"cluster_uri,omitempty"` } func (x *PromptMFARequest) Reset() { @@ -676,13 +676,6 @@ func (*PromptMFARequest) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{10} } -func (x *PromptMFARequest) GetRootClusterUri() string { - if x != nil { - return x.RootClusterUri - } - return "" -} - func (x *PromptMFARequest) GetReason() string { if x != nil { return x.Reason @@ -704,6 +697,13 @@ func (x *PromptMFARequest) GetWebauthn() bool { return false } +func (x *PromptMFARequest) GetClusterUri() string { + if x != nil { + return x.ClusterUri + } + return "" +} + // Response for PromptMFA. type PromptMFAResponse struct { state protoimpl.MessageState @@ -1059,97 +1059,98 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = []byte{ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x22, 0x2b, 0x0a, 0x29, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x84, 0x01, 0x0a, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x6f, - 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x74, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x74, 0x6f, 0x74, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x65, 0x62, 0x61, 0x75, - 0x74, 0x68, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, 0x65, 0x62, 0x61, 0x75, - 0x74, 0x68, 0x6e, 0x22, 0x30, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x70, - 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, - 0x70, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x74, + 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x74, 0x6f, 0x74, 0x70, 0x12, 0x1a, 0x0a, + 0x08, 0x77, 0x65, 0x62, 0x61, 0x75, 0x74, 0x68, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x77, 0x65, 0x62, 0x61, 0x75, 0x74, 0x68, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, + 0x52, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, + 0x72, 0x69, 0x22, 0x30, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x70, 0x5f, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x70, + 0x43, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, + 0x0a, 0x18, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x21, 0x47, 0x65, - 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x6a, 0x0a, 0x18, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, - 0x3b, 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x24, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xba, 0x06, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, - 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, - 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, + 0x67, 0x73, 0x52, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3b, + 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x24, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, + 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0xba, 0x06, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, 0x6c, + 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x43, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, - 0x41, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, 0x6e, + 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, - 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, + 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, + 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, + 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, 0x65, + 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, - 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, - 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, - 0x77, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, - 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, + 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, - 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, - 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, + 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts index 5d85580f82e0c..adef7cf4b6a8e 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts @@ -210,10 +210,6 @@ export interface SendPendingHeadlessAuthenticationResponse { * @generated from protobuf message teleport.lib.teleterm.v1.PromptMFARequest */ export interface PromptMFARequest { - /** - * @generated from protobuf field: string root_cluster_uri = 1; - */ - rootClusterUri: string; /** * @generated from protobuf field: string reason = 2; */ @@ -226,6 +222,10 @@ export interface PromptMFARequest { * @generated from protobuf field: bool webauthn = 4; */ webauthn: boolean; + /** + * @generated from protobuf field: string cluster_uri = 5; + */ + clusterUri: string; } /** * Response for PromptMFA. @@ -779,18 +779,18 @@ export const SendPendingHeadlessAuthenticationResponse = new SendPendingHeadless class PromptMFARequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.PromptMFARequest", [ - { no: 1, name: "root_cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 2, name: "reason", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 3, name: "totp", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 4, name: "webauthn", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + { no: 4, name: "webauthn", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 5, name: "cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): PromptMFARequest { const message = globalThis.Object.create((this.messagePrototype!)); - message.rootClusterUri = ""; message.reason = ""; message.totp = false; message.webauthn = false; + message.clusterUri = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -800,9 +800,6 @@ class PromptMFARequest$Type extends MessageType { while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); switch (fieldNo) { - case /* string root_cluster_uri */ 1: - message.rootClusterUri = reader.string(); - break; case /* string reason */ 2: message.reason = reader.string(); break; @@ -812,6 +809,9 @@ class PromptMFARequest$Type extends MessageType { case /* bool webauthn */ 4: message.webauthn = reader.bool(); break; + case /* string cluster_uri */ 5: + message.clusterUri = reader.string(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -824,9 +824,6 @@ class PromptMFARequest$Type extends MessageType { return message; } internalBinaryWrite(message: PromptMFARequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string root_cluster_uri = 1; */ - if (message.rootClusterUri !== "") - writer.tag(1, WireType.LengthDelimited).string(message.rootClusterUri); /* string reason = 2; */ if (message.reason !== "") writer.tag(2, WireType.LengthDelimited).string(message.reason); @@ -836,6 +833,9 @@ class PromptMFARequest$Type extends MessageType { /* bool webauthn = 4; */ if (message.webauthn !== false) writer.tag(4, WireType.Varint).bool(message.webauthn); + /* string cluster_uri = 5; */ + if (message.clusterUri !== "") + writer.tag(5, WireType.LengthDelimited).string(message.clusterUri); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); diff --git a/lib/teleterm/apiserver/handler/handler_auth.go b/lib/teleterm/apiserver/handler/handler_auth.go index 5ef1283f1e3e6..30a4a5d83fc3c 100644 --- a/lib/teleterm/apiserver/handler/handler_auth.go +++ b/lib/teleterm/apiserver/handler/handler_auth.go @@ -32,6 +32,11 @@ func (s *Handler) Login(ctx context.Context, req *api.LoginRequest) (*api.EmptyR if err != nil { return nil, trace.Wrap(err) } + + if !cluster.URI.IsRoot() { + return nil, trace.BadParameter("cluster URI must be a root URI") + } + // The credentials + MFA login flow in the Electron app assumes that the default CLI prompt is // used and works around that. Thus we have to remove the teleterm-specific MFAPromptConstructor // added by daemon.Service.ResolveClusterURI. @@ -83,6 +88,11 @@ func (s *Handler) LoginPasswordless(stream api.TerminalService_LoginPasswordless if err != nil { return trace.Wrap(err) } + + if !cluster.URI.IsRoot() { + return trace.BadParameter("cluster URI must be a root URI") + } + // The passwordless login flow in the Electron app assumes that the default CLI prompt is used and // works around that. Thus we have to remove the teleterm-specific MFAPromptConstructor added by // daemon.Service.ResolveClusterURI. diff --git a/lib/teleterm/daemon/daemon.go b/lib/teleterm/daemon/daemon.go index af8ab870be825..83cb437e037e5 100644 --- a/lib/teleterm/daemon/daemon.go +++ b/lib/teleterm/daemon/daemon.go @@ -263,7 +263,7 @@ func (s *Service) ResolveClusterURI(uri uri.ResourceURI) (*clusters.Cluster, *cl // Custom MFAPromptConstructor gets removed during the calls to Login and LoginPasswordless RPCs. // Those RPCs assume that the default CLI prompt is in use. - clusterClient.MFAPromptConstructor = s.NewMFAPromptConstructor(cluster.URI.String()) + clusterClient.MFAPromptConstructor = s.NewMFAPromptConstructor(cluster.URI) return cluster, clusterClient, nil } @@ -346,7 +346,7 @@ func (s *Service) createGateway(ctx context.Context, params CreateGatewayParams) LocalPort: params.LocalPort, OnExpiredCert: s.reissueGatewayCerts, KubeconfigsDir: s.cfg.KubeconfigsDir, - MFAPromptConstructor: s.NewMFAPromptConstructor(targetURI.String()), + MFAPromptConstructor: s.NewMFAPromptConstructor(targetURI), ClusterClient: clusterClient, } @@ -370,7 +370,7 @@ func (s *Service) createGateway(ctx context.Context, params CreateGatewayParams) // per-session MFA checks. func (s *Service) reissueGatewayCerts(ctx context.Context, g gateway.Gateway) (tls.Certificate, error) { reloginReq := &api.ReloginRequest{ - RootClusterUri: g.TargetURI().GetClusterURI().String(), + RootClusterUri: g.TargetURI().GetRootClusterURI().String(), Reason: &api.ReloginRequest_GatewayCertExpired{ GatewayCertExpired: &api.GatewayCertExpired{ GatewayUri: g.URI().String(), diff --git a/lib/teleterm/daemon/mfaprompt.go b/lib/teleterm/daemon/mfaprompt.go index 0deae2e7af931..5a797c21f39e1 100644 --- a/lib/teleterm/daemon/mfaprompt.go +++ b/lib/teleterm/daemon/mfaprompt.go @@ -30,29 +30,30 @@ import ( wancli "github.com/gravitational/teleport/lib/auth/webauthncli" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" libmfa "github.com/gravitational/teleport/lib/client/mfa" + "github.com/gravitational/teleport/lib/teleterm/api/uri" ) // mfaPrompt is a tshd implementation of mfa.Prompt that uses the // tshdEventsClient to propagate mfa prompts to the Electron App. type mfaPrompt struct { cfg libmfa.PromptConfig - clusterURI string + resourceURI uri.ResourceURI promptAppMFA func(ctx context.Context, in *api.PromptMFARequest) (*api.PromptMFAResponse, error) } // NewMFAPromptConstructor returns a new MFA prompt constructor -// for this service and the given cluster. -func (s *Service) NewMFAPromptConstructor(clusterURI string) func(cfg *libmfa.PromptConfig) mfa.Prompt { +// for this service and the given resource URI. +func (s *Service) NewMFAPromptConstructor(resourceURI uri.ResourceURI) func(cfg *libmfa.PromptConfig) mfa.Prompt { return func(cfg *libmfa.PromptConfig) mfa.Prompt { - return s.NewMFAPrompt(clusterURI, cfg) + return s.NewMFAPrompt(resourceURI, cfg) } } -// NewMFAPrompt returns a new MFA prompt for this service and the given cluster. -func (s *Service) NewMFAPrompt(clusterURI string, cfg *libmfa.PromptConfig) *mfaPrompt { +// NewMFAPrompt returns a new MFA prompt for this service and the given resource URI. +func (s *Service) NewMFAPrompt(resourceURI uri.ResourceURI, cfg *libmfa.PromptConfig) *mfaPrompt { return &mfaPrompt{ cfg: *cfg, - clusterURI: clusterURI, + resourceURI: resourceURI, promptAppMFA: s.promptAppMFA, } } @@ -125,10 +126,10 @@ func (p *mfaPrompt) promptWebauthn(ctx context.Context, chal *proto.MFAAuthentic func (p *mfaPrompt) promptMFA(ctx context.Context, chal *proto.MFAAuthenticateChallenge, runOpts libmfa.RunOpts) (*proto.MFAAuthenticateResponse, error) { resp, err := p.promptAppMFA(ctx, &api.PromptMFARequest{ - RootClusterUri: p.clusterURI, - Reason: p.cfg.PromptReason, - Totp: runOpts.PromptTOTP, - Webauthn: runOpts.PromptWebauthn, + ClusterUri: p.resourceURI.GetClusterURI().String(), + Reason: p.cfg.PromptReason, + Totp: runOpts.PromptTOTP, + Webauthn: runOpts.PromptWebauthn, }) if err != nil { return nil, trail.FromGRPC(err) diff --git a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto index 0663f68146566..070f268f9c884 100644 --- a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto +++ b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto @@ -133,10 +133,12 @@ message SendPendingHeadlessAuthenticationResponse {} // Request for PromptMFA. message PromptMFARequest { - string root_cluster_uri = 1; + reserved 1; // root_cluster_uri + reserved "root_cluster_uri"; string reason = 2; bool totp = 3; bool webauthn = 4; + string cluster_uri = 5; } // Response for PromptMFA. diff --git a/web/packages/teleterm/src/services/tshdEvents/index.ts b/web/packages/teleterm/src/services/tshdEvents/index.ts index 6b6d5a2edb60d..eb63c14cb9a3d 100644 --- a/web/packages/teleterm/src/services/tshdEvents/index.ts +++ b/web/packages/teleterm/src/services/tshdEvents/index.ts @@ -34,10 +34,6 @@ export interface ReloginRequest extends api.ReloginRequest { } export type SendNotificationRequest = api.SendNotificationRequest; -export type PromptMfaRequest = api.PromptMFARequest & { - rootClusterUri: uri.RootClusterUri; -}; - export interface SendPendingHeadlessAuthenticationRequest extends api.SendPendingHeadlessAuthenticationRequest { rootClusterUri: uri.RootClusterUri; diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.story.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.story.tsx index 7f0a735c4cc5a..42a48089b48a0 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.story.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.story.tsx @@ -16,7 +16,10 @@ * along with this program. If not, see . */ -import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; +import { + makeRootCluster, + makeLeafCluster, +} from 'teleterm/services/tshd/testHelpers'; import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider'; import { ReAuthenticate } from './ReAuthenticate'; @@ -27,7 +30,7 @@ export default { const promptMfaRequest = { reason: 'MFA is required to access Kubernetes cluster "minikube"', - rootClusterUri: makeRootCluster().uri, + clusterUri: makeRootCluster().uri, webauthn: false, totp: false, }; @@ -76,7 +79,22 @@ export const MultilineTitle = () => ( ...promptMfaRequest, webauthn: true, totp: true, - rootClusterUri: '/clusters/lorem.cloud.gravitational.io', + clusterUri: '/clusters/lorem.cloud.gravitational.io', + }} + onCancel={() => {}} + onSuccess={showToken} + /> + +); + +export const ForLeafCluster = () => ( + + {}} onSuccess={showToken} diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx index d3a97d3499a34..bdcdbc135e712 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx @@ -17,6 +17,9 @@ */ import { FC, useState } from 'react'; + +import { PromptMFARequest } from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb'; + import DialogConfirmation, { DialogContent, DialogFooter, @@ -42,7 +45,6 @@ import { Option } from 'shared/components/Select'; import { assertUnreachable } from 'shared/utils/assertUnreachable'; import { useAppContext } from 'teleterm/ui/appContextProvider'; -import { PromptMfaRequest } from 'teleterm/services/tshdEvents'; import LinearProgress from 'teleterm/ui/components/LinearProgress'; import svgHardwareKey from 'teleterm/ui/ClusterConnect/ClusterLogin/FormLogin/PromptWebauthn/hardware.svg'; import { useLogger } from 'teleterm/ui/hooks/useLogger'; @@ -51,7 +53,7 @@ import { routing } from 'teleterm/ui/uri'; type MfaType = 'webauthn' | 'totp'; export const ReAuthenticate: FC<{ - promptMfaRequest: PromptMfaRequest; + promptMfaRequest: PromptMFARequest; onCancel: () => void; onSuccess: (otp: string) => void; }> = props => { @@ -83,13 +85,18 @@ export const ReAuthenticate: FC<{ const [selectedMfaType, setSelectedMfaType] = useState(availableMfaTypes[0]); const [otpToken, setOtpToken] = useState(''); - const { rootClusterUri } = req; + const { clusterUri } = req; const { clustersService } = useAppContext(); // TODO(ravicious): Use a profile name here from the URI and remove the dependency on // clustersService. https://github.com/gravitational/teleport/issues/33733 - const clusterName = - clustersService.findCluster(rootClusterUri)?.name || + const rootClusterUri = routing.ensureRootClusterUri(clusterUri); + const rootClusterName = + clustersService.findRootClusterByResource(rootClusterUri)?.name || routing.parseClusterName(rootClusterUri); + const clusterName = + clustersService.findCluster(clusterUri)?.name || + routing.parseClusterName(clusterUri); + const isLeafCluster = routing.isLeafCluster(clusterUri); return ( - Verify your identity on {clusterName} + Verify your identity on {rootClusterName} {req.reason} + {isLeafCluster && ` from trusted cluster "${clusterName}"`} diff --git a/web/packages/teleterm/src/ui/services/modals/modalsService.ts b/web/packages/teleterm/src/ui/services/modals/modalsService.ts index e07c23c9dcfe7..6078e49dd2079 100644 --- a/web/packages/teleterm/src/ui/services/modals/modalsService.ts +++ b/web/packages/teleterm/src/ui/services/modals/modalsService.ts @@ -23,8 +23,6 @@ import * as types from 'teleterm/services/tshd/types'; import { RootClusterUri } from 'teleterm/ui/uri'; import { ResourceSearchError } from 'teleterm/ui/services/resources'; -import { PromptMfaRequest } from 'teleterm/services/tshdEvents'; - import { ImmutableStore } from '../immutableStore'; import type * as uri from 'teleterm/ui/uri'; @@ -211,7 +209,7 @@ export interface DialogHeadlessAuthentication { export interface DialogReAuthenticate { kind: 'reauthenticate'; - promptMfaRequest: PromptMfaRequest; + promptMfaRequest: tshdEventsApi.PromptMFARequest; onSuccess(totpCode: string): void; onCancel(): void; } From 93f8f92bdd45e035d7710f4aede83374246c24a0 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 5 Jun 2024 16:52:07 -0300 Subject: [PATCH 12/33] fix: Do not DNS resolve on TestJoinServiceClient_RegisterUsingTPMMethod (#42513) --- api/client/joinservice_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/api/client/joinservice_test.go b/api/client/joinservice_test.go index dec8138ea57d0..a8a8509866d67 100644 --- a/api/client/joinservice_test.go +++ b/api/client/joinservice_test.go @@ -116,9 +116,14 @@ func TestJoinServiceClient_RegisterUsingTPMMethod(t *testing.T) { cancel() }() - c, err := grpc.NewClient("unused.com", grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { - return lis.DialContext(ctx) - }), grpc.WithTransportCredentials(insecure.NewCredentials())) + // grpc.NewClient attempts to DNS resolve addr, whereas grpc.Dial doesn't. + c, err := grpc.Dial( + "bufconn", + grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { + return lis.DialContext(ctx) + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) require.NoError(t, err) joinClient := NewJoinServiceClient(proto.NewJoinServiceClient(c)) From 72f4a10366e84b5099bd6419063a77de8c4ae02e Mon Sep 17 00:00:00 2001 From: Alex McGrath Date: Wed, 5 Jun 2024 21:56:07 +0100 Subject: [PATCH 13/33] Log information about second_factors removal at startup (#41416) * Log information on second_factors removal at startup * include upgrade instructions as a usermessage * resolve comments --- lib/auth/init.go | 26 +++++++++++++++++++++++++- lib/modules/modules.go | 5 ++++- lib/modules/modules_test.go | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/auth/init.go b/lib/auth/init.go index 95653834ea758..ea1c27b2503cd 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -23,6 +23,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "os" "slices" @@ -709,6 +710,20 @@ func generateAuthority(ctx context.Context, asrv *Server, caID types.CertAuthID) return ca, nil } +var secondFactorUpgradeInstructions = ` +Teleport requires second factor authentication for local users. +The auth_service configuration should be updated to enable it. + +auth_service: + authentication: + second_factor: on + webauthn: + rp_id: example.com + +For more information: +- https://goteleport.com/docs/access-controls/guides/webauthn/ +` + func initializeAuthPreference(ctx context.Context, asrv *Server, newAuthPref types.AuthPreference) error { const iterationLimit = 3 for i := 0; i < iterationLimit; i++ { @@ -724,7 +739,13 @@ func initializeAuthPreference(ctx context.Context, asrv *Server, newAuthPref typ if !shouldReplace { if os.Getenv(teleport.EnvVarAllowNoSecondFactor) != "true" { - return trace.Wrap(modules.ValidateResource(storedAuthPref)) + err := modules.ValidateResource(storedAuthPref) + if errors.Is(err, modules.ErrCannotDisableSecondFactor) { + return trace.Wrap(err, secondFactorUpgradeInstructions) + } + if err != nil { + return trace.Wrap(err) + } } return nil } @@ -744,6 +765,9 @@ func initializeAuthPreference(ctx context.Context, asrv *Server, newAuthPref typ if trace.IsCompareFailed(err) { continue } + if errors.Is(err, modules.ErrCannotDisableSecondFactor) { + return trace.Wrap(err, secondFactorUpgradeInstructions) + } return trace.Wrap(err) } diff --git a/lib/modules/modules.go b/lib/modules/modules.go index 09ce2ba0bf0f5..2f3a9fa3abbbc 100644 --- a/lib/modules/modules.go +++ b/lib/modules/modules.go @@ -23,6 +23,7 @@ package modules import ( "context" "crypto" + "errors" "fmt" "os" "runtime" @@ -338,6 +339,8 @@ func GetModules() Modules { return modules } +var ErrCannotDisableSecondFactor = errors.New("cannot disable multi-factor authentication") + // ValidateResource performs additional resource checks. func ValidateResource(res types.Resource) error { // todo(lxea): DELETE IN 17 [remove env var, leave insecure test mode] @@ -348,7 +351,7 @@ func ValidateResource(res types.Resource) error { case types.AuthPreference: switch r.GetSecondFactor() { case constants.SecondFactorOff, constants.SecondFactorOptional: - return trace.BadParameter("cannot disable two-factor authentication") + return trace.Wrap(ErrCannotDisableSecondFactor) } } } diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go index 2161ce1f8fe50..4b00efd2d79f2 100644 --- a/lib/modules/modules_test.go +++ b/lib/modules/modules_test.go @@ -60,7 +60,7 @@ func TestValidateAuthPreferenceOnCloud(t *testing.T) { authPref.SetSecondFactor(constants.SecondFactorOff) _, err = testServer.AuthServer.UpdateAuthPreference(ctx, authPref) - require.EqualError(t, err, "cannot disable two-factor authentication") + require.EqualError(t, err, modules.ErrCannotDisableSecondFactor.Error()) } func TestValidateSessionRecordingConfigOnCloud(t *testing.T) { From 3e01d3c7d8cb50c6fb20d4d44c62dee350efd0b3 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 5 Jun 2024 18:53:54 -0300 Subject: [PATCH 14/33] chore: Bump OpenSSL to 3.0.14 (#42494) --- build.assets/Dockerfile | 4 ++-- build.assets/Dockerfile-centos7 | 4 ++-- build.assets/build-fido2-macos.sh | 4 ++-- .../buildbox/usr/local/lib/pkgconfig/libcrypto-static.pc | 2 +- .../centos7/usr/local/lib64/pkgconfig/libcrypto-static.pc | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.assets/Dockerfile b/build.assets/Dockerfile index 80a29bac530bd..79206e85faa45 100644 --- a/build.assets/Dockerfile +++ b/build.assets/Dockerfile @@ -46,9 +46,9 @@ RUN git clone --depth=1 https://github.com/PJK/libcbor.git -b v0.10.2 && \ # Install openssl. # install_sw install only binaries, skips docs. -RUN git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.0.13 && \ +RUN git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.0.14 && \ cd openssl && \ - [ "$(git rev-parse HEAD)" = '85cf92f55d9e2ac5aacf92bedd33fb890b9f8b4c' ] && \ + [ "$(git rev-parse HEAD)" = '9cff14fd97814baf8a9a07d8447960a64d616ada' ] && \ ./config --release -fPIC --libdir=/usr/local/lib && \ make -j"$(nproc)" && \ make install_sw diff --git a/build.assets/Dockerfile-centos7 b/build.assets/Dockerfile-centos7 index 1b4551d49f3e7..e5fe0dc56ce31 100644 --- a/build.assets/Dockerfile-centos7 +++ b/build.assets/Dockerfile-centos7 @@ -106,9 +106,9 @@ RUN git clone --depth=1 https://github.com/PJK/libcbor.git -b v0.10.2 && \ # Specific install arguments used to skip docs. # Note that FIPS is enabled as part of this build, but it is unused without the # necessary configuration (which is included as part of the separate FIPS buildbox). -RUN git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.0.13 && \ +RUN git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.0.14 && \ cd openssl && \ - [ "$(git rev-parse HEAD)" = '85cf92f55d9e2ac5aacf92bedd33fb890b9f8b4c' ] && \ + [ "$(git rev-parse HEAD)" = '9cff14fd97814baf8a9a07d8447960a64d616ada' ] && \ ./config enable-fips --release -fPIC --libdir=/usr/local/lib64 && \ make -j"$(nproc)" && \ make install_sw install_ssldirs install_fips diff --git a/build.assets/build-fido2-macos.sh b/build.assets/build-fido2-macos.sh index 43eb5889dd854..a8a52001b83af 100755 --- a/build.assets/build-fido2-macos.sh +++ b/build.assets/build-fido2-macos.sh @@ -23,8 +23,8 @@ fi # Note: versions are the same as the corresponding git tags for each repo. readonly CBOR_VERSION=v0.10.2 readonly CBOR_COMMIT=efa6c0886bae46bdaef9b679f61f4b9d8bc296ae -readonly CRYPTO_VERSION=openssl-3.0.13 -readonly CRYPTO_COMMIT=85cf92f55d9e2ac5aacf92bedd33fb890b9f8b4c +readonly CRYPTO_VERSION=openssl-3.0.14 +readonly CRYPTO_COMMIT=9cff14fd97814baf8a9a07d8447960a64d616ada readonly FIDO2_VERSION=1.14.0 readonly FIDO2_COMMIT=1a9d335c8f0e821f9eff27482fdda96e59a4f577 diff --git a/build.assets/pkgconfig/buildbox/usr/local/lib/pkgconfig/libcrypto-static.pc b/build.assets/pkgconfig/buildbox/usr/local/lib/pkgconfig/libcrypto-static.pc index 81df33b763eb0..59ce056c73b31 100644 --- a/build.assets/pkgconfig/buildbox/usr/local/lib/pkgconfig/libcrypto-static.pc +++ b/build.assets/pkgconfig/buildbox/usr/local/lib/pkgconfig/libcrypto-static.pc @@ -7,6 +7,6 @@ modulesdir=${libdir}/ossl-modules Name: OpenSSL-libcrypto Description: OpenSSL cryptography library -Version: 3.0.13 +Version: 3.0.14 Libs: ${libdir}/libcrypto.a -ldl -pthread Cflags: -I${includedir} diff --git a/build.assets/pkgconfig/centos7/usr/local/lib64/pkgconfig/libcrypto-static.pc b/build.assets/pkgconfig/centos7/usr/local/lib64/pkgconfig/libcrypto-static.pc index b4d14d80a7d48..a51b52b44d157 100644 --- a/build.assets/pkgconfig/centos7/usr/local/lib64/pkgconfig/libcrypto-static.pc +++ b/build.assets/pkgconfig/centos7/usr/local/lib64/pkgconfig/libcrypto-static.pc @@ -7,6 +7,6 @@ modulesdir=${libdir}/ossl-modules Name: OpenSSL-libcrypto Description: OpenSSL cryptography library -Version: 3.0.13 +Version: 3.0.14 Libs: ${libdir}/libcrypto.a -ldl -pthread Cflags: -I${includedir} From d413e5fdf55cca8d87d9116d646241f71e04de36 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 5 Jun 2024 19:04:34 -0300 Subject: [PATCH 15/33] chore: Bump protoc-gen-go-grpc to v1.4.0 (#42497) * chore: Bump protoc-gen-go-grpc to v1.4.0 * Update generated protos * Show the diff whenever a git diff fails * Update generated protos --- Makefile | 4 + api/gen/proto/go/assist/v1/assist_grpc.pb.go | 41 +++-- .../v1/accesslist_service_grpc.pb.go | 82 ++++++---- ...access_monitoring_rules_service_grpc.pb.go | 31 ++-- .../teleport/auditlog/v1/auditlog_grpc.pb.go | 20 ++- .../v1/clusterconfig_service_grpc.pb.go | 52 ++++-- .../v1/crownjewel_service_grpc.pb.go | 28 ++-- .../dbobject/v1/dbobject_service_grpc.pb.go | 28 ++-- .../v1/dbobjectimportrule_service_grpc.pb.go | 28 ++-- .../v1/devicetrust_service_grpc.pb.go | 104 +++++++++--- .../v1/discoveryconfig_service_grpc.pb.go | 42 +++-- .../externalauditstorage_service_grpc.pb.go | 65 ++++++-- .../integration/v1/awsoidc_service_grpc.pb.go | 37 +++-- .../v1/integration_service_grpc.pb.go | 31 ++-- .../teleport/kube/v1/kube_service_grpc.pb.go | 15 +- .../kubewaitingcontainer_service_grpc.pb.go | 26 ++- .../loginrule/v1/loginrule_service_grpc.pb.go | 28 ++-- .../machineid/v1/bot_service_grpc.pb.go | 28 ++-- .../v1/workload_identity_service_grpc.pb.go | 15 +- .../v1/notifications_service_grpc.pb.go | 31 ++-- .../teleport/okta/v1/okta_service_grpc.pb.go | 49 ++++-- .../plugins/v1/plugin_service_grpc.pb.go | 43 +++-- .../teleport/presence/v1/service_grpc.pb.go | 22 ++- .../v1/resourceusage_service_grpc.pb.go | 13 +- .../go/teleport/samlidp/v1/samlidp_grpc.pb.go | 16 +- .../teleport/scim/v1/scim_service_grpc.pb.go | 25 ++- .../v1/secreports_service_grpc.pb.go | 52 ++++-- .../transport/v1/transport_service_grpc.pb.go | 37 +++-- .../trust/v1/trust_service_grpc.pb.go | 31 ++-- .../v1/userloginstate_service_grpc.pb.go | 25 ++- .../users/v1/users_service_grpc.pb.go | 28 ++-- .../vnet/v1/vnet_config_service_grpc.pb.go | 25 ++- .../v1/userpreferences_grpc.pb.go | 16 +- .../v1alpha/access_graph_service_grpc.pb.go | 57 ++++--- .../lib/teleterm/v1/service_grpc.pb.go | 151 ++++++++++++------ .../v1/tshd_events_service_grpc.pb.go | 30 ++-- .../teleterm/vnet/v1/vnet_service_grpc.pb.go | 16 +- go.mod | 2 +- go.sum | 4 +- integrations/event-handler/go.mod | 2 +- integrations/event-handler/go.sum | 4 +- integrations/terraform/go.mod | 2 +- integrations/terraform/go.sum | 4 +- lib/multiplexer/test/ping_grpc.pb.go | 13 +- 44 files changed, 972 insertions(+), 431 deletions(-) diff --git a/Makefile b/Makefile index 854aa31ea192d..074321f8b96c3 100644 --- a/Makefile +++ b/Makefile @@ -1388,6 +1388,7 @@ derive: derive-up-to-date: must-start-clean/host derive @if ! git diff --quiet; then \ echo 'Please run make derive.'; \ + git diff; \ exit 1; \ fi @@ -1423,6 +1424,7 @@ endif protos-up-to-date/host: must-start-clean/host grpc/host @if ! git diff --quiet; then \ echo 'Please run make grpc.'; \ + git diff; \ exit 1; \ fi @@ -1430,6 +1432,7 @@ protos-up-to-date/host: must-start-clean/host grpc/host must-start-clean/host: @if ! git diff --quiet; then \ echo 'This must be run from a repo with no unstaged commits.'; \ + git diff; \ exit 1; \ fi @@ -1439,6 +1442,7 @@ crds-up-to-date: must-start-clean/host $(MAKE) -C integrations/operator manifests @if ! git diff --quiet; then \ echo 'Please run make -C integrations/operator manifests.'; \ + git diff; \ exit 1; \ fi diff --git a/api/gen/proto/go/assist/v1/assist_grpc.pb.go b/api/gen/proto/go/assist/v1/assist_grpc.pb.go index f86a586be0689..c9b63831db087 100644 --- a/api/gen/proto/go/assist/v1/assist_grpc.pb.go +++ b/api/gen/proto/go/assist/v1/assist_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/assist/v1/assist.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AssistService_CreateAssistantConversation_FullMethodName = "/teleport.assist.v1.AssistService/CreateAssistantConversation" @@ -47,6 +47,8 @@ const ( // AssistServiceClient is the client API for AssistService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AssistService is a service that provides an ability to communicate with the Teleport Assist. type AssistServiceClient interface { // CreateNewConversation creates a new conversation and returns the UUID of it. CreateAssistantConversation(ctx context.Context, in *CreateAssistantConversationRequest, opts ...grpc.CallOption) (*CreateAssistantConversationResponse, error) @@ -75,8 +77,9 @@ func NewAssistServiceClient(cc grpc.ClientConnInterface) AssistServiceClient { } func (c *assistServiceClient) CreateAssistantConversation(ctx context.Context, in *CreateAssistantConversationRequest, opts ...grpc.CallOption) (*CreateAssistantConversationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateAssistantConversationResponse) - err := c.cc.Invoke(ctx, AssistService_CreateAssistantConversation_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_CreateAssistantConversation_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -84,8 +87,9 @@ func (c *assistServiceClient) CreateAssistantConversation(ctx context.Context, i } func (c *assistServiceClient) GetAssistantConversations(ctx context.Context, in *GetAssistantConversationsRequest, opts ...grpc.CallOption) (*GetAssistantConversationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAssistantConversationsResponse) - err := c.cc.Invoke(ctx, AssistService_GetAssistantConversations_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_GetAssistantConversations_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -93,8 +97,9 @@ func (c *assistServiceClient) GetAssistantConversations(ctx context.Context, in } func (c *assistServiceClient) DeleteAssistantConversation(ctx context.Context, in *DeleteAssistantConversationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AssistService_DeleteAssistantConversation_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_DeleteAssistantConversation_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +107,9 @@ func (c *assistServiceClient) DeleteAssistantConversation(ctx context.Context, i } func (c *assistServiceClient) GetAssistantMessages(ctx context.Context, in *GetAssistantMessagesRequest, opts ...grpc.CallOption) (*GetAssistantMessagesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAssistantMessagesResponse) - err := c.cc.Invoke(ctx, AssistService_GetAssistantMessages_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_GetAssistantMessages_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -111,8 +117,9 @@ func (c *assistServiceClient) GetAssistantMessages(ctx context.Context, in *GetA } func (c *assistServiceClient) CreateAssistantMessage(ctx context.Context, in *CreateAssistantMessageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AssistService_CreateAssistantMessage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_CreateAssistantMessage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -120,8 +127,9 @@ func (c *assistServiceClient) CreateAssistantMessage(ctx context.Context, in *Cr } func (c *assistServiceClient) UpdateAssistantConversationInfo(ctx context.Context, in *UpdateAssistantConversationInfoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AssistService_UpdateAssistantConversationInfo_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_UpdateAssistantConversationInfo_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,8 +137,9 @@ func (c *assistServiceClient) UpdateAssistantConversationInfo(ctx context.Contex } func (c *assistServiceClient) IsAssistEnabled(ctx context.Context, in *IsAssistEnabledRequest, opts ...grpc.CallOption) (*IsAssistEnabledResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(IsAssistEnabledResponse) - err := c.cc.Invoke(ctx, AssistService_IsAssistEnabled_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_IsAssistEnabled_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -138,8 +147,9 @@ func (c *assistServiceClient) IsAssistEnabled(ctx context.Context, in *IsAssistE } func (c *assistServiceClient) SearchUnifiedResources(ctx context.Context, in *SearchUnifiedResourcesRequest, opts ...grpc.CallOption) (*SearchUnifiedResourcesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SearchUnifiedResourcesResponse) - err := c.cc.Invoke(ctx, AssistService_SearchUnifiedResources_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistService_SearchUnifiedResources_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -149,6 +159,8 @@ func (c *assistServiceClient) SearchUnifiedResources(ctx context.Context, in *Se // AssistServiceServer is the server API for AssistService service. // All implementations must embed UnimplementedAssistServiceServer // for forward compatibility +// +// AssistService is a service that provides an ability to communicate with the Teleport Assist. type AssistServiceServer interface { // CreateNewConversation creates a new conversation and returns the UUID of it. CreateAssistantConversation(context.Context, *CreateAssistantConversationRequest) (*CreateAssistantConversationResponse, error) @@ -405,6 +417,8 @@ const ( // AssistEmbeddingServiceClient is the client API for AssistEmbeddingService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AssistEmbeddingService is a service that provides an ability to communicate with the Assist Embedding service. type AssistEmbeddingServiceClient interface { // AssistantGetEmbeddings returns the embeddings for the given query. GetAssistantEmbeddings(ctx context.Context, in *GetAssistantEmbeddingsRequest, opts ...grpc.CallOption) (*GetAssistantEmbeddingsResponse, error) @@ -419,8 +433,9 @@ func NewAssistEmbeddingServiceClient(cc grpc.ClientConnInterface) AssistEmbeddin } func (c *assistEmbeddingServiceClient) GetAssistantEmbeddings(ctx context.Context, in *GetAssistantEmbeddingsRequest, opts ...grpc.CallOption) (*GetAssistantEmbeddingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAssistantEmbeddingsResponse) - err := c.cc.Invoke(ctx, AssistEmbeddingService_GetAssistantEmbeddings_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AssistEmbeddingService_GetAssistantEmbeddings_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -430,6 +445,8 @@ func (c *assistEmbeddingServiceClient) GetAssistantEmbeddings(ctx context.Contex // AssistEmbeddingServiceServer is the server API for AssistEmbeddingService service. // All implementations must embed UnimplementedAssistEmbeddingServiceServer // for forward compatibility +// +// AssistEmbeddingService is a service that provides an ability to communicate with the Assist Embedding service. type AssistEmbeddingServiceServer interface { // AssistantGetEmbeddings returns the embeddings for the given query. GetAssistantEmbeddings(context.Context, *GetAssistantEmbeddingsRequest) (*GetAssistantEmbeddingsResponse, error) diff --git a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go index d9419c95fbc0c..13c53d238ee29 100644 --- a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/accesslist/v1/accesslist_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AccessListService_GetAccessLists_FullMethodName = "/teleport.accesslist.v1.AccessListService/GetAccessLists" @@ -63,6 +63,8 @@ const ( // AccessListServiceClient is the client API for AccessListService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AccessListService provides CRUD methods for Access List resources. type AccessListServiceClient interface { // GetAccessLists returns a list of all access lists. GetAccessLists(ctx context.Context, in *GetAccessListsRequest, opts ...grpc.CallOption) (*GetAccessListsResponse, error) @@ -134,8 +136,9 @@ func NewAccessListServiceClient(cc grpc.ClientConnInterface) AccessListServiceCl } func (c *accessListServiceClient) GetAccessLists(ctx context.Context, in *GetAccessListsRequest, opts ...grpc.CallOption) (*GetAccessListsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAccessListsResponse) - err := c.cc.Invoke(ctx, AccessListService_GetAccessLists_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_GetAccessLists_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -143,8 +146,9 @@ func (c *accessListServiceClient) GetAccessLists(ctx context.Context, in *GetAcc } func (c *accessListServiceClient) ListAccessLists(ctx context.Context, in *ListAccessListsRequest, opts ...grpc.CallOption) (*ListAccessListsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAccessListsResponse) - err := c.cc.Invoke(ctx, AccessListService_ListAccessLists_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_ListAccessLists_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -152,8 +156,9 @@ func (c *accessListServiceClient) ListAccessLists(ctx context.Context, in *ListA } func (c *accessListServiceClient) GetAccessList(ctx context.Context, in *GetAccessListRequest, opts ...grpc.CallOption) (*AccessList, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessList) - err := c.cc.Invoke(ctx, AccessListService_GetAccessList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_GetAccessList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -161,8 +166,9 @@ func (c *accessListServiceClient) GetAccessList(ctx context.Context, in *GetAcce } func (c *accessListServiceClient) UpsertAccessList(ctx context.Context, in *UpsertAccessListRequest, opts ...grpc.CallOption) (*AccessList, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessList) - err := c.cc.Invoke(ctx, AccessListService_UpsertAccessList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_UpsertAccessList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -170,8 +176,9 @@ func (c *accessListServiceClient) UpsertAccessList(ctx context.Context, in *Upse } func (c *accessListServiceClient) UpdateAccessList(ctx context.Context, in *UpdateAccessListRequest, opts ...grpc.CallOption) (*AccessList, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessList) - err := c.cc.Invoke(ctx, AccessListService_UpdateAccessList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_UpdateAccessList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -179,8 +186,9 @@ func (c *accessListServiceClient) UpdateAccessList(ctx context.Context, in *Upda } func (c *accessListServiceClient) DeleteAccessList(ctx context.Context, in *DeleteAccessListRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAccessList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAccessList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -188,8 +196,9 @@ func (c *accessListServiceClient) DeleteAccessList(ctx context.Context, in *Dele } func (c *accessListServiceClient) DeleteAllAccessLists(ctx context.Context, in *DeleteAllAccessListsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessLists_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessLists_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -197,8 +206,9 @@ func (c *accessListServiceClient) DeleteAllAccessLists(ctx context.Context, in * } func (c *accessListServiceClient) GetAccessListsToReview(ctx context.Context, in *GetAccessListsToReviewRequest, opts ...grpc.CallOption) (*GetAccessListsToReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAccessListsToReviewResponse) - err := c.cc.Invoke(ctx, AccessListService_GetAccessListsToReview_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_GetAccessListsToReview_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -206,8 +216,9 @@ func (c *accessListServiceClient) GetAccessListsToReview(ctx context.Context, in } func (c *accessListServiceClient) CountAccessListMembers(ctx context.Context, in *CountAccessListMembersRequest, opts ...grpc.CallOption) (*CountAccessListMembersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CountAccessListMembersResponse) - err := c.cc.Invoke(ctx, AccessListService_CountAccessListMembers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_CountAccessListMembers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -215,8 +226,9 @@ func (c *accessListServiceClient) CountAccessListMembers(ctx context.Context, in } func (c *accessListServiceClient) ListAccessListMembers(ctx context.Context, in *ListAccessListMembersRequest, opts ...grpc.CallOption) (*ListAccessListMembersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAccessListMembersResponse) - err := c.cc.Invoke(ctx, AccessListService_ListAccessListMembers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_ListAccessListMembers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -224,8 +236,9 @@ func (c *accessListServiceClient) ListAccessListMembers(ctx context.Context, in } func (c *accessListServiceClient) ListAllAccessListMembers(ctx context.Context, in *ListAllAccessListMembersRequest, opts ...grpc.CallOption) (*ListAllAccessListMembersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAllAccessListMembersResponse) - err := c.cc.Invoke(ctx, AccessListService_ListAllAccessListMembers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_ListAllAccessListMembers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -233,8 +246,9 @@ func (c *accessListServiceClient) ListAllAccessListMembers(ctx context.Context, } func (c *accessListServiceClient) GetAccessListMember(ctx context.Context, in *GetAccessListMemberRequest, opts ...grpc.CallOption) (*Member, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Member) - err := c.cc.Invoke(ctx, AccessListService_GetAccessListMember_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_GetAccessListMember_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -242,8 +256,9 @@ func (c *accessListServiceClient) GetAccessListMember(ctx context.Context, in *G } func (c *accessListServiceClient) UpsertAccessListMember(ctx context.Context, in *UpsertAccessListMemberRequest, opts ...grpc.CallOption) (*Member, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Member) - err := c.cc.Invoke(ctx, AccessListService_UpsertAccessListMember_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_UpsertAccessListMember_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -251,8 +266,9 @@ func (c *accessListServiceClient) UpsertAccessListMember(ctx context.Context, in } func (c *accessListServiceClient) UpdateAccessListMember(ctx context.Context, in *UpdateAccessListMemberRequest, opts ...grpc.CallOption) (*Member, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Member) - err := c.cc.Invoke(ctx, AccessListService_UpdateAccessListMember_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_UpdateAccessListMember_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -260,8 +276,9 @@ func (c *accessListServiceClient) UpdateAccessListMember(ctx context.Context, in } func (c *accessListServiceClient) DeleteAccessListMember(ctx context.Context, in *DeleteAccessListMemberRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAccessListMember_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAccessListMember_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -269,8 +286,9 @@ func (c *accessListServiceClient) DeleteAccessListMember(ctx context.Context, in } func (c *accessListServiceClient) DeleteAllAccessListMembersForAccessList(ctx context.Context, in *DeleteAllAccessListMembersForAccessListRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessListMembersForAccessList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessListMembersForAccessList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -278,8 +296,9 @@ func (c *accessListServiceClient) DeleteAllAccessListMembersForAccessList(ctx co } func (c *accessListServiceClient) DeleteAllAccessListMembers(ctx context.Context, in *DeleteAllAccessListMembersRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessListMembers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAllAccessListMembers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -287,8 +306,9 @@ func (c *accessListServiceClient) DeleteAllAccessListMembers(ctx context.Context } func (c *accessListServiceClient) UpsertAccessListWithMembers(ctx context.Context, in *UpsertAccessListWithMembersRequest, opts ...grpc.CallOption) (*UpsertAccessListWithMembersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpsertAccessListWithMembersResponse) - err := c.cc.Invoke(ctx, AccessListService_UpsertAccessListWithMembers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_UpsertAccessListWithMembers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -296,8 +316,9 @@ func (c *accessListServiceClient) UpsertAccessListWithMembers(ctx context.Contex } func (c *accessListServiceClient) ListAccessListReviews(ctx context.Context, in *ListAccessListReviewsRequest, opts ...grpc.CallOption) (*ListAccessListReviewsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAccessListReviewsResponse) - err := c.cc.Invoke(ctx, AccessListService_ListAccessListReviews_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_ListAccessListReviews_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -305,8 +326,9 @@ func (c *accessListServiceClient) ListAccessListReviews(ctx context.Context, in } func (c *accessListServiceClient) ListAllAccessListReviews(ctx context.Context, in *ListAllAccessListReviewsRequest, opts ...grpc.CallOption) (*ListAllAccessListReviewsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAllAccessListReviewsResponse) - err := c.cc.Invoke(ctx, AccessListService_ListAllAccessListReviews_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_ListAllAccessListReviews_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -314,8 +336,9 @@ func (c *accessListServiceClient) ListAllAccessListReviews(ctx context.Context, } func (c *accessListServiceClient) CreateAccessListReview(ctx context.Context, in *CreateAccessListReviewRequest, opts ...grpc.CallOption) (*CreateAccessListReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateAccessListReviewResponse) - err := c.cc.Invoke(ctx, AccessListService_CreateAccessListReview_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_CreateAccessListReview_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -323,8 +346,9 @@ func (c *accessListServiceClient) CreateAccessListReview(ctx context.Context, in } func (c *accessListServiceClient) DeleteAccessListReview(ctx context.Context, in *DeleteAccessListReviewRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessListService_DeleteAccessListReview_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_DeleteAccessListReview_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -332,8 +356,9 @@ func (c *accessListServiceClient) DeleteAccessListReview(ctx context.Context, in } func (c *accessListServiceClient) AccessRequestPromote(ctx context.Context, in *AccessRequestPromoteRequest, opts ...grpc.CallOption) (*AccessRequestPromoteResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessRequestPromoteResponse) - err := c.cc.Invoke(ctx, AccessListService_AccessRequestPromote_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_AccessRequestPromote_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -341,8 +366,9 @@ func (c *accessListServiceClient) AccessRequestPromote(ctx context.Context, in * } func (c *accessListServiceClient) GetSuggestedAccessLists(ctx context.Context, in *GetSuggestedAccessListsRequest, opts ...grpc.CallOption) (*GetSuggestedAccessListsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetSuggestedAccessListsResponse) - err := c.cc.Invoke(ctx, AccessListService_GetSuggestedAccessLists_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessListService_GetSuggestedAccessLists_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -352,6 +378,8 @@ func (c *accessListServiceClient) GetSuggestedAccessLists(ctx context.Context, i // AccessListServiceServer is the server API for AccessListService service. // All implementations must embed UnimplementedAccessListServiceServer // for forward compatibility +// +// AccessListService provides CRUD methods for Access List resources. type AccessListServiceServer interface { // GetAccessLists returns a list of all access lists. GetAccessLists(context.Context, *GetAccessListsRequest) (*GetAccessListsResponse, error) diff --git a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go index 896483a6e85b1..71c4bbcd01f27 100644 --- a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/accessmonitoringrules/v1/access_monitoring_rules_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AccessMonitoringRulesService_CreateAccessMonitoringRule_FullMethodName = "/teleport.accessmonitoringrules.v1.AccessMonitoringRulesService/CreateAccessMonitoringRule" @@ -46,6 +46,8 @@ const ( // AccessMonitoringRulesServiceClient is the client API for AccessMonitoringRulesService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AccessMonitoringRulesService provides CRUD methods for Access Monitoring Rules resources. type AccessMonitoringRulesServiceClient interface { // CreateAccessMonitoringRule creates the specified access monitoring rule. CreateAccessMonitoringRule(ctx context.Context, in *CreateAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*AccessMonitoringRule, error) @@ -72,8 +74,9 @@ func NewAccessMonitoringRulesServiceClient(cc grpc.ClientConnInterface) AccessMo } func (c *accessMonitoringRulesServiceClient) CreateAccessMonitoringRule(ctx context.Context, in *CreateAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*AccessMonitoringRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessMonitoringRule) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_CreateAccessMonitoringRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_CreateAccessMonitoringRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -81,8 +84,9 @@ func (c *accessMonitoringRulesServiceClient) CreateAccessMonitoringRule(ctx cont } func (c *accessMonitoringRulesServiceClient) UpdateAccessMonitoringRule(ctx context.Context, in *UpdateAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*AccessMonitoringRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessMonitoringRule) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_UpdateAccessMonitoringRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_UpdateAccessMonitoringRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -90,8 +94,9 @@ func (c *accessMonitoringRulesServiceClient) UpdateAccessMonitoringRule(ctx cont } func (c *accessMonitoringRulesServiceClient) UpsertAccessMonitoringRule(ctx context.Context, in *UpsertAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*AccessMonitoringRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessMonitoringRule) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_UpsertAccessMonitoringRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_UpsertAccessMonitoringRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -99,8 +104,9 @@ func (c *accessMonitoringRulesServiceClient) UpsertAccessMonitoringRule(ctx cont } func (c *accessMonitoringRulesServiceClient) GetAccessMonitoringRule(ctx context.Context, in *GetAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*AccessMonitoringRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AccessMonitoringRule) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_GetAccessMonitoringRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_GetAccessMonitoringRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -108,8 +114,9 @@ func (c *accessMonitoringRulesServiceClient) GetAccessMonitoringRule(ctx context } func (c *accessMonitoringRulesServiceClient) DeleteAccessMonitoringRule(ctx context.Context, in *DeleteAccessMonitoringRuleRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_DeleteAccessMonitoringRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_DeleteAccessMonitoringRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -117,8 +124,9 @@ func (c *accessMonitoringRulesServiceClient) DeleteAccessMonitoringRule(ctx cont } func (c *accessMonitoringRulesServiceClient) ListAccessMonitoringRules(ctx context.Context, in *ListAccessMonitoringRulesRequest, opts ...grpc.CallOption) (*ListAccessMonitoringRulesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAccessMonitoringRulesResponse) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_ListAccessMonitoringRules_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_ListAccessMonitoringRules_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -126,8 +134,9 @@ func (c *accessMonitoringRulesServiceClient) ListAccessMonitoringRules(ctx conte } func (c *accessMonitoringRulesServiceClient) ListAccessMonitoringRulesWithFilter(ctx context.Context, in *ListAccessMonitoringRulesWithFilterRequest, opts ...grpc.CallOption) (*ListAccessMonitoringRulesWithFilterResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAccessMonitoringRulesWithFilterResponse) - err := c.cc.Invoke(ctx, AccessMonitoringRulesService_ListAccessMonitoringRulesWithFilter_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessMonitoringRulesService_ListAccessMonitoringRulesWithFilter_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -137,6 +146,8 @@ func (c *accessMonitoringRulesServiceClient) ListAccessMonitoringRulesWithFilter // AccessMonitoringRulesServiceServer is the server API for AccessMonitoringRulesService service. // All implementations must embed UnimplementedAccessMonitoringRulesServiceServer // for forward compatibility +// +// AccessMonitoringRulesService provides CRUD methods for Access Monitoring Rules resources. type AccessMonitoringRulesServiceServer interface { // CreateAccessMonitoringRule creates the specified access monitoring rule. CreateAccessMonitoringRule(context.Context, *CreateAccessMonitoringRuleRequest) (*AccessMonitoringRule, error) diff --git a/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go b/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go index d20b71d55761e..e72abf53a92c6 100644 --- a/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go +++ b/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/auditlog/v1/auditlog.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AuditLogService_StreamUnstructuredSessionEvents_FullMethodName = "/teleport.auditlog.v1.AuditLogService/StreamUnstructuredSessionEvents" @@ -40,6 +40,8 @@ const ( // AuditLogServiceClient is the client API for AuditLogService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AuditLogService provides methods to access audit log. type AuditLogServiceClient interface { // StreamUnstructuredSessionEvents streams audit events from a given session recording in an unstructured format. // This endpoint is used by the event handler to retrieve the session events as JSON. @@ -58,11 +60,12 @@ func NewAuditLogServiceClient(cc grpc.ClientConnInterface) AuditLogServiceClient } func (c *auditLogServiceClient) StreamUnstructuredSessionEvents(ctx context.Context, in *StreamUnstructuredSessionEventsRequest, opts ...grpc.CallOption) (AuditLogService_StreamUnstructuredSessionEventsClient, error) { - stream, err := c.cc.NewStream(ctx, &AuditLogService_ServiceDesc.Streams[0], AuditLogService_StreamUnstructuredSessionEvents_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AuditLogService_ServiceDesc.Streams[0], AuditLogService_StreamUnstructuredSessionEvents_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &auditLogServiceStreamUnstructuredSessionEventsClient{stream} + x := &auditLogServiceStreamUnstructuredSessionEventsClient{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -90,8 +93,9 @@ func (x *auditLogServiceStreamUnstructuredSessionEventsClient) Recv() (*EventUns } func (c *auditLogServiceClient) GetUnstructuredEvents(ctx context.Context, in *GetUnstructuredEventsRequest, opts ...grpc.CallOption) (*EventsUnstructured, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EventsUnstructured) - err := c.cc.Invoke(ctx, AuditLogService_GetUnstructuredEvents_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AuditLogService_GetUnstructuredEvents_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -101,6 +105,8 @@ func (c *auditLogServiceClient) GetUnstructuredEvents(ctx context.Context, in *G // AuditLogServiceServer is the server API for AuditLogService service. // All implementations must embed UnimplementedAuditLogServiceServer // for forward compatibility +// +// AuditLogService provides methods to access audit log. type AuditLogServiceServer interface { // StreamUnstructuredSessionEvents streams audit events from a given session recording in an unstructured format. // This endpoint is used by the event handler to retrieve the session events as JSON. @@ -139,7 +145,7 @@ func _AuditLogService_StreamUnstructuredSessionEvents_Handler(srv interface{}, s if err := stream.RecvMsg(m); err != nil { return err } - return srv.(AuditLogServiceServer).StreamUnstructuredSessionEvents(m, &auditLogServiceStreamUnstructuredSessionEventsServer{stream}) + return srv.(AuditLogServiceServer).StreamUnstructuredSessionEvents(m, &auditLogServiceStreamUnstructuredSessionEventsServer{ServerStream: stream}) } type AuditLogService_StreamUnstructuredSessionEventsServer interface { diff --git a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go index 00dc46d7dbfca..3f23f0666be96 100644 --- a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/clusterconfig/v1/clusterconfig_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ClusterConfigService_GetClusterNetworkingConfig_FullMethodName = "/teleport.clusterconfig.v1.ClusterConfigService/GetClusterNetworkingConfig" @@ -53,6 +53,8 @@ const ( // ClusterConfigServiceClient is the client API for ClusterConfigService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// ClusterConfigService provides methods to manage cluster configuration resources. type ClusterConfigServiceClient interface { // GetClusterNetworkingConfig retrieves the active cluster networking configuration. GetClusterNetworkingConfig(ctx context.Context, in *GetClusterNetworkingConfigRequest, opts ...grpc.CallOption) (*types.ClusterNetworkingConfigV2, error) @@ -93,8 +95,9 @@ func NewClusterConfigServiceClient(cc grpc.ClientConnInterface) ClusterConfigSer } func (c *clusterConfigServiceClient) GetClusterNetworkingConfig(ctx context.Context, in *GetClusterNetworkingConfigRequest, opts ...grpc.CallOption) (*types.ClusterNetworkingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.ClusterNetworkingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterNetworkingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterNetworkingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +105,9 @@ func (c *clusterConfigServiceClient) GetClusterNetworkingConfig(ctx context.Cont } func (c *clusterConfigServiceClient) UpdateClusterNetworkingConfig(ctx context.Context, in *UpdateClusterNetworkingConfigRequest, opts ...grpc.CallOption) (*types.ClusterNetworkingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.ClusterNetworkingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpdateClusterNetworkingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpdateClusterNetworkingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -111,8 +115,9 @@ func (c *clusterConfigServiceClient) UpdateClusterNetworkingConfig(ctx context.C } func (c *clusterConfigServiceClient) UpsertClusterNetworkingConfig(ctx context.Context, in *UpsertClusterNetworkingConfigRequest, opts ...grpc.CallOption) (*types.ClusterNetworkingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.ClusterNetworkingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpsertClusterNetworkingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpsertClusterNetworkingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -120,8 +125,9 @@ func (c *clusterConfigServiceClient) UpsertClusterNetworkingConfig(ctx context.C } func (c *clusterConfigServiceClient) ResetClusterNetworkingConfig(ctx context.Context, in *ResetClusterNetworkingConfigRequest, opts ...grpc.CallOption) (*types.ClusterNetworkingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.ClusterNetworkingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_ResetClusterNetworkingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_ResetClusterNetworkingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,8 +135,9 @@ func (c *clusterConfigServiceClient) ResetClusterNetworkingConfig(ctx context.Co } func (c *clusterConfigServiceClient) GetSessionRecordingConfig(ctx context.Context, in *GetSessionRecordingConfigRequest, opts ...grpc.CallOption) (*types.SessionRecordingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.SessionRecordingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_GetSessionRecordingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_GetSessionRecordingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -138,8 +145,9 @@ func (c *clusterConfigServiceClient) GetSessionRecordingConfig(ctx context.Conte } func (c *clusterConfigServiceClient) UpdateSessionRecordingConfig(ctx context.Context, in *UpdateSessionRecordingConfigRequest, opts ...grpc.CallOption) (*types.SessionRecordingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.SessionRecordingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpdateSessionRecordingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpdateSessionRecordingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -147,8 +155,9 @@ func (c *clusterConfigServiceClient) UpdateSessionRecordingConfig(ctx context.Co } func (c *clusterConfigServiceClient) UpsertSessionRecordingConfig(ctx context.Context, in *UpsertSessionRecordingConfigRequest, opts ...grpc.CallOption) (*types.SessionRecordingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.SessionRecordingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpsertSessionRecordingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpsertSessionRecordingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -156,8 +165,9 @@ func (c *clusterConfigServiceClient) UpsertSessionRecordingConfig(ctx context.Co } func (c *clusterConfigServiceClient) ResetSessionRecordingConfig(ctx context.Context, in *ResetSessionRecordingConfigRequest, opts ...grpc.CallOption) (*types.SessionRecordingConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.SessionRecordingConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_ResetSessionRecordingConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_ResetSessionRecordingConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -165,8 +175,9 @@ func (c *clusterConfigServiceClient) ResetSessionRecordingConfig(ctx context.Con } func (c *clusterConfigServiceClient) GetAuthPreference(ctx context.Context, in *GetAuthPreferenceRequest, opts ...grpc.CallOption) (*types.AuthPreferenceV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.AuthPreferenceV2) - err := c.cc.Invoke(ctx, ClusterConfigService_GetAuthPreference_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_GetAuthPreference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -174,8 +185,9 @@ func (c *clusterConfigServiceClient) GetAuthPreference(ctx context.Context, in * } func (c *clusterConfigServiceClient) UpdateAuthPreference(ctx context.Context, in *UpdateAuthPreferenceRequest, opts ...grpc.CallOption) (*types.AuthPreferenceV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.AuthPreferenceV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpdateAuthPreference_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpdateAuthPreference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -183,8 +195,9 @@ func (c *clusterConfigServiceClient) UpdateAuthPreference(ctx context.Context, i } func (c *clusterConfigServiceClient) UpsertAuthPreference(ctx context.Context, in *UpsertAuthPreferenceRequest, opts ...grpc.CallOption) (*types.AuthPreferenceV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.AuthPreferenceV2) - err := c.cc.Invoke(ctx, ClusterConfigService_UpsertAuthPreference_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_UpsertAuthPreference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -192,8 +205,9 @@ func (c *clusterConfigServiceClient) UpsertAuthPreference(ctx context.Context, i } func (c *clusterConfigServiceClient) ResetAuthPreference(ctx context.Context, in *ResetAuthPreferenceRequest, opts ...grpc.CallOption) (*types.AuthPreferenceV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.AuthPreferenceV2) - err := c.cc.Invoke(ctx, ClusterConfigService_ResetAuthPreference_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_ResetAuthPreference_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -201,8 +215,9 @@ func (c *clusterConfigServiceClient) ResetAuthPreference(ctx context.Context, in } func (c *clusterConfigServiceClient) GetClusterAuditConfig(ctx context.Context, in *GetClusterAuditConfigRequest, opts ...grpc.CallOption) (*types.ClusterAuditConfigV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.ClusterAuditConfigV2) - err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterAuditConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterAuditConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -210,8 +225,9 @@ func (c *clusterConfigServiceClient) GetClusterAuditConfig(ctx context.Context, } func (c *clusterConfigServiceClient) GetClusterAccessGraphConfig(ctx context.Context, in *GetClusterAccessGraphConfigRequest, opts ...grpc.CallOption) (*GetClusterAccessGraphConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetClusterAccessGraphConfigResponse) - err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterAccessGraphConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ClusterConfigService_GetClusterAccessGraphConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -221,6 +237,8 @@ func (c *clusterConfigServiceClient) GetClusterAccessGraphConfig(ctx context.Con // ClusterConfigServiceServer is the server API for ClusterConfigService service. // All implementations must embed UnimplementedClusterConfigServiceServer // for forward compatibility +// +// ClusterConfigService provides methods to manage cluster configuration resources. type ClusterConfigServiceServer interface { // GetClusterNetworkingConfig retrieves the active cluster networking configuration. GetClusterNetworkingConfig(context.Context, *GetClusterNetworkingConfigRequest) (*types.ClusterNetworkingConfigV2, error) diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go index 50bf14c9e9c28..5098686592a34 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/crownjewel/v1/crownjewel_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( CrownJewelService_CreateCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/CreateCrownJewel" @@ -45,6 +45,8 @@ const ( // CrownJewelServiceClient is the client API for CrownJewelService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// CrownJewelService is a service that provides methods to manage CrownJewels. type CrownJewelServiceClient interface { // CreateCrownJewel creates a new CrownJewel. CreateCrownJewel(ctx context.Context, in *CreateCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) @@ -69,8 +71,9 @@ func NewCrownJewelServiceClient(cc grpc.ClientConnInterface) CrownJewelServiceCl } func (c *crownJewelServiceClient) CreateCrownJewel(ctx context.Context, in *CreateCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CrownJewel) - err := c.cc.Invoke(ctx, CrownJewelService_CreateCrownJewel_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_CreateCrownJewel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -78,8 +81,9 @@ func (c *crownJewelServiceClient) CreateCrownJewel(ctx context.Context, in *Crea } func (c *crownJewelServiceClient) GetCrownJewel(ctx context.Context, in *GetCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CrownJewel) - err := c.cc.Invoke(ctx, CrownJewelService_GetCrownJewel_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_GetCrownJewel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -87,8 +91,9 @@ func (c *crownJewelServiceClient) GetCrownJewel(ctx context.Context, in *GetCrow } func (c *crownJewelServiceClient) ListCrownJewels(ctx context.Context, in *ListCrownJewelsRequest, opts ...grpc.CallOption) (*ListCrownJewelsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListCrownJewelsResponse) - err := c.cc.Invoke(ctx, CrownJewelService_ListCrownJewels_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_ListCrownJewels_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -96,8 +101,9 @@ func (c *crownJewelServiceClient) ListCrownJewels(ctx context.Context, in *ListC } func (c *crownJewelServiceClient) UpdateCrownJewel(ctx context.Context, in *UpdateCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CrownJewel) - err := c.cc.Invoke(ctx, CrownJewelService_UpdateCrownJewel_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_UpdateCrownJewel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -105,8 +111,9 @@ func (c *crownJewelServiceClient) UpdateCrownJewel(ctx context.Context, in *Upda } func (c *crownJewelServiceClient) UpsertCrownJewel(ctx context.Context, in *UpsertCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CrownJewel) - err := c.cc.Invoke(ctx, CrownJewelService_UpsertCrownJewel_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_UpsertCrownJewel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -114,8 +121,9 @@ func (c *crownJewelServiceClient) UpsertCrownJewel(ctx context.Context, in *Upse } func (c *crownJewelServiceClient) DeleteCrownJewel(ctx context.Context, in *DeleteCrownJewelRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, CrownJewelService_DeleteCrownJewel_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, CrownJewelService_DeleteCrownJewel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,6 +133,8 @@ func (c *crownJewelServiceClient) DeleteCrownJewel(ctx context.Context, in *Dele // CrownJewelServiceServer is the server API for CrownJewelService service. // All implementations must embed UnimplementedCrownJewelServiceServer // for forward compatibility +// +// CrownJewelService is a service that provides methods to manage CrownJewels. type CrownJewelServiceServer interface { // CreateCrownJewel creates a new CrownJewel. CreateCrownJewel(context.Context, *CreateCrownJewelRequest) (*CrownJewel, error) diff --git a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go index f4e3249d1f8e4..9b87181d0a7e9 100644 --- a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/dbobject/v1/dbobject_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DatabaseObjectService_GetDatabaseObject_FullMethodName = "/teleport.dbobject.v1.DatabaseObjectService/GetDatabaseObject" @@ -45,6 +45,8 @@ const ( // DatabaseObjectServiceClient is the client API for DatabaseObjectService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// DatabaseObjectService provides methods to manage Teleport DatabaseObjects type DatabaseObjectServiceClient interface { // GetDatabaseObject is used to query a database object resource by its name. // @@ -80,8 +82,9 @@ func NewDatabaseObjectServiceClient(cc grpc.ClientConnInterface) DatabaseObjectS } func (c *databaseObjectServiceClient) GetDatabaseObject(ctx context.Context, in *GetDatabaseObjectRequest, opts ...grpc.CallOption) (*DatabaseObject, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObject) - err := c.cc.Invoke(ctx, DatabaseObjectService_GetDatabaseObject_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_GetDatabaseObject_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -89,8 +92,9 @@ func (c *databaseObjectServiceClient) GetDatabaseObject(ctx context.Context, in } func (c *databaseObjectServiceClient) ListDatabaseObjects(ctx context.Context, in *ListDatabaseObjectsRequest, opts ...grpc.CallOption) (*ListDatabaseObjectsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDatabaseObjectsResponse) - err := c.cc.Invoke(ctx, DatabaseObjectService_ListDatabaseObjects_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_ListDatabaseObjects_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -98,8 +102,9 @@ func (c *databaseObjectServiceClient) ListDatabaseObjects(ctx context.Context, i } func (c *databaseObjectServiceClient) CreateDatabaseObject(ctx context.Context, in *CreateDatabaseObjectRequest, opts ...grpc.CallOption) (*DatabaseObject, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObject) - err := c.cc.Invoke(ctx, DatabaseObjectService_CreateDatabaseObject_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_CreateDatabaseObject_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -107,8 +112,9 @@ func (c *databaseObjectServiceClient) CreateDatabaseObject(ctx context.Context, } func (c *databaseObjectServiceClient) UpdateDatabaseObject(ctx context.Context, in *UpdateDatabaseObjectRequest, opts ...grpc.CallOption) (*DatabaseObject, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObject) - err := c.cc.Invoke(ctx, DatabaseObjectService_UpdateDatabaseObject_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_UpdateDatabaseObject_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -116,8 +122,9 @@ func (c *databaseObjectServiceClient) UpdateDatabaseObject(ctx context.Context, } func (c *databaseObjectServiceClient) UpsertDatabaseObject(ctx context.Context, in *UpsertDatabaseObjectRequest, opts ...grpc.CallOption) (*DatabaseObject, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObject) - err := c.cc.Invoke(ctx, DatabaseObjectService_UpsertDatabaseObject_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_UpsertDatabaseObject_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,8 +132,9 @@ func (c *databaseObjectServiceClient) UpsertDatabaseObject(ctx context.Context, } func (c *databaseObjectServiceClient) DeleteDatabaseObject(ctx context.Context, in *DeleteDatabaseObjectRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, DatabaseObjectService_DeleteDatabaseObject_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectService_DeleteDatabaseObject_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -136,6 +144,8 @@ func (c *databaseObjectServiceClient) DeleteDatabaseObject(ctx context.Context, // DatabaseObjectServiceServer is the server API for DatabaseObjectService service. // All implementations must embed UnimplementedDatabaseObjectServiceServer // for forward compatibility +// +// DatabaseObjectService provides methods to manage Teleport DatabaseObjects type DatabaseObjectServiceServer interface { // GetDatabaseObject is used to query a database object resource by its name. // diff --git a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go index 17c9c032eedae..f92f31efe1ff6 100644 --- a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/dbobjectimportrule/v1/dbobjectimportrule_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DatabaseObjectImportRuleService_GetDatabaseObjectImportRule_FullMethodName = "/teleport.dbobjectimportrule.v1.DatabaseObjectImportRuleService/GetDatabaseObjectImportRule" @@ -45,6 +45,8 @@ const ( // DatabaseObjectImportRuleServiceClient is the client API for DatabaseObjectImportRuleService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// DatabaseObjectImportRuleService provides methods to manage Teleport DatabaseObjectImportRules type DatabaseObjectImportRuleServiceClient interface { // GetDatabaseObjectImportRule is used to query a DatabaseObjectImportRule resource by its name. // @@ -80,8 +82,9 @@ func NewDatabaseObjectImportRuleServiceClient(cc grpc.ClientConnInterface) Datab } func (c *databaseObjectImportRuleServiceClient) GetDatabaseObjectImportRule(ctx context.Context, in *GetDatabaseObjectImportRuleRequest, opts ...grpc.CallOption) (*DatabaseObjectImportRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObjectImportRule) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_GetDatabaseObjectImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_GetDatabaseObjectImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -89,8 +92,9 @@ func (c *databaseObjectImportRuleServiceClient) GetDatabaseObjectImportRule(ctx } func (c *databaseObjectImportRuleServiceClient) ListDatabaseObjectImportRules(ctx context.Context, in *ListDatabaseObjectImportRulesRequest, opts ...grpc.CallOption) (*ListDatabaseObjectImportRulesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDatabaseObjectImportRulesResponse) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_ListDatabaseObjectImportRules_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_ListDatabaseObjectImportRules_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -98,8 +102,9 @@ func (c *databaseObjectImportRuleServiceClient) ListDatabaseObjectImportRules(ct } func (c *databaseObjectImportRuleServiceClient) CreateDatabaseObjectImportRule(ctx context.Context, in *CreateDatabaseObjectImportRuleRequest, opts ...grpc.CallOption) (*DatabaseObjectImportRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObjectImportRule) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_CreateDatabaseObjectImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_CreateDatabaseObjectImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -107,8 +112,9 @@ func (c *databaseObjectImportRuleServiceClient) CreateDatabaseObjectImportRule(c } func (c *databaseObjectImportRuleServiceClient) UpdateDatabaseObjectImportRule(ctx context.Context, in *UpdateDatabaseObjectImportRuleRequest, opts ...grpc.CallOption) (*DatabaseObjectImportRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObjectImportRule) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_UpdateDatabaseObjectImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_UpdateDatabaseObjectImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -116,8 +122,9 @@ func (c *databaseObjectImportRuleServiceClient) UpdateDatabaseObjectImportRule(c } func (c *databaseObjectImportRuleServiceClient) UpsertDatabaseObjectImportRule(ctx context.Context, in *UpsertDatabaseObjectImportRuleRequest, opts ...grpc.CallOption) (*DatabaseObjectImportRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DatabaseObjectImportRule) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_UpsertDatabaseObjectImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_UpsertDatabaseObjectImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,8 +132,9 @@ func (c *databaseObjectImportRuleServiceClient) UpsertDatabaseObjectImportRule(c } func (c *databaseObjectImportRuleServiceClient) DeleteDatabaseObjectImportRule(ctx context.Context, in *DeleteDatabaseObjectImportRuleRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_DeleteDatabaseObjectImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DatabaseObjectImportRuleService_DeleteDatabaseObjectImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -136,6 +144,8 @@ func (c *databaseObjectImportRuleServiceClient) DeleteDatabaseObjectImportRule(c // DatabaseObjectImportRuleServiceServer is the server API for DatabaseObjectImportRuleService service. // All implementations must embed UnimplementedDatabaseObjectImportRuleServiceServer // for forward compatibility +// +// DatabaseObjectImportRuleService provides methods to manage Teleport DatabaseObjectImportRules type DatabaseObjectImportRuleServiceServer interface { // GetDatabaseObjectImportRule is used to query a DatabaseObjectImportRule resource by its name. // 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 88870c1a06460..86be79eb248cf 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 @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/devicetrust/v1/devicetrust_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DeviceTrustService_CreateDevice_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/CreateDevice" @@ -53,6 +53,28 @@ const ( // DeviceTrustServiceClient is the client API for DeviceTrustService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// DeviceTrustService provides methods to manage, enroll and authenticate +// trusted devices. +// +// A trusted device is a device that is registered and enrolled with Teleport, +// thus allowing the system to provide some guarantees about its provenance and +// state. +// +// Managing devices requires the corresponding CRUD "device" permission. +// Additionally, creating enrollment tokens requires the "create_enroll_token" +// permission and enrolling devices requires the "enroll" permission. See +// CreateDevice, CreateDeviceEnrollToken and EnrollDevice for reference. +// +// An authenticated, trusted device allows its user to perform device-aware +// actions. Such actions include accessing an SSH node, managing sensitive +// resources via `tctl`, etc. The enforcement mode is defined via cluster-wide +// and/or per-role toggles. Device authentication is automatic for enrolled +// devices communicating with Enterprise clusters. See AuthenticateDevice for +// reference. +// +// Device Trust is a Teleport Enterprise feature. Open Source Teleport clusters +// treat all Device RPCs as unimplemented (which, in fact, they are for OSS.) type DeviceTrustServiceClient interface { // CreateDevice creates a device, effectively registering it on Teleport. // Devices need to be registered before they can be enrolled. @@ -164,8 +186,9 @@ func NewDeviceTrustServiceClient(cc grpc.ClientConnInterface) DeviceTrustService } func (c *deviceTrustServiceClient) CreateDevice(ctx context.Context, in *CreateDeviceRequest, opts ...grpc.CallOption) (*Device, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Device) - err := c.cc.Invoke(ctx, DeviceTrustService_CreateDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_CreateDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -173,8 +196,9 @@ func (c *deviceTrustServiceClient) CreateDevice(ctx context.Context, in *CreateD } func (c *deviceTrustServiceClient) UpdateDevice(ctx context.Context, in *UpdateDeviceRequest, opts ...grpc.CallOption) (*Device, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Device) - err := c.cc.Invoke(ctx, DeviceTrustService_UpdateDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_UpdateDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -182,8 +206,9 @@ func (c *deviceTrustServiceClient) UpdateDevice(ctx context.Context, in *UpdateD } func (c *deviceTrustServiceClient) UpsertDevice(ctx context.Context, in *UpsertDeviceRequest, opts ...grpc.CallOption) (*Device, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Device) - err := c.cc.Invoke(ctx, DeviceTrustService_UpsertDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_UpsertDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -191,8 +216,9 @@ func (c *deviceTrustServiceClient) UpsertDevice(ctx context.Context, in *UpsertD } func (c *deviceTrustServiceClient) DeleteDevice(ctx context.Context, in *DeleteDeviceRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, DeviceTrustService_DeleteDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_DeleteDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -200,8 +226,9 @@ func (c *deviceTrustServiceClient) DeleteDevice(ctx context.Context, in *DeleteD } func (c *deviceTrustServiceClient) FindDevices(ctx context.Context, in *FindDevicesRequest, opts ...grpc.CallOption) (*FindDevicesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(FindDevicesResponse) - err := c.cc.Invoke(ctx, DeviceTrustService_FindDevices_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_FindDevices_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -209,8 +236,9 @@ func (c *deviceTrustServiceClient) FindDevices(ctx context.Context, in *FindDevi } func (c *deviceTrustServiceClient) GetDevice(ctx context.Context, in *GetDeviceRequest, opts ...grpc.CallOption) (*Device, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Device) - err := c.cc.Invoke(ctx, DeviceTrustService_GetDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_GetDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -218,8 +246,9 @@ func (c *deviceTrustServiceClient) GetDevice(ctx context.Context, in *GetDeviceR } func (c *deviceTrustServiceClient) ListDevices(ctx context.Context, in *ListDevicesRequest, opts ...grpc.CallOption) (*ListDevicesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDevicesResponse) - err := c.cc.Invoke(ctx, DeviceTrustService_ListDevices_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_ListDevices_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -227,8 +256,9 @@ func (c *deviceTrustServiceClient) ListDevices(ctx context.Context, in *ListDevi } func (c *deviceTrustServiceClient) BulkCreateDevices(ctx context.Context, in *BulkCreateDevicesRequest, opts ...grpc.CallOption) (*BulkCreateDevicesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(BulkCreateDevicesResponse) - err := c.cc.Invoke(ctx, DeviceTrustService_BulkCreateDevices_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_BulkCreateDevices_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -236,8 +266,9 @@ func (c *deviceTrustServiceClient) BulkCreateDevices(ctx context.Context, in *Bu } func (c *deviceTrustServiceClient) CreateDeviceEnrollToken(ctx context.Context, in *CreateDeviceEnrollTokenRequest, opts ...grpc.CallOption) (*DeviceEnrollToken, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeviceEnrollToken) - err := c.cc.Invoke(ctx, DeviceTrustService_CreateDeviceEnrollToken_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_CreateDeviceEnrollToken_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -245,11 +276,12 @@ func (c *deviceTrustServiceClient) CreateDeviceEnrollToken(ctx context.Context, } func (c *deviceTrustServiceClient) EnrollDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_EnrollDeviceClient, error) { - stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[0], DeviceTrustService_EnrollDevice_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[0], DeviceTrustService_EnrollDevice_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceEnrollDeviceClient{stream} + x := &deviceTrustServiceEnrollDeviceClient{ClientStream: stream} return x, nil } @@ -276,11 +308,12 @@ func (x *deviceTrustServiceEnrollDeviceClient) Recv() (*EnrollDeviceResponse, er } func (c *deviceTrustServiceClient) AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_AuthenticateDeviceClient, error) { - stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[1], DeviceTrustService_AuthenticateDevice_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[1], DeviceTrustService_AuthenticateDevice_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceAuthenticateDeviceClient{stream} + x := &deviceTrustServiceAuthenticateDeviceClient{ClientStream: stream} return x, nil } @@ -307,8 +340,9 @@ func (x *deviceTrustServiceAuthenticateDeviceClient) Recv() (*AuthenticateDevice } func (c *deviceTrustServiceClient) ConfirmDeviceWebAuthentication(ctx context.Context, in *ConfirmDeviceWebAuthenticationRequest, opts ...grpc.CallOption) (*ConfirmDeviceWebAuthenticationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ConfirmDeviceWebAuthenticationResponse) - err := c.cc.Invoke(ctx, DeviceTrustService_ConfirmDeviceWebAuthentication_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_ConfirmDeviceWebAuthentication_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -316,11 +350,12 @@ func (c *deviceTrustServiceClient) ConfirmDeviceWebAuthentication(ctx context.Co } 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...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[2], DeviceTrustService_SyncInventory_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceSyncInventoryClient{stream} + x := &deviceTrustServiceSyncInventoryClient{ClientStream: stream} return x, nil } @@ -348,8 +383,9 @@ func (x *deviceTrustServiceSyncInventoryClient) Recv() (*SyncInventoryResponse, // Deprecated: Do not use. func (c *deviceTrustServiceClient) GetDevicesUsage(ctx context.Context, in *GetDevicesUsageRequest, opts ...grpc.CallOption) (*DevicesUsage, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DevicesUsage) - err := c.cc.Invoke(ctx, DeviceTrustService_GetDevicesUsage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DeviceTrustService_GetDevicesUsage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -359,6 +395,28 @@ func (c *deviceTrustServiceClient) GetDevicesUsage(ctx context.Context, in *GetD // DeviceTrustServiceServer is the server API for DeviceTrustService service. // All implementations must embed UnimplementedDeviceTrustServiceServer // for forward compatibility +// +// DeviceTrustService provides methods to manage, enroll and authenticate +// trusted devices. +// +// A trusted device is a device that is registered and enrolled with Teleport, +// thus allowing the system to provide some guarantees about its provenance and +// state. +// +// Managing devices requires the corresponding CRUD "device" permission. +// Additionally, creating enrollment tokens requires the "create_enroll_token" +// permission and enrolling devices requires the "enroll" permission. See +// CreateDevice, CreateDeviceEnrollToken and EnrollDevice for reference. +// +// An authenticated, trusted device allows its user to perform device-aware +// actions. Such actions include accessing an SSH node, managing sensitive +// resources via `tctl`, etc. The enforcement mode is defined via cluster-wide +// and/or per-role toggles. Device authentication is automatic for enrolled +// devices communicating with Enterprise clusters. See AuthenticateDevice for +// reference. +// +// Device Trust is a Teleport Enterprise feature. Open Source Teleport clusters +// treat all Device RPCs as unimplemented (which, in fact, they are for OSS.) type DeviceTrustServiceServer interface { // CreateDevice creates a device, effectively registering it on Teleport. // Devices need to be registered before they can be enrolled. @@ -684,7 +742,7 @@ func _DeviceTrustService_CreateDeviceEnrollToken_Handler(srv interface{}, ctx co } func _DeviceTrustService_EnrollDevice_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).EnrollDevice(&deviceTrustServiceEnrollDeviceServer{stream}) + return srv.(DeviceTrustServiceServer).EnrollDevice(&deviceTrustServiceEnrollDeviceServer{ServerStream: stream}) } type DeviceTrustService_EnrollDeviceServer interface { @@ -710,7 +768,7 @@ func (x *deviceTrustServiceEnrollDeviceServer) Recv() (*EnrollDeviceRequest, err } func _DeviceTrustService_AuthenticateDevice_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).AuthenticateDevice(&deviceTrustServiceAuthenticateDeviceServer{stream}) + return srv.(DeviceTrustServiceServer).AuthenticateDevice(&deviceTrustServiceAuthenticateDeviceServer{ServerStream: stream}) } type DeviceTrustService_AuthenticateDeviceServer interface { @@ -754,7 +812,7 @@ func _DeviceTrustService_ConfirmDeviceWebAuthentication_Handler(srv interface{}, } func _DeviceTrustService_SyncInventory_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).SyncInventory(&deviceTrustServiceSyncInventoryServer{stream}) + return srv.(DeviceTrustServiceServer).SyncInventory(&deviceTrustServiceSyncInventoryServer{ServerStream: stream}) } type DeviceTrustService_SyncInventoryServer interface { diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go index 520738432b550..931e454d0d742 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/discoveryconfig/v1/discoveryconfig_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( DiscoveryConfigService_ListDiscoveryConfigs_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/ListDiscoveryConfigs" @@ -47,6 +47,12 @@ const ( // DiscoveryConfigServiceClient is the client API for DiscoveryConfigService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// DiscoveryConfigService provides methods to manage Discovery Configs. +// +// Each Discovery Config has a set of matchers and a DiscoveryGroup. +// DiscoveryServices also have a DiscoveryGroup which will be used to load all the matchers from +// all the DiscoveryConfigs that have the same Group. type DiscoveryConfigServiceClient interface { // ListDiscoveryConfigs returns a paginated list of Discovery Config resources. ListDiscoveryConfigs(ctx context.Context, in *ListDiscoveryConfigsRequest, opts ...grpc.CallOption) (*ListDiscoveryConfigsResponse, error) @@ -75,8 +81,9 @@ func NewDiscoveryConfigServiceClient(cc grpc.ClientConnInterface) DiscoveryConfi } func (c *discoveryConfigServiceClient) ListDiscoveryConfigs(ctx context.Context, in *ListDiscoveryConfigsRequest, opts ...grpc.CallOption) (*ListDiscoveryConfigsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDiscoveryConfigsResponse) - err := c.cc.Invoke(ctx, DiscoveryConfigService_ListDiscoveryConfigs_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_ListDiscoveryConfigs_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -84,8 +91,9 @@ func (c *discoveryConfigServiceClient) ListDiscoveryConfigs(ctx context.Context, } func (c *discoveryConfigServiceClient) GetDiscoveryConfig(ctx context.Context, in *GetDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiscoveryConfig) - err := c.cc.Invoke(ctx, DiscoveryConfigService_GetDiscoveryConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_GetDiscoveryConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -93,8 +101,9 @@ func (c *discoveryConfigServiceClient) GetDiscoveryConfig(ctx context.Context, i } func (c *discoveryConfigServiceClient) CreateDiscoveryConfig(ctx context.Context, in *CreateDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiscoveryConfig) - err := c.cc.Invoke(ctx, DiscoveryConfigService_CreateDiscoveryConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_CreateDiscoveryConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +111,9 @@ func (c *discoveryConfigServiceClient) CreateDiscoveryConfig(ctx context.Context } func (c *discoveryConfigServiceClient) UpdateDiscoveryConfig(ctx context.Context, in *UpdateDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiscoveryConfig) - err := c.cc.Invoke(ctx, DiscoveryConfigService_UpdateDiscoveryConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_UpdateDiscoveryConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -111,8 +121,9 @@ func (c *discoveryConfigServiceClient) UpdateDiscoveryConfig(ctx context.Context } func (c *discoveryConfigServiceClient) UpsertDiscoveryConfig(ctx context.Context, in *UpsertDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiscoveryConfig) - err := c.cc.Invoke(ctx, DiscoveryConfigService_UpsertDiscoveryConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_UpsertDiscoveryConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -120,8 +131,9 @@ func (c *discoveryConfigServiceClient) UpsertDiscoveryConfig(ctx context.Context } func (c *discoveryConfigServiceClient) DeleteDiscoveryConfig(ctx context.Context, in *DeleteDiscoveryConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, DiscoveryConfigService_DeleteDiscoveryConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_DeleteDiscoveryConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,8 +141,9 @@ func (c *discoveryConfigServiceClient) DeleteDiscoveryConfig(ctx context.Context } func (c *discoveryConfigServiceClient) DeleteAllDiscoveryConfigs(ctx context.Context, in *DeleteAllDiscoveryConfigsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, DiscoveryConfigService_DeleteAllDiscoveryConfigs_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_DeleteAllDiscoveryConfigs_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -138,8 +151,9 @@ func (c *discoveryConfigServiceClient) DeleteAllDiscoveryConfigs(ctx context.Con } func (c *discoveryConfigServiceClient) UpdateDiscoveryConfigStatus(ctx context.Context, in *UpdateDiscoveryConfigStatusRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiscoveryConfig) - err := c.cc.Invoke(ctx, DiscoveryConfigService_UpdateDiscoveryConfigStatus_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, DiscoveryConfigService_UpdateDiscoveryConfigStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -149,6 +163,12 @@ func (c *discoveryConfigServiceClient) UpdateDiscoveryConfigStatus(ctx context.C // DiscoveryConfigServiceServer is the server API for DiscoveryConfigService service. // All implementations must embed UnimplementedDiscoveryConfigServiceServer // for forward compatibility +// +// DiscoveryConfigService provides methods to manage Discovery Configs. +// +// Each Discovery Config has a set of matchers and a DiscoveryGroup. +// DiscoveryServices also have a DiscoveryGroup which will be used to load all the matchers from +// all the DiscoveryConfigs that have the same Group. type DiscoveryConfigServiceServer interface { // ListDiscoveryConfigs returns a paginated list of Discovery Config resources. ListDiscoveryConfigs(context.Context, *ListDiscoveryConfigsRequest) (*ListDiscoveryConfigsResponse, error) diff --git a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go index 40f0b4eb79da3..4daf16e04b176 100644 --- a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/externalauditstorage/v1/externalauditstorage_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ExternalAuditStorageService_GetDraftExternalAuditStorage_FullMethodName = "/teleport.externalauditstorage.v1.ExternalAuditStorageService/GetDraftExternalAuditStorage" @@ -50,6 +50,19 @@ const ( // ExternalAuditStorageServiceClient is the client API for ExternalAuditStorageService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// ExternalAuditStorageService provides methods to manage External Audit Storage. +// +// The service supports singleton "draft" and "cluster" configurations, allowing +// us to store state for an in-progress configuring and to test the connection +// before enabling the feature in the cluster. +// +// After creating and testing a draft configuration call +// PromoteToClusterExternalAuditStorage, which will clone the existing draft +// ExternalAuditStorage into a new resource and upsert it as the new cluster +// ExternalAuditStorage. +// The promoted cluster resource can't be mutated, it can only be deleted or +// replaced by promoting a new draft. type ExternalAuditStorageServiceClient interface { // GetDraftExternalAuditStorage returns the draft external audit storage configuration resource. GetDraftExternalAuditStorage(ctx context.Context, in *GetDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*GetDraftExternalAuditStorageResponse, error) @@ -90,8 +103,9 @@ func NewExternalAuditStorageServiceClient(cc grpc.ClientConnInterface) ExternalA } func (c *externalAuditStorageServiceClient) GetDraftExternalAuditStorage(ctx context.Context, in *GetDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*GetDraftExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetDraftExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_GetDraftExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_GetDraftExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -99,8 +113,9 @@ func (c *externalAuditStorageServiceClient) GetDraftExternalAuditStorage(ctx con } func (c *externalAuditStorageServiceClient) CreateDraftExternalAuditStorage(ctx context.Context, in *CreateDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*CreateDraftExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateDraftExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_CreateDraftExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_CreateDraftExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -108,8 +123,9 @@ func (c *externalAuditStorageServiceClient) CreateDraftExternalAuditStorage(ctx } func (c *externalAuditStorageServiceClient) UpsertDraftExternalAuditStorage(ctx context.Context, in *UpsertDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*UpsertDraftExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpsertDraftExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_UpsertDraftExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_UpsertDraftExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -117,8 +133,9 @@ func (c *externalAuditStorageServiceClient) UpsertDraftExternalAuditStorage(ctx } func (c *externalAuditStorageServiceClient) DeleteDraftExternalAuditStorage(ctx context.Context, in *DeleteDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_DeleteDraftExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_DeleteDraftExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -126,8 +143,9 @@ func (c *externalAuditStorageServiceClient) DeleteDraftExternalAuditStorage(ctx } func (c *externalAuditStorageServiceClient) PromoteToClusterExternalAuditStorage(ctx context.Context, in *PromoteToClusterExternalAuditStorageRequest, opts ...grpc.CallOption) (*PromoteToClusterExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PromoteToClusterExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_PromoteToClusterExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_PromoteToClusterExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -135,8 +153,9 @@ func (c *externalAuditStorageServiceClient) PromoteToClusterExternalAuditStorage } func (c *externalAuditStorageServiceClient) GetClusterExternalAuditStorage(ctx context.Context, in *GetClusterExternalAuditStorageRequest, opts ...grpc.CallOption) (*GetClusterExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetClusterExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_GetClusterExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_GetClusterExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -144,8 +163,9 @@ func (c *externalAuditStorageServiceClient) GetClusterExternalAuditStorage(ctx c } func (c *externalAuditStorageServiceClient) DisableClusterExternalAuditStorage(ctx context.Context, in *DisableClusterExternalAuditStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_DisableClusterExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_DisableClusterExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -153,8 +173,9 @@ func (c *externalAuditStorageServiceClient) DisableClusterExternalAuditStorage(c } func (c *externalAuditStorageServiceClient) GenerateDraftExternalAuditStorage(ctx context.Context, in *GenerateDraftExternalAuditStorageRequest, opts ...grpc.CallOption) (*GenerateDraftExternalAuditStorageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GenerateDraftExternalAuditStorageResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_GenerateDraftExternalAuditStorage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_GenerateDraftExternalAuditStorage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -162,8 +183,9 @@ func (c *externalAuditStorageServiceClient) GenerateDraftExternalAuditStorage(ct } func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageBuckets(ctx context.Context, in *TestDraftExternalAuditStorageBucketsRequest, opts ...grpc.CallOption) (*TestDraftExternalAuditStorageBucketsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TestDraftExternalAuditStorageBucketsResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageBuckets_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageBuckets_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -171,8 +193,9 @@ func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageBuckets } func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageGlue(ctx context.Context, in *TestDraftExternalAuditStorageGlueRequest, opts ...grpc.CallOption) (*TestDraftExternalAuditStorageGlueResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TestDraftExternalAuditStorageGlueResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageGlue_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageGlue_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -180,8 +203,9 @@ func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageGlue(ct } func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageAthena(ctx context.Context, in *TestDraftExternalAuditStorageAthenaRequest, opts ...grpc.CallOption) (*TestDraftExternalAuditStorageAthenaResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TestDraftExternalAuditStorageAthenaResponse) - err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageAthena_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ExternalAuditStorageService_TestDraftExternalAuditStorageAthena_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -191,6 +215,19 @@ func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageAthena( // ExternalAuditStorageServiceServer is the server API for ExternalAuditStorageService service. // All implementations must embed UnimplementedExternalAuditStorageServiceServer // for forward compatibility +// +// ExternalAuditStorageService provides methods to manage External Audit Storage. +// +// The service supports singleton "draft" and "cluster" configurations, allowing +// us to store state for an in-progress configuring and to test the connection +// before enabling the feature in the cluster. +// +// After creating and testing a draft configuration call +// PromoteToClusterExternalAuditStorage, which will clone the existing draft +// ExternalAuditStorage into a new resource and upsert it as the new cluster +// ExternalAuditStorage. +// The promoted cluster resource can't be mutated, it can only be deleted or +// replaced by promoting a new draft. type ExternalAuditStorageServiceServer interface { // GetDraftExternalAuditStorage returns the draft external audit storage configuration resource. GetDraftExternalAuditStorage(context.Context, *GetDraftExternalAuditStorageRequest) (*GetDraftExternalAuditStorageResponse, error) diff --git a/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go b/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go index 77a917bf9a78d..5731d134c91e4 100644 --- a/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/integration/v1/awsoidc_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AWSOIDCService_ListEICE_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListEICE" @@ -47,6 +47,8 @@ const ( // AWSOIDCServiceClient is the client API for AWSOIDCService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AWSOIDCService provides access to AWS APIs using the AWS OIDC Integration. type AWSOIDCServiceClient interface { // ListEICE returns a list of EC2 Instance Connect Endpoints. // An optional NextToken that can be used to fetch the next page. @@ -92,8 +94,9 @@ func NewAWSOIDCServiceClient(cc grpc.ClientConnInterface) AWSOIDCServiceClient { } func (c *aWSOIDCServiceClient) ListEICE(ctx context.Context, in *ListEICERequest, opts ...grpc.CallOption) (*ListEICEResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListEICEResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_ListEICE_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_ListEICE_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -101,8 +104,9 @@ func (c *aWSOIDCServiceClient) ListEICE(ctx context.Context, in *ListEICERequest } func (c *aWSOIDCServiceClient) CreateEICE(ctx context.Context, in *CreateEICERequest, opts ...grpc.CallOption) (*CreateEICEResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateEICEResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_CreateEICE_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_CreateEICE_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -110,8 +114,9 @@ func (c *aWSOIDCServiceClient) CreateEICE(ctx context.Context, in *CreateEICEReq } func (c *aWSOIDCServiceClient) ListDatabases(ctx context.Context, in *ListDatabasesRequest, opts ...grpc.CallOption) (*ListDatabasesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDatabasesResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_ListDatabases_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_ListDatabases_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -119,8 +124,9 @@ func (c *aWSOIDCServiceClient) ListDatabases(ctx context.Context, in *ListDataba } func (c *aWSOIDCServiceClient) ListSecurityGroups(ctx context.Context, in *ListSecurityGroupsRequest, opts ...grpc.CallOption) (*ListSecurityGroupsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListSecurityGroupsResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_ListSecurityGroups_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_ListSecurityGroups_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -128,8 +134,9 @@ func (c *aWSOIDCServiceClient) ListSecurityGroups(ctx context.Context, in *ListS } func (c *aWSOIDCServiceClient) DeployDatabaseService(ctx context.Context, in *DeployDatabaseServiceRequest, opts ...grpc.CallOption) (*DeployDatabaseServiceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeployDatabaseServiceResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_DeployDatabaseService_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_DeployDatabaseService_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -137,8 +144,9 @@ func (c *aWSOIDCServiceClient) DeployDatabaseService(ctx context.Context, in *De } func (c *aWSOIDCServiceClient) DeployService(ctx context.Context, in *DeployServiceRequest, opts ...grpc.CallOption) (*DeployServiceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeployServiceResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_DeployService_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_DeployService_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -146,8 +154,9 @@ func (c *aWSOIDCServiceClient) DeployService(ctx context.Context, in *DeployServ } func (c *aWSOIDCServiceClient) EnrollEKSClusters(ctx context.Context, in *EnrollEKSClustersRequest, opts ...grpc.CallOption) (*EnrollEKSClustersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EnrollEKSClustersResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_EnrollEKSClusters_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_EnrollEKSClusters_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -155,8 +164,9 @@ func (c *aWSOIDCServiceClient) EnrollEKSClusters(ctx context.Context, in *Enroll } func (c *aWSOIDCServiceClient) ListEC2(ctx context.Context, in *ListEC2Request, opts ...grpc.CallOption) (*ListEC2Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListEC2Response) - err := c.cc.Invoke(ctx, AWSOIDCService_ListEC2_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_ListEC2_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -164,8 +174,9 @@ func (c *aWSOIDCServiceClient) ListEC2(ctx context.Context, in *ListEC2Request, } func (c *aWSOIDCServiceClient) ListEKSClusters(ctx context.Context, in *ListEKSClustersRequest, opts ...grpc.CallOption) (*ListEKSClustersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListEKSClustersResponse) - err := c.cc.Invoke(ctx, AWSOIDCService_ListEKSClusters_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AWSOIDCService_ListEKSClusters_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -175,6 +186,8 @@ func (c *aWSOIDCServiceClient) ListEKSClusters(ctx context.Context, in *ListEKSC // AWSOIDCServiceServer is the server API for AWSOIDCService service. // All implementations must embed UnimplementedAWSOIDCServiceServer // for forward compatibility +// +// AWSOIDCService provides access to AWS APIs using the AWS OIDC Integration. type AWSOIDCServiceServer interface { // ListEICE returns a list of EC2 Instance Connect Endpoints. // An optional NextToken that can be used to fetch the next page. diff --git a/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go b/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go index 95be8603fcc6b..dac1038e618ab 100644 --- a/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/integration/v1/integration_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( IntegrationService_ListIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/ListIntegrations" @@ -47,6 +47,8 @@ const ( // IntegrationServiceClient is the client API for IntegrationService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// IntegrationService provides methods to manage Integrations with 3rd party APIs. type IntegrationServiceClient interface { // ListIntegrations returns a paginated list of Integration resources. ListIntegrations(ctx context.Context, in *ListIntegrationsRequest, opts ...grpc.CallOption) (*ListIntegrationsResponse, error) @@ -74,8 +76,9 @@ func NewIntegrationServiceClient(cc grpc.ClientConnInterface) IntegrationService } func (c *integrationServiceClient) ListIntegrations(ctx context.Context, in *ListIntegrationsRequest, opts ...grpc.CallOption) (*ListIntegrationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListIntegrationsResponse) - err := c.cc.Invoke(ctx, IntegrationService_ListIntegrations_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_ListIntegrations_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -83,8 +86,9 @@ func (c *integrationServiceClient) ListIntegrations(ctx context.Context, in *Lis } func (c *integrationServiceClient) GetIntegration(ctx context.Context, in *GetIntegrationRequest, opts ...grpc.CallOption) (*types.IntegrationV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.IntegrationV1) - err := c.cc.Invoke(ctx, IntegrationService_GetIntegration_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_GetIntegration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -92,8 +96,9 @@ func (c *integrationServiceClient) GetIntegration(ctx context.Context, in *GetIn } func (c *integrationServiceClient) CreateIntegration(ctx context.Context, in *CreateIntegrationRequest, opts ...grpc.CallOption) (*types.IntegrationV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.IntegrationV1) - err := c.cc.Invoke(ctx, IntegrationService_CreateIntegration_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_CreateIntegration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -101,8 +106,9 @@ func (c *integrationServiceClient) CreateIntegration(ctx context.Context, in *Cr } func (c *integrationServiceClient) UpdateIntegration(ctx context.Context, in *UpdateIntegrationRequest, opts ...grpc.CallOption) (*types.IntegrationV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.IntegrationV1) - err := c.cc.Invoke(ctx, IntegrationService_UpdateIntegration_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_UpdateIntegration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -110,8 +116,9 @@ func (c *integrationServiceClient) UpdateIntegration(ctx context.Context, in *Up } func (c *integrationServiceClient) DeleteIntegration(ctx context.Context, in *DeleteIntegrationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, IntegrationService_DeleteIntegration_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_DeleteIntegration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -119,8 +126,9 @@ func (c *integrationServiceClient) DeleteIntegration(ctx context.Context, in *De } func (c *integrationServiceClient) DeleteAllIntegrations(ctx context.Context, in *DeleteAllIntegrationsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, IntegrationService_DeleteAllIntegrations_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_DeleteAllIntegrations_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -128,8 +136,9 @@ func (c *integrationServiceClient) DeleteAllIntegrations(ctx context.Context, in } func (c *integrationServiceClient) GenerateAWSOIDCToken(ctx context.Context, in *GenerateAWSOIDCTokenRequest, opts ...grpc.CallOption) (*GenerateAWSOIDCTokenResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GenerateAWSOIDCTokenResponse) - err := c.cc.Invoke(ctx, IntegrationService_GenerateAWSOIDCToken_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, IntegrationService_GenerateAWSOIDCToken_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -139,6 +148,8 @@ func (c *integrationServiceClient) GenerateAWSOIDCToken(ctx context.Context, in // IntegrationServiceServer is the server API for IntegrationService service. // All implementations must embed UnimplementedIntegrationServiceServer // for forward compatibility +// +// IntegrationService provides methods to manage Integrations with 3rd party APIs. type IntegrationServiceServer interface { // ListIntegrations returns a paginated list of Integration resources. ListIntegrations(context.Context, *ListIntegrationsRequest) (*ListIntegrationsResponse, error) diff --git a/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go b/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go index 2280ba473cac0..2be5ac46ee7d6 100644 --- a/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/kube/v1/kube_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( KubeService_ListKubernetesResources_FullMethodName = "/teleport.kube.v1.KubeService/ListKubernetesResources" @@ -39,6 +39,9 @@ const ( // KubeServiceClient is the client API for KubeService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// KubeService provides methods to list Kubernetes resources when users are not allowed +// to access the underlying cluster or resources but their `search_as_roles` allow. type KubeServiceClient interface { // ListKubernetesResources lists the Kubernetes resources without leaking details. ListKubernetesResources(ctx context.Context, in *ListKubernetesResourcesRequest, opts ...grpc.CallOption) (*ListKubernetesResourcesResponse, error) @@ -53,8 +56,9 @@ func NewKubeServiceClient(cc grpc.ClientConnInterface) KubeServiceClient { } func (c *kubeServiceClient) ListKubernetesResources(ctx context.Context, in *ListKubernetesResourcesRequest, opts ...grpc.CallOption) (*ListKubernetesResourcesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListKubernetesResourcesResponse) - err := c.cc.Invoke(ctx, KubeService_ListKubernetesResources_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, KubeService_ListKubernetesResources_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -64,6 +68,9 @@ func (c *kubeServiceClient) ListKubernetesResources(ctx context.Context, in *Lis // KubeServiceServer is the server API for KubeService service. // All implementations must embed UnimplementedKubeServiceServer // for forward compatibility +// +// KubeService provides methods to list Kubernetes resources when users are not allowed +// to access the underlying cluster or resources but their `search_as_roles` allow. type KubeServiceServer interface { // ListKubernetesResources lists the Kubernetes resources without leaking details. ListKubernetesResources(context.Context, *ListKubernetesResourcesRequest) (*ListKubernetesResourcesResponse, error) diff --git a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go index 97bf310158364..5d7ce460c5e50 100644 --- a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( KubeWaitingContainersService_ListKubernetesWaitingContainers_FullMethodName = "/teleport.kubewaitingcontainer.v1.KubeWaitingContainersService/ListKubernetesWaitingContainers" @@ -43,6 +43,10 @@ const ( // KubeWaitingContainersServiceClient is the client API for KubeWaitingContainersService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// KubeWaitingContainersService manages Kubernetes ephemeral +// containers that are waiting to be created until moderated +// session conditions are met. type KubeWaitingContainersServiceClient interface { // ListKubernetesWaitingContainers returns a Kubernetes ephemeral // container that is waiting to be created. @@ -67,8 +71,9 @@ func NewKubeWaitingContainersServiceClient(cc grpc.ClientConnInterface) KubeWait } func (c *kubeWaitingContainersServiceClient) ListKubernetesWaitingContainers(ctx context.Context, in *ListKubernetesWaitingContainersRequest, opts ...grpc.CallOption) (*ListKubernetesWaitingContainersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListKubernetesWaitingContainersResponse) - err := c.cc.Invoke(ctx, KubeWaitingContainersService_ListKubernetesWaitingContainers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, KubeWaitingContainersService_ListKubernetesWaitingContainers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -76,8 +81,9 @@ func (c *kubeWaitingContainersServiceClient) ListKubernetesWaitingContainers(ctx } func (c *kubeWaitingContainersServiceClient) GetKubernetesWaitingContainer(ctx context.Context, in *GetKubernetesWaitingContainerRequest, opts ...grpc.CallOption) (*KubernetesWaitingContainer, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(KubernetesWaitingContainer) - err := c.cc.Invoke(ctx, KubeWaitingContainersService_GetKubernetesWaitingContainer_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, KubeWaitingContainersService_GetKubernetesWaitingContainer_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -85,8 +91,9 @@ func (c *kubeWaitingContainersServiceClient) GetKubernetesWaitingContainer(ctx c } func (c *kubeWaitingContainersServiceClient) CreateKubernetesWaitingContainer(ctx context.Context, in *CreateKubernetesWaitingContainerRequest, opts ...grpc.CallOption) (*KubernetesWaitingContainer, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(KubernetesWaitingContainer) - err := c.cc.Invoke(ctx, KubeWaitingContainersService_CreateKubernetesWaitingContainer_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, KubeWaitingContainersService_CreateKubernetesWaitingContainer_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -94,8 +101,9 @@ func (c *kubeWaitingContainersServiceClient) CreateKubernetesWaitingContainer(ct } func (c *kubeWaitingContainersServiceClient) DeleteKubernetesWaitingContainer(ctx context.Context, in *DeleteKubernetesWaitingContainerRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, KubeWaitingContainersService_DeleteKubernetesWaitingContainer_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, KubeWaitingContainersService_DeleteKubernetesWaitingContainer_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -105,6 +113,10 @@ func (c *kubeWaitingContainersServiceClient) DeleteKubernetesWaitingContainer(ct // KubeWaitingContainersServiceServer is the server API for KubeWaitingContainersService service. // All implementations must embed UnimplementedKubeWaitingContainersServiceServer // for forward compatibility +// +// KubeWaitingContainersService manages Kubernetes ephemeral +// containers that are waiting to be created until moderated +// session conditions are met. type KubeWaitingContainersServiceServer interface { // ListKubernetesWaitingContainers returns a Kubernetes ephemeral // container that is waiting to be created. diff --git a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go index a8d4dc7aa99a0..fd138656fadf4 100644 --- a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/loginrule/v1/loginrule_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( LoginRuleService_CreateLoginRule_FullMethodName = "/teleport.loginrule.v1.LoginRuleService/CreateLoginRule" @@ -45,6 +45,8 @@ const ( // LoginRuleServiceClient is the client API for LoginRuleService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// LoginRuleService provides CRUD methods for the LoginRule resource. type LoginRuleServiceClient interface { // CreateLoginRule creates a login rule if one with the same name does not // already exist, else it returns an error. @@ -73,8 +75,9 @@ func NewLoginRuleServiceClient(cc grpc.ClientConnInterface) LoginRuleServiceClie } func (c *loginRuleServiceClient) CreateLoginRule(ctx context.Context, in *CreateLoginRuleRequest, opts ...grpc.CallOption) (*LoginRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoginRule) - err := c.cc.Invoke(ctx, LoginRuleService_CreateLoginRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_CreateLoginRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -82,8 +85,9 @@ func (c *loginRuleServiceClient) CreateLoginRule(ctx context.Context, in *Create } func (c *loginRuleServiceClient) UpsertLoginRule(ctx context.Context, in *UpsertLoginRuleRequest, opts ...grpc.CallOption) (*LoginRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoginRule) - err := c.cc.Invoke(ctx, LoginRuleService_UpsertLoginRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_UpsertLoginRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -91,8 +95,9 @@ func (c *loginRuleServiceClient) UpsertLoginRule(ctx context.Context, in *Upsert } func (c *loginRuleServiceClient) GetLoginRule(ctx context.Context, in *GetLoginRuleRequest, opts ...grpc.CallOption) (*LoginRule, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoginRule) - err := c.cc.Invoke(ctx, LoginRuleService_GetLoginRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_GetLoginRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -100,8 +105,9 @@ func (c *loginRuleServiceClient) GetLoginRule(ctx context.Context, in *GetLoginR } func (c *loginRuleServiceClient) ListLoginRules(ctx context.Context, in *ListLoginRulesRequest, opts ...grpc.CallOption) (*ListLoginRulesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListLoginRulesResponse) - err := c.cc.Invoke(ctx, LoginRuleService_ListLoginRules_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_ListLoginRules_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -109,8 +115,9 @@ func (c *loginRuleServiceClient) ListLoginRules(ctx context.Context, in *ListLog } func (c *loginRuleServiceClient) DeleteLoginRule(ctx context.Context, in *DeleteLoginRuleRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, LoginRuleService_DeleteLoginRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_DeleteLoginRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -118,8 +125,9 @@ func (c *loginRuleServiceClient) DeleteLoginRule(ctx context.Context, in *Delete } func (c *loginRuleServiceClient) TestLoginRule(ctx context.Context, in *TestLoginRuleRequest, opts ...grpc.CallOption) (*TestLoginRuleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TestLoginRuleResponse) - err := c.cc.Invoke(ctx, LoginRuleService_TestLoginRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, LoginRuleService_TestLoginRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,6 +137,8 @@ func (c *loginRuleServiceClient) TestLoginRule(ctx context.Context, in *TestLogi // LoginRuleServiceServer is the server API for LoginRuleService service. // All implementations must embed UnimplementedLoginRuleServiceServer // for forward compatibility +// +// LoginRuleService provides CRUD methods for the LoginRule resource. type LoginRuleServiceServer interface { // CreateLoginRule creates a login rule if one with the same name does not // already exist, else it returns an error. diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go index c8830ad91e6eb..cad254d963677 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/machineid/v1/bot_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( BotService_GetBot_FullMethodName = "/teleport.machineid.v1.BotService/GetBot" @@ -45,6 +45,8 @@ const ( // BotServiceClient is the client API for BotService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// BotService provides methods to manage Teleport Bots type BotServiceClient interface { // GetBot is used to query a Bot resource by its name. // @@ -80,8 +82,9 @@ func NewBotServiceClient(cc grpc.ClientConnInterface) BotServiceClient { } func (c *botServiceClient) GetBot(ctx context.Context, in *GetBotRequest, opts ...grpc.CallOption) (*Bot, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Bot) - err := c.cc.Invoke(ctx, BotService_GetBot_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_GetBot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -89,8 +92,9 @@ func (c *botServiceClient) GetBot(ctx context.Context, in *GetBotRequest, opts . } func (c *botServiceClient) ListBots(ctx context.Context, in *ListBotsRequest, opts ...grpc.CallOption) (*ListBotsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListBotsResponse) - err := c.cc.Invoke(ctx, BotService_ListBots_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_ListBots_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -98,8 +102,9 @@ func (c *botServiceClient) ListBots(ctx context.Context, in *ListBotsRequest, op } func (c *botServiceClient) CreateBot(ctx context.Context, in *CreateBotRequest, opts ...grpc.CallOption) (*Bot, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Bot) - err := c.cc.Invoke(ctx, BotService_CreateBot_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_CreateBot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -107,8 +112,9 @@ func (c *botServiceClient) CreateBot(ctx context.Context, in *CreateBotRequest, } func (c *botServiceClient) UpdateBot(ctx context.Context, in *UpdateBotRequest, opts ...grpc.CallOption) (*Bot, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Bot) - err := c.cc.Invoke(ctx, BotService_UpdateBot_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_UpdateBot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -116,8 +122,9 @@ func (c *botServiceClient) UpdateBot(ctx context.Context, in *UpdateBotRequest, } func (c *botServiceClient) UpsertBot(ctx context.Context, in *UpsertBotRequest, opts ...grpc.CallOption) (*Bot, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Bot) - err := c.cc.Invoke(ctx, BotService_UpsertBot_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_UpsertBot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,8 +132,9 @@ func (c *botServiceClient) UpsertBot(ctx context.Context, in *UpsertBotRequest, } func (c *botServiceClient) DeleteBot(ctx context.Context, in *DeleteBotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, BotService_DeleteBot_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, BotService_DeleteBot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -136,6 +144,8 @@ func (c *botServiceClient) DeleteBot(ctx context.Context, in *DeleteBotRequest, // BotServiceServer is the server API for BotService service. // All implementations must embed UnimplementedBotServiceServer // for forward compatibility +// +// BotService provides methods to manage Teleport Bots type BotServiceServer interface { // GetBot is used to query a Bot resource by its name. // diff --git a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go index c804b3bd1c1be..5d600458af5c8 100644 --- a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/machineid/v1/workload_identity_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( WorkloadIdentityService_SignX509SVIDs_FullMethodName = "/teleport.machineid.v1.WorkloadIdentityService/SignX509SVIDs" @@ -39,6 +39,9 @@ const ( // WorkloadIdentityServiceClient is the client API for WorkloadIdentityService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// WorkloadIdentityService provides the signing of workload identity documents. +// It currently only supports signing SPIFFE x509 SVIDs. type WorkloadIdentityServiceClient interface { // SignX509SVIDs generates signed x509 SVIDs based on the SVIDs provided in // the request. @@ -54,8 +57,9 @@ func NewWorkloadIdentityServiceClient(cc grpc.ClientConnInterface) WorkloadIdent } func (c *workloadIdentityServiceClient) SignX509SVIDs(ctx context.Context, in *SignX509SVIDsRequest, opts ...grpc.CallOption) (*SignX509SVIDsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SignX509SVIDsResponse) - err := c.cc.Invoke(ctx, WorkloadIdentityService_SignX509SVIDs_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, WorkloadIdentityService_SignX509SVIDs_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -65,6 +69,9 @@ func (c *workloadIdentityServiceClient) SignX509SVIDs(ctx context.Context, in *S // WorkloadIdentityServiceServer is the server API for WorkloadIdentityService service. // All implementations must embed UnimplementedWorkloadIdentityServiceServer // for forward compatibility +// +// WorkloadIdentityService provides the signing of workload identity documents. +// It currently only supports signing SPIFFE x509 SVIDs. type WorkloadIdentityServiceServer interface { // SignX509SVIDs generates signed x509 SVIDs based on the SVIDs provided in // the request. diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go index a00e1de288d50..1774ed66652fe 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/notifications/v1/notifications_service.proto @@ -33,8 +33,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( NotificationService_CreateUserNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateUserNotification" @@ -49,6 +49,8 @@ const ( // NotificationServiceClient is the client API for NotificationService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// NotificationService provides CRUD operations for notifications resources. type NotificationServiceClient interface { // CreateUserNotification creates a user-specific notification. CreateUserNotification(ctx context.Context, in *CreateUserNotificationRequest, opts ...grpc.CallOption) (*Notification, error) @@ -75,8 +77,9 @@ func NewNotificationServiceClient(cc grpc.ClientConnInterface) NotificationServi } func (c *notificationServiceClient) CreateUserNotification(ctx context.Context, in *CreateUserNotificationRequest, opts ...grpc.CallOption) (*Notification, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Notification) - err := c.cc.Invoke(ctx, NotificationService_CreateUserNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_CreateUserNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -84,8 +87,9 @@ func (c *notificationServiceClient) CreateUserNotification(ctx context.Context, } func (c *notificationServiceClient) DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, NotificationService_DeleteUserNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_DeleteUserNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -93,8 +97,9 @@ func (c *notificationServiceClient) DeleteUserNotification(ctx context.Context, } func (c *notificationServiceClient) CreateGlobalNotification(ctx context.Context, in *CreateGlobalNotificationRequest, opts ...grpc.CallOption) (*GlobalNotification, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GlobalNotification) - err := c.cc.Invoke(ctx, NotificationService_CreateGlobalNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_CreateGlobalNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +107,9 @@ func (c *notificationServiceClient) CreateGlobalNotification(ctx context.Context } func (c *notificationServiceClient) DeleteGlobalNotification(ctx context.Context, in *DeleteGlobalNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, NotificationService_DeleteGlobalNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_DeleteGlobalNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -111,8 +117,9 @@ func (c *notificationServiceClient) DeleteGlobalNotification(ctx context.Context } func (c *notificationServiceClient) ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListNotificationsResponse) - err := c.cc.Invoke(ctx, NotificationService_ListNotifications_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_ListNotifications_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -120,8 +127,9 @@ func (c *notificationServiceClient) ListNotifications(ctx context.Context, in *L } func (c *notificationServiceClient) UpsertUserNotificationState(ctx context.Context, in *UpsertUserNotificationStateRequest, opts ...grpc.CallOption) (*UserNotificationState, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UserNotificationState) - err := c.cc.Invoke(ctx, NotificationService_UpsertUserNotificationState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_UpsertUserNotificationState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,8 +137,9 @@ func (c *notificationServiceClient) UpsertUserNotificationState(ctx context.Cont } func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.Context, in *UpsertUserLastSeenNotificationRequest, opts ...grpc.CallOption) (*UserLastSeenNotification, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UserLastSeenNotification) - err := c.cc.Invoke(ctx, NotificationService_UpsertUserLastSeenNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, NotificationService_UpsertUserLastSeenNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -140,6 +149,8 @@ func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.C // NotificationServiceServer is the server API for NotificationService service. // All implementations must embed UnimplementedNotificationServiceServer // for forward compatibility +// +// NotificationService provides CRUD operations for notifications resources. type NotificationServiceServer interface { // CreateUserNotification creates a user-specific notification. CreateUserNotification(context.Context, *CreateUserNotificationRequest) (*Notification, error) diff --git a/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go b/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go index 084a4646a3f05..4a3ea01a1f53e 100644 --- a/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/okta/v1/okta_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( OktaService_ListOktaImportRules_FullMethodName = "/teleport.okta.v1.OktaService/ListOktaImportRules" @@ -53,6 +53,8 @@ const ( // OktaServiceClient is the client API for OktaService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// OktaService provides CRUD methods for Okta resources. type OktaServiceClient interface { // ListOktaImportRules returns a paginated list of all Okta import rule resources. ListOktaImportRules(ctx context.Context, in *ListOktaImportRulesRequest, opts ...grpc.CallOption) (*ListOktaImportRulesResponse, error) @@ -91,8 +93,9 @@ func NewOktaServiceClient(cc grpc.ClientConnInterface) OktaServiceClient { } func (c *oktaServiceClient) ListOktaImportRules(ctx context.Context, in *ListOktaImportRulesRequest, opts ...grpc.CallOption) (*ListOktaImportRulesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListOktaImportRulesResponse) - err := c.cc.Invoke(ctx, OktaService_ListOktaImportRules_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_ListOktaImportRules_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -100,8 +103,9 @@ func (c *oktaServiceClient) ListOktaImportRules(ctx context.Context, in *ListOkt } func (c *oktaServiceClient) GetOktaImportRule(ctx context.Context, in *GetOktaImportRuleRequest, opts ...grpc.CallOption) (*types.OktaImportRuleV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaImportRuleV1) - err := c.cc.Invoke(ctx, OktaService_GetOktaImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_GetOktaImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -109,8 +113,9 @@ func (c *oktaServiceClient) GetOktaImportRule(ctx context.Context, in *GetOktaIm } func (c *oktaServiceClient) CreateOktaImportRule(ctx context.Context, in *CreateOktaImportRuleRequest, opts ...grpc.CallOption) (*types.OktaImportRuleV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaImportRuleV1) - err := c.cc.Invoke(ctx, OktaService_CreateOktaImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_CreateOktaImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -118,8 +123,9 @@ func (c *oktaServiceClient) CreateOktaImportRule(ctx context.Context, in *Create } func (c *oktaServiceClient) UpdateOktaImportRule(ctx context.Context, in *UpdateOktaImportRuleRequest, opts ...grpc.CallOption) (*types.OktaImportRuleV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaImportRuleV1) - err := c.cc.Invoke(ctx, OktaService_UpdateOktaImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_UpdateOktaImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -127,8 +133,9 @@ func (c *oktaServiceClient) UpdateOktaImportRule(ctx context.Context, in *Update } func (c *oktaServiceClient) DeleteOktaImportRule(ctx context.Context, in *DeleteOktaImportRuleRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, OktaService_DeleteOktaImportRule_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_DeleteOktaImportRule_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -136,8 +143,9 @@ func (c *oktaServiceClient) DeleteOktaImportRule(ctx context.Context, in *Delete } func (c *oktaServiceClient) DeleteAllOktaImportRules(ctx context.Context, in *DeleteAllOktaImportRulesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, OktaService_DeleteAllOktaImportRules_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_DeleteAllOktaImportRules_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -145,8 +153,9 @@ func (c *oktaServiceClient) DeleteAllOktaImportRules(ctx context.Context, in *De } func (c *oktaServiceClient) ListOktaAssignments(ctx context.Context, in *ListOktaAssignmentsRequest, opts ...grpc.CallOption) (*ListOktaAssignmentsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListOktaAssignmentsResponse) - err := c.cc.Invoke(ctx, OktaService_ListOktaAssignments_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_ListOktaAssignments_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -154,8 +163,9 @@ func (c *oktaServiceClient) ListOktaAssignments(ctx context.Context, in *ListOkt } func (c *oktaServiceClient) GetOktaAssignment(ctx context.Context, in *GetOktaAssignmentRequest, opts ...grpc.CallOption) (*types.OktaAssignmentV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaAssignmentV1) - err := c.cc.Invoke(ctx, OktaService_GetOktaAssignment_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_GetOktaAssignment_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -163,8 +173,9 @@ func (c *oktaServiceClient) GetOktaAssignment(ctx context.Context, in *GetOktaAs } func (c *oktaServiceClient) CreateOktaAssignment(ctx context.Context, in *CreateOktaAssignmentRequest, opts ...grpc.CallOption) (*types.OktaAssignmentV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaAssignmentV1) - err := c.cc.Invoke(ctx, OktaService_CreateOktaAssignment_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_CreateOktaAssignment_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -172,8 +183,9 @@ func (c *oktaServiceClient) CreateOktaAssignment(ctx context.Context, in *Create } func (c *oktaServiceClient) UpdateOktaAssignment(ctx context.Context, in *UpdateOktaAssignmentRequest, opts ...grpc.CallOption) (*types.OktaAssignmentV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.OktaAssignmentV1) - err := c.cc.Invoke(ctx, OktaService_UpdateOktaAssignment_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_UpdateOktaAssignment_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -181,8 +193,9 @@ func (c *oktaServiceClient) UpdateOktaAssignment(ctx context.Context, in *Update } func (c *oktaServiceClient) UpdateOktaAssignmentStatus(ctx context.Context, in *UpdateOktaAssignmentStatusRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, OktaService_UpdateOktaAssignmentStatus_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_UpdateOktaAssignmentStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -190,8 +203,9 @@ func (c *oktaServiceClient) UpdateOktaAssignmentStatus(ctx context.Context, in * } func (c *oktaServiceClient) DeleteOktaAssignment(ctx context.Context, in *DeleteOktaAssignmentRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, OktaService_DeleteOktaAssignment_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_DeleteOktaAssignment_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -199,8 +213,9 @@ func (c *oktaServiceClient) DeleteOktaAssignment(ctx context.Context, in *Delete } func (c *oktaServiceClient) DeleteAllOktaAssignments(ctx context.Context, in *DeleteAllOktaAssignmentsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, OktaService_DeleteAllOktaAssignments_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, OktaService_DeleteAllOktaAssignments_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -210,6 +225,8 @@ func (c *oktaServiceClient) DeleteAllOktaAssignments(ctx context.Context, in *De // OktaServiceServer is the server API for OktaService service. // All implementations must embed UnimplementedOktaServiceServer // for forward compatibility +// +// OktaService provides CRUD methods for Okta resources. type OktaServiceServer interface { // ListOktaImportRules returns a paginated list of all Okta import rule resources. ListOktaImportRules(context.Context, *ListOktaImportRulesRequest) (*ListOktaImportRulesResponse, error) diff --git a/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go b/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go index bccd115651e0b..1ea0bb21d8d4c 100644 --- a/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/plugins/v1/plugin_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( PluginService_CreatePlugin_FullMethodName = "/teleport.plugins.v1.PluginService/CreatePlugin" @@ -51,6 +51,8 @@ const ( // PluginServiceClient is the client API for PluginService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// PluginService provides CRUD operations for Plugin resources. type PluginServiceClient interface { // CreatePlugin creates a new plugin instance. CreatePlugin(ctx context.Context, in *CreatePluginRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) @@ -89,8 +91,9 @@ func NewPluginServiceClient(cc grpc.ClientConnInterface) PluginServiceClient { } func (c *pluginServiceClient) CreatePlugin(ctx context.Context, in *CreatePluginRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PluginService_CreatePlugin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_CreatePlugin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -98,8 +101,9 @@ func (c *pluginServiceClient) CreatePlugin(ctx context.Context, in *CreatePlugin } func (c *pluginServiceClient) GetPlugin(ctx context.Context, in *GetPluginRequest, opts ...grpc.CallOption) (*types.PluginV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.PluginV1) - err := c.cc.Invoke(ctx, PluginService_GetPlugin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_GetPlugin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -107,8 +111,9 @@ func (c *pluginServiceClient) GetPlugin(ctx context.Context, in *GetPluginReques } func (c *pluginServiceClient) UpdatePlugin(ctx context.Context, in *UpdatePluginRequest, opts ...grpc.CallOption) (*types.PluginV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.PluginV1) - err := c.cc.Invoke(ctx, PluginService_UpdatePlugin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_UpdatePlugin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -116,8 +121,9 @@ func (c *pluginServiceClient) UpdatePlugin(ctx context.Context, in *UpdatePlugin } func (c *pluginServiceClient) DeletePlugin(ctx context.Context, in *DeletePluginRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PluginService_DeletePlugin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_DeletePlugin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,8 +131,9 @@ func (c *pluginServiceClient) DeletePlugin(ctx context.Context, in *DeletePlugin } func (c *pluginServiceClient) ListPlugins(ctx context.Context, in *ListPluginsRequest, opts ...grpc.CallOption) (*ListPluginsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListPluginsResponse) - err := c.cc.Invoke(ctx, PluginService_ListPlugins_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_ListPlugins_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -134,8 +141,9 @@ func (c *pluginServiceClient) ListPlugins(ctx context.Context, in *ListPluginsRe } func (c *pluginServiceClient) SetPluginCredentials(ctx context.Context, in *SetPluginCredentialsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PluginService_SetPluginCredentials_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_SetPluginCredentials_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -143,8 +151,9 @@ func (c *pluginServiceClient) SetPluginCredentials(ctx context.Context, in *SetP } func (c *pluginServiceClient) SetPluginStatus(ctx context.Context, in *SetPluginStatusRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PluginService_SetPluginStatus_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_SetPluginStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -152,8 +161,9 @@ func (c *pluginServiceClient) SetPluginStatus(ctx context.Context, in *SetPlugin } func (c *pluginServiceClient) GetAvailablePluginTypes(ctx context.Context, in *GetAvailablePluginTypesRequest, opts ...grpc.CallOption) (*GetAvailablePluginTypesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAvailablePluginTypesResponse) - err := c.cc.Invoke(ctx, PluginService_GetAvailablePluginTypes_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_GetAvailablePluginTypes_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -161,8 +171,9 @@ func (c *pluginServiceClient) GetAvailablePluginTypes(ctx context.Context, in *G } func (c *pluginServiceClient) SearchPluginStaticCredentials(ctx context.Context, in *SearchPluginStaticCredentialsRequest, opts ...grpc.CallOption) (*SearchPluginStaticCredentialsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SearchPluginStaticCredentialsResponse) - err := c.cc.Invoke(ctx, PluginService_SearchPluginStaticCredentials_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_SearchPluginStaticCredentials_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -170,8 +181,9 @@ func (c *pluginServiceClient) SearchPluginStaticCredentials(ctx context.Context, } func (c *pluginServiceClient) NeedsCleanup(ctx context.Context, in *NeedsCleanupRequest, opts ...grpc.CallOption) (*NeedsCleanupResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(NeedsCleanupResponse) - err := c.cc.Invoke(ctx, PluginService_NeedsCleanup_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_NeedsCleanup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -179,8 +191,9 @@ func (c *pluginServiceClient) NeedsCleanup(ctx context.Context, in *NeedsCleanup } func (c *pluginServiceClient) Cleanup(ctx context.Context, in *CleanupRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PluginService_Cleanup_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PluginService_Cleanup_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -190,6 +203,8 @@ func (c *pluginServiceClient) Cleanup(ctx context.Context, in *CleanupRequest, o // PluginServiceServer is the server API for PluginService service. // All implementations must embed UnimplementedPluginServiceServer // for forward compatibility +// +// PluginService provides CRUD operations for Plugin resources. type PluginServiceServer interface { // CreatePlugin creates a new plugin instance. CreatePlugin(context.Context, *CreatePluginRequest) (*emptypb.Empty, error) diff --git a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go index 0d2ed5d027f5d..973054bb808c7 100644 --- a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go +++ b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/presence/v1/service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( PresenceService_GetRemoteCluster_FullMethodName = "/teleport.presence.v1.PresenceService/GetRemoteCluster" @@ -44,6 +44,8 @@ const ( // PresenceServiceClient is the client API for PresenceService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// PresenceService provides methods to manage presence of RemoteClusters type PresenceServiceClient interface { // GetRemoteCluster retrieves a RemoteCluster by name. GetRemoteCluster(ctx context.Context, in *GetRemoteClusterRequest, opts ...grpc.CallOption) (*types.RemoteClusterV3, error) @@ -64,8 +66,9 @@ func NewPresenceServiceClient(cc grpc.ClientConnInterface) PresenceServiceClient } func (c *presenceServiceClient) GetRemoteCluster(ctx context.Context, in *GetRemoteClusterRequest, opts ...grpc.CallOption) (*types.RemoteClusterV3, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.RemoteClusterV3) - err := c.cc.Invoke(ctx, PresenceService_GetRemoteCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PresenceService_GetRemoteCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -73,8 +76,9 @@ func (c *presenceServiceClient) GetRemoteCluster(ctx context.Context, in *GetRem } func (c *presenceServiceClient) ListRemoteClusters(ctx context.Context, in *ListRemoteClustersRequest, opts ...grpc.CallOption) (*ListRemoteClustersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListRemoteClustersResponse) - err := c.cc.Invoke(ctx, PresenceService_ListRemoteClusters_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PresenceService_ListRemoteClusters_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -82,8 +86,9 @@ func (c *presenceServiceClient) ListRemoteClusters(ctx context.Context, in *List } func (c *presenceServiceClient) UpdateRemoteCluster(ctx context.Context, in *UpdateRemoteClusterRequest, opts ...grpc.CallOption) (*types.RemoteClusterV3, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.RemoteClusterV3) - err := c.cc.Invoke(ctx, PresenceService_UpdateRemoteCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PresenceService_UpdateRemoteCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -91,8 +96,9 @@ func (c *presenceServiceClient) UpdateRemoteCluster(ctx context.Context, in *Upd } func (c *presenceServiceClient) DeleteRemoteCluster(ctx context.Context, in *DeleteRemoteClusterRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, PresenceService_DeleteRemoteCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, PresenceService_DeleteRemoteCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,6 +108,8 @@ func (c *presenceServiceClient) DeleteRemoteCluster(ctx context.Context, in *Del // PresenceServiceServer is the server API for PresenceService service. // All implementations must embed UnimplementedPresenceServiceServer // for forward compatibility +// +// PresenceService provides methods to manage presence of RemoteClusters type PresenceServiceServer interface { // GetRemoteCluster retrieves a RemoteCluster by name. GetRemoteCluster(context.Context, *GetRemoteClusterRequest) (*types.RemoteClusterV3, error) diff --git a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go index 2295ea96e1f96..c82b4821e7e16 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/resourceusage/v1/resourceusage_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( ResourceUsageService_GetUsage_FullMethodName = "/teleport.resourceusage.v1.ResourceUsageService/GetUsage" @@ -39,6 +39,8 @@ const ( // ResourceUsageServiceClient is the client API for ResourceUsageService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// ResourceUsageService is a service to fetch information about the usage of limited resources on usage-billed plans. type ResourceUsageServiceClient interface { // GetUsage returns usage information for all limited resources. GetUsage(ctx context.Context, in *GetUsageRequest, opts ...grpc.CallOption) (*GetUsageResponse, error) @@ -53,8 +55,9 @@ func NewResourceUsageServiceClient(cc grpc.ClientConnInterface) ResourceUsageSer } func (c *resourceUsageServiceClient) GetUsage(ctx context.Context, in *GetUsageRequest, opts ...grpc.CallOption) (*GetUsageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUsageResponse) - err := c.cc.Invoke(ctx, ResourceUsageService_GetUsage_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, ResourceUsageService_GetUsage_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -64,6 +67,8 @@ func (c *resourceUsageServiceClient) GetUsage(ctx context.Context, in *GetUsageR // ResourceUsageServiceServer is the server API for ResourceUsageService service. // All implementations must embed UnimplementedResourceUsageServiceServer // for forward compatibility +// +// ResourceUsageService is a service to fetch information about the usage of limited resources on usage-billed plans. type ResourceUsageServiceServer interface { // GetUsage returns usage information for all limited resources. GetUsage(context.Context, *GetUsageRequest) (*GetUsageResponse, error) diff --git a/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go b/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go index 136f54383c858..41f6c31477cac 100644 --- a/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go +++ b/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/samlidp/v1/samlidp.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( SAMLIdPService_ProcessSAMLIdPRequest_FullMethodName = "/teleport.samlidp.v1.SAMLIdPService/ProcessSAMLIdPRequest" @@ -40,6 +40,8 @@ const ( // SAMLIdPServiceClient is the client API for SAMLIdPService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// SAMLIdPService provides utility methods for the SAML identity provider. type SAMLIdPServiceClient interface { // ProcessSAMLIdPRequest processes the SAML auth request. ProcessSAMLIdPRequest(ctx context.Context, in *ProcessSAMLIdPRequestRequest, opts ...grpc.CallOption) (*ProcessSAMLIdPRequestResponse, error) @@ -56,8 +58,9 @@ func NewSAMLIdPServiceClient(cc grpc.ClientConnInterface) SAMLIdPServiceClient { } func (c *sAMLIdPServiceClient) ProcessSAMLIdPRequest(ctx context.Context, in *ProcessSAMLIdPRequestRequest, opts ...grpc.CallOption) (*ProcessSAMLIdPRequestResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ProcessSAMLIdPRequestResponse) - err := c.cc.Invoke(ctx, SAMLIdPService_ProcessSAMLIdPRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SAMLIdPService_ProcessSAMLIdPRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -65,8 +68,9 @@ func (c *sAMLIdPServiceClient) ProcessSAMLIdPRequest(ctx context.Context, in *Pr } func (c *sAMLIdPServiceClient) TestSAMLIdPAttributeMapping(ctx context.Context, in *TestSAMLIdPAttributeMappingRequest, opts ...grpc.CallOption) (*TestSAMLIdPAttributeMappingResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TestSAMLIdPAttributeMappingResponse) - err := c.cc.Invoke(ctx, SAMLIdPService_TestSAMLIdPAttributeMapping_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SAMLIdPService_TestSAMLIdPAttributeMapping_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -76,6 +80,8 @@ func (c *sAMLIdPServiceClient) TestSAMLIdPAttributeMapping(ctx context.Context, // SAMLIdPServiceServer is the server API for SAMLIdPService service. // All implementations must embed UnimplementedSAMLIdPServiceServer // for forward compatibility +// +// SAMLIdPService provides utility methods for the SAML identity provider. type SAMLIdPServiceServer interface { // ProcessSAMLIdPRequest processes the SAML auth request. ProcessSAMLIdPRequest(context.Context, *ProcessSAMLIdPRequestRequest) (*ProcessSAMLIdPRequestResponse, error) diff --git a/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go b/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go index 0d80105b0ae8e..3f0a50f31614f 100644 --- a/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/scim/v1/scim_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( SCIMService_ListSCIMResources_FullMethodName = "/teleport.scim.v1.SCIMService/ListSCIMResources" @@ -44,6 +44,8 @@ const ( // SCIMServiceClient is the client API for SCIMService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// SCIMService implements a SCIM gateway for external IDPs for user provisioning type SCIMServiceClient interface { // List fetches all (or a subset of all) resources resources of a given type ListSCIMResources(ctx context.Context, in *ListSCIMResourcesRequest, opts ...grpc.CallOption) (*ResourceList, error) @@ -68,8 +70,9 @@ func NewSCIMServiceClient(cc grpc.ClientConnInterface) SCIMServiceClient { } func (c *sCIMServiceClient) ListSCIMResources(ctx context.Context, in *ListSCIMResourcesRequest, opts ...grpc.CallOption) (*ResourceList, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ResourceList) - err := c.cc.Invoke(ctx, SCIMService_ListSCIMResources_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SCIMService_ListSCIMResources_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -77,8 +80,9 @@ func (c *sCIMServiceClient) ListSCIMResources(ctx context.Context, in *ListSCIMR } func (c *sCIMServiceClient) GetSCIMResource(ctx context.Context, in *GetSCIMResourceRequest, opts ...grpc.CallOption) (*Resource, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Resource) - err := c.cc.Invoke(ctx, SCIMService_GetSCIMResource_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SCIMService_GetSCIMResource_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -86,8 +90,9 @@ func (c *sCIMServiceClient) GetSCIMResource(ctx context.Context, in *GetSCIMReso } func (c *sCIMServiceClient) CreateSCIMResource(ctx context.Context, in *CreateSCIMResourceRequest, opts ...grpc.CallOption) (*Resource, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Resource) - err := c.cc.Invoke(ctx, SCIMService_CreateSCIMResource_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SCIMService_CreateSCIMResource_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -95,8 +100,9 @@ func (c *sCIMServiceClient) CreateSCIMResource(ctx context.Context, in *CreateSC } func (c *sCIMServiceClient) UpdateSCIMResource(ctx context.Context, in *UpdateSCIMResourceRequest, opts ...grpc.CallOption) (*Resource, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Resource) - err := c.cc.Invoke(ctx, SCIMService_UpdateSCIMResource_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SCIMService_UpdateSCIMResource_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -104,8 +110,9 @@ func (c *sCIMServiceClient) UpdateSCIMResource(ctx context.Context, in *UpdateSC } func (c *sCIMServiceClient) DeleteSCIMResource(ctx context.Context, in *DeleteSCIMResourceRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SCIMService_DeleteSCIMResource_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SCIMService_DeleteSCIMResource_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -115,6 +122,8 @@ func (c *sCIMServiceClient) DeleteSCIMResource(ctx context.Context, in *DeleteSC // SCIMServiceServer is the server API for SCIMService service. // All implementations must embed UnimplementedSCIMServiceServer // for forward compatibility +// +// SCIMService implements a SCIM gateway for external IDPs for user provisioning type SCIMServiceServer interface { // List fetches all (or a subset of all) resources resources of a given type ListSCIMResources(context.Context, *ListSCIMResourcesRequest) (*ResourceList, error) diff --git a/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go b/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go index 79a438a08ced6..bc25d091ee6f6 100644 --- a/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/secreports/v1/secreports_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( SecReportsService_UpsertAuditQuery_FullMethodName = "/teleport.secreports.v1.SecReportsService/UpsertAuditQuery" @@ -53,6 +53,8 @@ const ( // SecReportsServiceClient is the client API for SecReportsService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// SecReportsService is a service that manages security reports. type SecReportsServiceClient interface { // UpsertAuditQuery upsets an audit query. UpsertAuditQuery(ctx context.Context, in *UpsertAuditQueryRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) @@ -93,8 +95,9 @@ func NewSecReportsServiceClient(cc grpc.ClientConnInterface) SecReportsServiceCl } func (c *secReportsServiceClient) UpsertAuditQuery(ctx context.Context, in *UpsertAuditQueryRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SecReportsService_UpsertAuditQuery_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_UpsertAuditQuery_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +105,9 @@ func (c *secReportsServiceClient) UpsertAuditQuery(ctx context.Context, in *Upse } func (c *secReportsServiceClient) GetAuditQuery(ctx context.Context, in *GetAuditQueryRequest, opts ...grpc.CallOption) (*AuditQuery, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AuditQuery) - err := c.cc.Invoke(ctx, SecReportsService_GetAuditQuery_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetAuditQuery_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -111,8 +115,9 @@ func (c *secReportsServiceClient) GetAuditQuery(ctx context.Context, in *GetAudi } func (c *secReportsServiceClient) ListAuditQueries(ctx context.Context, in *ListAuditQueriesRequest, opts ...grpc.CallOption) (*ListAuditQueriesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListAuditQueriesResponse) - err := c.cc.Invoke(ctx, SecReportsService_ListAuditQueries_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_ListAuditQueries_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -120,8 +125,9 @@ func (c *secReportsServiceClient) ListAuditQueries(ctx context.Context, in *List } func (c *secReportsServiceClient) DeleteAuditQuery(ctx context.Context, in *DeleteAuditQueryRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SecReportsService_DeleteAuditQuery_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_DeleteAuditQuery_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -129,8 +135,9 @@ func (c *secReportsServiceClient) DeleteAuditQuery(ctx context.Context, in *Dele } func (c *secReportsServiceClient) UpsertReport(ctx context.Context, in *UpsertReportRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SecReportsService_UpsertReport_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_UpsertReport_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -138,8 +145,9 @@ func (c *secReportsServiceClient) UpsertReport(ctx context.Context, in *UpsertRe } func (c *secReportsServiceClient) GetReport(ctx context.Context, in *GetReportRequest, opts ...grpc.CallOption) (*Report, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Report) - err := c.cc.Invoke(ctx, SecReportsService_GetReport_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetReport_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -147,8 +155,9 @@ func (c *secReportsServiceClient) GetReport(ctx context.Context, in *GetReportRe } func (c *secReportsServiceClient) ListReports(ctx context.Context, in *ListReportsRequest, opts ...grpc.CallOption) (*ListReportsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListReportsResponse) - err := c.cc.Invoke(ctx, SecReportsService_ListReports_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_ListReports_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -156,8 +165,9 @@ func (c *secReportsServiceClient) ListReports(ctx context.Context, in *ListRepor } func (c *secReportsServiceClient) DeleteReport(ctx context.Context, in *DeleteReportRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SecReportsService_DeleteReport_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_DeleteReport_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -165,8 +175,9 @@ func (c *secReportsServiceClient) DeleteReport(ctx context.Context, in *DeleteRe } func (c *secReportsServiceClient) RunAuditQuery(ctx context.Context, in *RunAuditQueryRequest, opts ...grpc.CallOption) (*RunAuditQueryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RunAuditQueryResponse) - err := c.cc.Invoke(ctx, SecReportsService_RunAuditQuery_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_RunAuditQuery_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -174,8 +185,9 @@ func (c *secReportsServiceClient) RunAuditQuery(ctx context.Context, in *RunAudi } func (c *secReportsServiceClient) GetAuditQueryResult(ctx context.Context, in *GetAuditQueryResultRequest, opts ...grpc.CallOption) (*GetAuditQueryResultResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAuditQueryResultResponse) - err := c.cc.Invoke(ctx, SecReportsService_GetAuditQueryResult_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetAuditQueryResult_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -183,8 +195,9 @@ func (c *secReportsServiceClient) GetAuditQueryResult(ctx context.Context, in *G } func (c *secReportsServiceClient) RunReport(ctx context.Context, in *RunReportRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SecReportsService_RunReport_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_RunReport_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -192,8 +205,9 @@ func (c *secReportsServiceClient) RunReport(ctx context.Context, in *RunReportRe } func (c *secReportsServiceClient) GetReportResult(ctx context.Context, in *GetReportResultRequest, opts ...grpc.CallOption) (*GetReportResultResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetReportResultResponse) - err := c.cc.Invoke(ctx, SecReportsService_GetReportResult_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetReportResult_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -201,8 +215,9 @@ func (c *secReportsServiceClient) GetReportResult(ctx context.Context, in *GetRe } func (c *secReportsServiceClient) GetReportState(ctx context.Context, in *GetReportStateRequest, opts ...grpc.CallOption) (*ReportState, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReportState) - err := c.cc.Invoke(ctx, SecReportsService_GetReportState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetReportState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -210,8 +225,9 @@ func (c *secReportsServiceClient) GetReportState(ctx context.Context, in *GetRep } func (c *secReportsServiceClient) GetSchema(ctx context.Context, in *GetSchemaRequest, opts ...grpc.CallOption) (*GetSchemaResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetSchemaResponse) - err := c.cc.Invoke(ctx, SecReportsService_GetSchema_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, SecReportsService_GetSchema_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -221,6 +237,8 @@ func (c *secReportsServiceClient) GetSchema(ctx context.Context, in *GetSchemaRe // SecReportsServiceServer is the server API for SecReportsService service. // All implementations must embed UnimplementedSecReportsServiceServer // for forward compatibility +// +// SecReportsService is a service that manages security reports. type SecReportsServiceServer interface { // UpsertAuditQuery upsets an audit query. UpsertAuditQuery(context.Context, *UpsertAuditQueryRequest) (*emptypb.Empty, error) diff --git a/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go b/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go index 3112cd8d28657..5f4dae9862ac8 100644 --- a/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/transport/v1/transport_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TransportService_GetClusterDetails_FullMethodName = "/teleport.transport.v1.TransportService/GetClusterDetails" @@ -41,6 +41,13 @@ const ( // TransportServiceClient is the client API for TransportService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// TransportService provides methods to proxy connections to various Teleport instances. +// +// All connections are operate on top of a bidirectional stream which transports +// raw payloads from higher level protocols (i.e. SSH). Clients must send an initial +// message on the stream to set up the connections accordingly. After the initial +// request either side may freely send data in any order until the stream is terminated. type TransportServiceClient interface { // GetClusterDetails provides cluster information that may affect how transport // should occur. @@ -68,8 +75,9 @@ func NewTransportServiceClient(cc grpc.ClientConnInterface) TransportServiceClie } func (c *transportServiceClient) GetClusterDetails(ctx context.Context, in *GetClusterDetailsRequest, opts ...grpc.CallOption) (*GetClusterDetailsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetClusterDetailsResponse) - err := c.cc.Invoke(ctx, TransportService_GetClusterDetails_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TransportService_GetClusterDetails_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -77,11 +85,12 @@ func (c *transportServiceClient) GetClusterDetails(ctx context.Context, in *GetC } func (c *transportServiceClient) ProxySSH(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxySSHClient, error) { - stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[0], TransportService_ProxySSH_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[0], TransportService_ProxySSH_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transportServiceProxySSHClient{stream} + x := &transportServiceProxySSHClient{ClientStream: stream} return x, nil } @@ -108,11 +117,12 @@ func (x *transportServiceProxySSHClient) Recv() (*ProxySSHResponse, error) { } func (c *transportServiceClient) ProxyCluster(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxyClusterClient, error) { - stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[1], TransportService_ProxyCluster_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[1], TransportService_ProxyCluster_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transportServiceProxyClusterClient{stream} + x := &transportServiceProxyClusterClient{ClientStream: stream} return x, nil } @@ -141,6 +151,13 @@ func (x *transportServiceProxyClusterClient) Recv() (*ProxyClusterResponse, erro // TransportServiceServer is the server API for TransportService service. // All implementations must embed UnimplementedTransportServiceServer // for forward compatibility +// +// TransportService provides methods to proxy connections to various Teleport instances. +// +// All connections are operate on top of a bidirectional stream which transports +// raw payloads from higher level protocols (i.e. SSH). Clients must send an initial +// message on the stream to set up the connections accordingly. After the initial +// request either side may freely send data in any order until the stream is terminated. type TransportServiceServer interface { // GetClusterDetails provides cluster information that may affect how transport // should occur. @@ -205,7 +222,7 @@ func _TransportService_GetClusterDetails_Handler(srv interface{}, ctx context.Co } func _TransportService_ProxySSH_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TransportServiceServer).ProxySSH(&transportServiceProxySSHServer{stream}) + return srv.(TransportServiceServer).ProxySSH(&transportServiceProxySSHServer{ServerStream: stream}) } type TransportService_ProxySSHServer interface { @@ -231,7 +248,7 @@ func (x *transportServiceProxySSHServer) Recv() (*ProxySSHRequest, error) { } func _TransportService_ProxyCluster_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TransportServiceServer).ProxyCluster(&transportServiceProxyClusterServer{stream}) + return srv.(TransportServiceServer).ProxyCluster(&transportServiceProxyClusterServer{ServerStream: stream}) } type TransportService_ProxyClusterServer interface { diff --git a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go index 1c30771b89e6f..3915be697442e 100644 --- a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/trust/v1/trust_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TrustService_GetCertAuthority_FullMethodName = "/teleport.trust.v1.TrustService/GetCertAuthority" @@ -47,6 +47,8 @@ const ( // TrustServiceClient is the client API for TrustService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// TrustService provides methods to manage certificate authorities. type TrustServiceClient interface { // GetCertAuthority returns a cert authority by type and domain. GetCertAuthority(ctx context.Context, in *GetCertAuthorityRequest, opts ...grpc.CallOption) (*types.CertAuthorityV2, error) @@ -74,8 +76,9 @@ func NewTrustServiceClient(cc grpc.ClientConnInterface) TrustServiceClient { } func (c *trustServiceClient) GetCertAuthority(ctx context.Context, in *GetCertAuthorityRequest, opts ...grpc.CallOption) (*types.CertAuthorityV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.CertAuthorityV2) - err := c.cc.Invoke(ctx, TrustService_GetCertAuthority_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_GetCertAuthority_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -83,8 +86,9 @@ func (c *trustServiceClient) GetCertAuthority(ctx context.Context, in *GetCertAu } func (c *trustServiceClient) GetCertAuthorities(ctx context.Context, in *GetCertAuthoritiesRequest, opts ...grpc.CallOption) (*GetCertAuthoritiesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetCertAuthoritiesResponse) - err := c.cc.Invoke(ctx, TrustService_GetCertAuthorities_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_GetCertAuthorities_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -92,8 +96,9 @@ func (c *trustServiceClient) GetCertAuthorities(ctx context.Context, in *GetCert } func (c *trustServiceClient) DeleteCertAuthority(ctx context.Context, in *DeleteCertAuthorityRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, TrustService_DeleteCertAuthority_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_DeleteCertAuthority_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -101,8 +106,9 @@ func (c *trustServiceClient) DeleteCertAuthority(ctx context.Context, in *Delete } func (c *trustServiceClient) UpsertCertAuthority(ctx context.Context, in *UpsertCertAuthorityRequest, opts ...grpc.CallOption) (*types.CertAuthorityV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(types.CertAuthorityV2) - err := c.cc.Invoke(ctx, TrustService_UpsertCertAuthority_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_UpsertCertAuthority_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -110,8 +116,9 @@ func (c *trustServiceClient) UpsertCertAuthority(ctx context.Context, in *Upsert } func (c *trustServiceClient) RotateCertAuthority(ctx context.Context, in *RotateCertAuthorityRequest, opts ...grpc.CallOption) (*RotateCertAuthorityResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RotateCertAuthorityResponse) - err := c.cc.Invoke(ctx, TrustService_RotateCertAuthority_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_RotateCertAuthority_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -119,8 +126,9 @@ func (c *trustServiceClient) RotateCertAuthority(ctx context.Context, in *Rotate } func (c *trustServiceClient) RotateExternalCertAuthority(ctx context.Context, in *RotateExternalCertAuthorityRequest, opts ...grpc.CallOption) (*RotateExternalCertAuthorityResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RotateExternalCertAuthorityResponse) - err := c.cc.Invoke(ctx, TrustService_RotateExternalCertAuthority_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_RotateExternalCertAuthority_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -128,8 +136,9 @@ func (c *trustServiceClient) RotateExternalCertAuthority(ctx context.Context, in } func (c *trustServiceClient) GenerateHostCert(ctx context.Context, in *GenerateHostCertRequest, opts ...grpc.CallOption) (*GenerateHostCertResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GenerateHostCertResponse) - err := c.cc.Invoke(ctx, TrustService_GenerateHostCert_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TrustService_GenerateHostCert_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -139,6 +148,8 @@ func (c *trustServiceClient) GenerateHostCert(ctx context.Context, in *GenerateH // TrustServiceServer is the server API for TrustService service. // All implementations must embed UnimplementedTrustServiceServer // for forward compatibility +// +// TrustService provides methods to manage certificate authorities. type TrustServiceServer interface { // GetCertAuthority returns a cert authority by type and domain. GetCertAuthority(context.Context, *GetCertAuthorityRequest) (*types.CertAuthorityV2, error) diff --git a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go index 1560d46e51646..521a09fdfc699 100644 --- a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/userloginstate/v1/userloginstate_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( UserLoginStateService_GetUserLoginStates_FullMethodName = "/teleport.userloginstate.v1.UserLoginStateService/GetUserLoginStates" @@ -44,6 +44,8 @@ const ( // UserLoginStateServiceClient is the client API for UserLoginStateService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// UserLoginStateService provides CRUD methods for user login state resources. type UserLoginStateServiceClient interface { // GetUserLoginStates returns a list of all user login states. GetUserLoginStates(ctx context.Context, in *GetUserLoginStatesRequest, opts ...grpc.CallOption) (*GetUserLoginStatesResponse, error) @@ -66,8 +68,9 @@ func NewUserLoginStateServiceClient(cc grpc.ClientConnInterface) UserLoginStateS } func (c *userLoginStateServiceClient) GetUserLoginStates(ctx context.Context, in *GetUserLoginStatesRequest, opts ...grpc.CallOption) (*GetUserLoginStatesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUserLoginStatesResponse) - err := c.cc.Invoke(ctx, UserLoginStateService_GetUserLoginStates_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserLoginStateService_GetUserLoginStates_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -75,8 +78,9 @@ func (c *userLoginStateServiceClient) GetUserLoginStates(ctx context.Context, in } func (c *userLoginStateServiceClient) GetUserLoginState(ctx context.Context, in *GetUserLoginStateRequest, opts ...grpc.CallOption) (*UserLoginState, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UserLoginState) - err := c.cc.Invoke(ctx, UserLoginStateService_GetUserLoginState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserLoginStateService_GetUserLoginState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -84,8 +88,9 @@ func (c *userLoginStateServiceClient) GetUserLoginState(ctx context.Context, in } func (c *userLoginStateServiceClient) UpsertUserLoginState(ctx context.Context, in *UpsertUserLoginStateRequest, opts ...grpc.CallOption) (*UserLoginState, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UserLoginState) - err := c.cc.Invoke(ctx, UserLoginStateService_UpsertUserLoginState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserLoginStateService_UpsertUserLoginState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -93,8 +98,9 @@ func (c *userLoginStateServiceClient) UpsertUserLoginState(ctx context.Context, } func (c *userLoginStateServiceClient) DeleteUserLoginState(ctx context.Context, in *DeleteUserLoginStateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, UserLoginStateService_DeleteUserLoginState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserLoginStateService_DeleteUserLoginState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +108,9 @@ func (c *userLoginStateServiceClient) DeleteUserLoginState(ctx context.Context, } func (c *userLoginStateServiceClient) DeleteAllUserLoginStates(ctx context.Context, in *DeleteAllUserLoginStatesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, UserLoginStateService_DeleteAllUserLoginStates_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserLoginStateService_DeleteAllUserLoginStates_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -113,6 +120,8 @@ func (c *userLoginStateServiceClient) DeleteAllUserLoginStates(ctx context.Conte // UserLoginStateServiceServer is the server API for UserLoginStateService service. // All implementations must embed UnimplementedUserLoginStateServiceServer // for forward compatibility +// +// UserLoginStateService provides CRUD methods for user login state resources. type UserLoginStateServiceServer interface { // GetUserLoginStates returns a list of all user login states. GetUserLoginStates(context.Context, *GetUserLoginStatesRequest) (*GetUserLoginStatesResponse, error) diff --git a/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go b/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go index 320821e2151b6..271665bfecaa4 100644 --- a/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/users/v1/users_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( UsersService_GetUser_FullMethodName = "/teleport.users.v1.UsersService/GetUser" @@ -45,6 +45,8 @@ const ( // UsersServiceClient is the client API for UsersService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// UsersService provides methods to manage Teleport users. type UsersServiceClient interface { // GetUser retrieves a user by name or looks up the current user if requested. GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) @@ -69,8 +71,9 @@ func NewUsersServiceClient(cc grpc.ClientConnInterface) UsersServiceClient { } func (c *usersServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUserResponse) - err := c.cc.Invoke(ctx, UsersService_GetUser_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_GetUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -78,8 +81,9 @@ func (c *usersServiceClient) GetUser(ctx context.Context, in *GetUserRequest, op } func (c *usersServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListUsersResponse) - err := c.cc.Invoke(ctx, UsersService_ListUsers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_ListUsers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -87,8 +91,9 @@ func (c *usersServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest } func (c *usersServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateUserResponse) - err := c.cc.Invoke(ctx, UsersService_CreateUser_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_CreateUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -96,8 +101,9 @@ func (c *usersServiceClient) CreateUser(ctx context.Context, in *CreateUserReque } func (c *usersServiceClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpdateUserResponse) - err := c.cc.Invoke(ctx, UsersService_UpdateUser_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_UpdateUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -105,8 +111,9 @@ func (c *usersServiceClient) UpdateUser(ctx context.Context, in *UpdateUserReque } func (c *usersServiceClient) UpsertUser(ctx context.Context, in *UpsertUserRequest, opts ...grpc.CallOption) (*UpsertUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpsertUserResponse) - err := c.cc.Invoke(ctx, UsersService_UpsertUser_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_UpsertUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -114,8 +121,9 @@ func (c *usersServiceClient) UpsertUser(ctx context.Context, in *UpsertUserReque } func (c *usersServiceClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, UsersService_DeleteUser_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UsersService_DeleteUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -125,6 +133,8 @@ func (c *usersServiceClient) DeleteUser(ctx context.Context, in *DeleteUserReque // UsersServiceServer is the server API for UsersService service. // All implementations must embed UnimplementedUsersServiceServer // for forward compatibility +// +// UsersService provides methods to manage Teleport users. type UsersServiceServer interface { // GetUser retrieves a user by name or looks up the current user if requested. GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) diff --git a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go index f5e768ac7eb90..e17d0a3d1d21f 100644 --- a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/vnet/v1/vnet_config_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( VnetConfigService_GetVnetConfig_FullMethodName = "/teleport.vnet.v1.VnetConfigService/GetVnetConfig" @@ -44,6 +44,8 @@ const ( // VnetConfigServiceClient is the client API for VnetConfigService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// VnetConfigService provides an API to manage the singleton VnetConfig. type VnetConfigServiceClient interface { // GetVnetConfig returns the specified VnetConfig. GetVnetConfig(ctx context.Context, in *GetVnetConfigRequest, opts ...grpc.CallOption) (*VnetConfig, error) @@ -66,8 +68,9 @@ func NewVnetConfigServiceClient(cc grpc.ClientConnInterface) VnetConfigServiceCl } func (c *vnetConfigServiceClient) GetVnetConfig(ctx context.Context, in *GetVnetConfigRequest, opts ...grpc.CallOption) (*VnetConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(VnetConfig) - err := c.cc.Invoke(ctx, VnetConfigService_GetVnetConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetConfigService_GetVnetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -75,8 +78,9 @@ func (c *vnetConfigServiceClient) GetVnetConfig(ctx context.Context, in *GetVnet } func (c *vnetConfigServiceClient) CreateVnetConfig(ctx context.Context, in *CreateVnetConfigRequest, opts ...grpc.CallOption) (*VnetConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(VnetConfig) - err := c.cc.Invoke(ctx, VnetConfigService_CreateVnetConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetConfigService_CreateVnetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -84,8 +88,9 @@ func (c *vnetConfigServiceClient) CreateVnetConfig(ctx context.Context, in *Crea } func (c *vnetConfigServiceClient) UpdateVnetConfig(ctx context.Context, in *UpdateVnetConfigRequest, opts ...grpc.CallOption) (*VnetConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(VnetConfig) - err := c.cc.Invoke(ctx, VnetConfigService_UpdateVnetConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetConfigService_UpdateVnetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -93,8 +98,9 @@ func (c *vnetConfigServiceClient) UpdateVnetConfig(ctx context.Context, in *Upda } func (c *vnetConfigServiceClient) UpsertVnetConfig(ctx context.Context, in *UpsertVnetConfigRequest, opts ...grpc.CallOption) (*VnetConfig, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(VnetConfig) - err := c.cc.Invoke(ctx, VnetConfigService_UpsertVnetConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetConfigService_UpsertVnetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -102,8 +108,9 @@ func (c *vnetConfigServiceClient) UpsertVnetConfig(ctx context.Context, in *Upse } func (c *vnetConfigServiceClient) DeleteVnetConfig(ctx context.Context, in *DeleteVnetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, VnetConfigService_DeleteVnetConfig_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetConfigService_DeleteVnetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -113,6 +120,8 @@ func (c *vnetConfigServiceClient) DeleteVnetConfig(ctx context.Context, in *Dele // VnetConfigServiceServer is the server API for VnetConfigService service. // All implementations must embed UnimplementedVnetConfigServiceServer // for forward compatibility +// +// VnetConfigService provides an API to manage the singleton VnetConfig. type VnetConfigServiceServer interface { // GetVnetConfig returns the specified VnetConfig. GetVnetConfig(context.Context, *GetVnetConfigRequest) (*VnetConfig, error) diff --git a/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go b/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go index 47fd6c6475447..c7163c0936509 100644 --- a/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go +++ b/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/userpreferences/v1/userpreferences.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( UserPreferencesService_GetUserPreferences_FullMethodName = "/teleport.userpreferences.v1.UserPreferencesService/GetUserPreferences" @@ -41,6 +41,8 @@ const ( // UserPreferencesServiceClient is the client API for UserPreferencesService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// UserPreferencesService is a service that stores user settings. type UserPreferencesServiceClient interface { // GetUserPreferences returns the user preferences for a given user. GetUserPreferences(ctx context.Context, in *GetUserPreferencesRequest, opts ...grpc.CallOption) (*GetUserPreferencesResponse, error) @@ -57,8 +59,9 @@ func NewUserPreferencesServiceClient(cc grpc.ClientConnInterface) UserPreference } func (c *userPreferencesServiceClient) GetUserPreferences(ctx context.Context, in *GetUserPreferencesRequest, opts ...grpc.CallOption) (*GetUserPreferencesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUserPreferencesResponse) - err := c.cc.Invoke(ctx, UserPreferencesService_GetUserPreferences_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserPreferencesService_GetUserPreferences_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -66,8 +69,9 @@ func (c *userPreferencesServiceClient) GetUserPreferences(ctx context.Context, i } func (c *userPreferencesServiceClient) UpsertUserPreferences(ctx context.Context, in *UpsertUserPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, UserPreferencesService_UpsertUserPreferences_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, UserPreferencesService_UpsertUserPreferences_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -77,6 +81,8 @@ func (c *userPreferencesServiceClient) UpsertUserPreferences(ctx context.Context // UserPreferencesServiceServer is the server API for UserPreferencesService service. // All implementations must embed UnimplementedUserPreferencesServiceServer // for forward compatibility +// +// UserPreferencesService is a service that stores user settings. type UserPreferencesServiceServer interface { // GetUserPreferences returns the user preferences for a given user. GetUserPreferences(context.Context, *GetUserPreferencesRequest) (*GetUserPreferencesResponse, error) diff --git a/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go b/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go index bd6d9c9a86de4..7f0812c5f881f 100644 --- a/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: accessgraph/v1alpha/access_graph_service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AccessGraphService_Query_FullMethodName = "/accessgraph.v1alpha.AccessGraphService/Query" @@ -50,6 +50,8 @@ const ( // AccessGraphServiceClient is the client API for AccessGraphService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AccessGraphService is a service for interacting the access graph service. type AccessGraphServiceClient interface { // Query queries the access graph. // Currently only used by WebUI. @@ -101,8 +103,9 @@ func NewAccessGraphServiceClient(cc grpc.ClientConnInterface) AccessGraphService } func (c *accessGraphServiceClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(QueryResponse) - err := c.cc.Invoke(ctx, AccessGraphService_Query_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessGraphService_Query_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -110,8 +113,9 @@ func (c *accessGraphServiceClient) Query(ctx context.Context, in *QueryRequest, } func (c *accessGraphServiceClient) GetFile(ctx context.Context, in *GetFileRequest, opts ...grpc.CallOption) (*GetFileResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetFileResponse) - err := c.cc.Invoke(ctx, AccessGraphService_GetFile_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessGraphService_GetFile_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -119,11 +123,12 @@ func (c *accessGraphServiceClient) GetFile(ctx context.Context, in *GetFileReque } func (c *accessGraphServiceClient) EventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[0], AccessGraphService_EventsStream_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[0], AccessGraphService_EventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEventsStreamClient{stream} + x := &accessGraphServiceEventsStreamClient{ClientStream: stream} return x, nil } @@ -153,11 +158,12 @@ func (x *accessGraphServiceEventsStreamClient) CloseAndRecv() (*EventsStreamResp } func (c *accessGraphServiceClient) EventsStreamV2(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamV2Client, error) { - stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[1], AccessGraphService_EventsStreamV2_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[1], AccessGraphService_EventsStreamV2_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEventsStreamV2Client{stream} + x := &accessGraphServiceEventsStreamV2Client{ClientStream: stream} return x, nil } @@ -184,8 +190,9 @@ func (x *accessGraphServiceEventsStreamV2Client) Recv() (*EventsStreamV2Response } func (c *accessGraphServiceClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RegisterResponse) - err := c.cc.Invoke(ctx, AccessGraphService_Register_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessGraphService_Register_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -193,8 +200,9 @@ func (c *accessGraphServiceClient) Register(ctx context.Context, in *RegisterReq } func (c *accessGraphServiceClient) ReplaceCAs(ctx context.Context, in *ReplaceCAsRequest, opts ...grpc.CallOption) (*ReplaceCAsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReplaceCAsResponse) - err := c.cc.Invoke(ctx, AccessGraphService_ReplaceCAs_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AccessGraphService_ReplaceCAs_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -202,11 +210,12 @@ func (c *accessGraphServiceClient) ReplaceCAs(ctx context.Context, in *ReplaceCA } func (c *accessGraphServiceClient) AWSEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_AWSEventsStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[2], AccessGraphService_AWSEventsStream_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[2], AccessGraphService_AWSEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceAWSEventsStreamClient{stream} + x := &accessGraphServiceAWSEventsStreamClient{ClientStream: stream} return x, nil } @@ -236,11 +245,12 @@ func (x *accessGraphServiceAWSEventsStreamClient) CloseAndRecv() (*AWSEventsStre } func (c *accessGraphServiceClient) GitlabEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_GitlabEventsStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[3], AccessGraphService_GitlabEventsStream_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[3], AccessGraphService_GitlabEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceGitlabEventsStreamClient{stream} + x := &accessGraphServiceGitlabEventsStreamClient{ClientStream: stream} return x, nil } @@ -267,11 +277,12 @@ func (x *accessGraphServiceGitlabEventsStreamClient) Recv() (*GitlabEventsStream } func (c *accessGraphServiceClient) EntraEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EntraEventsStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[4], AccessGraphService_EntraEventsStream_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[4], AccessGraphService_EntraEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEntraEventsStreamClient{stream} + x := &accessGraphServiceEntraEventsStreamClient{ClientStream: stream} return x, nil } @@ -300,6 +311,8 @@ func (x *accessGraphServiceEntraEventsStreamClient) Recv() (*EntraEventsStreamRe // AccessGraphServiceServer is the server API for AccessGraphService service. // All implementations must embed UnimplementedAccessGraphServiceServer // for forward compatibility +// +// AccessGraphService is a service for interacting the access graph service. type AccessGraphServiceServer interface { // Query queries the access graph. // Currently only used by WebUI. @@ -424,7 +437,7 @@ func _AccessGraphService_GetFile_Handler(srv interface{}, ctx context.Context, d } func _AccessGraphService_EventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EventsStream(&accessGraphServiceEventsStreamServer{stream}) + return srv.(AccessGraphServiceServer).EventsStream(&accessGraphServiceEventsStreamServer{ServerStream: stream}) } type AccessGraphService_EventsStreamServer interface { @@ -450,7 +463,7 @@ func (x *accessGraphServiceEventsStreamServer) Recv() (*EventsStreamRequest, err } func _AccessGraphService_EventsStreamV2_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EventsStreamV2(&accessGraphServiceEventsStreamV2Server{stream}) + return srv.(AccessGraphServiceServer).EventsStreamV2(&accessGraphServiceEventsStreamV2Server{ServerStream: stream}) } type AccessGraphService_EventsStreamV2Server interface { @@ -512,7 +525,7 @@ func _AccessGraphService_ReplaceCAs_Handler(srv interface{}, ctx context.Context } func _AccessGraphService_AWSEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).AWSEventsStream(&accessGraphServiceAWSEventsStreamServer{stream}) + return srv.(AccessGraphServiceServer).AWSEventsStream(&accessGraphServiceAWSEventsStreamServer{ServerStream: stream}) } type AccessGraphService_AWSEventsStreamServer interface { @@ -538,7 +551,7 @@ func (x *accessGraphServiceAWSEventsStreamServer) Recv() (*AWSEventsStreamReques } func _AccessGraphService_GitlabEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).GitlabEventsStream(&accessGraphServiceGitlabEventsStreamServer{stream}) + return srv.(AccessGraphServiceServer).GitlabEventsStream(&accessGraphServiceGitlabEventsStreamServer{ServerStream: stream}) } type AccessGraphService_GitlabEventsStreamServer interface { @@ -564,7 +577,7 @@ func (x *accessGraphServiceGitlabEventsStreamServer) Recv() (*GitlabEventsStream } func _AccessGraphService_EntraEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EntraEventsStream(&accessGraphServiceEntraEventsStreamServer{stream}) + return srv.(AccessGraphServiceServer).EntraEventsStream(&accessGraphServiceEntraEventsStreamServer{ServerStream: stream}) } type AccessGraphService_EntraEventsStreamServer interface { diff --git a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go index 2a0e67550d748..f389a1704c7e7 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/lib/teleterm/v1/service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TerminalService_UpdateTshdEventsServerAddress_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/UpdateTshdEventsServerAddress" @@ -82,6 +82,13 @@ const ( // TerminalServiceClient is the client API for TerminalService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// TerminalService is used by the Electron app to communicate with the tsh daemon. +// +// While we aim to preserve backwards compatibility in order to satisfy CI checks and follow the +// proto practices used within the company, this service is not guaranteed to be stable across +// versions. The packaging process of Teleport Connect ensures that the server and the client use +// the same version of the service. type TerminalServiceClient interface { // UpdateTshdEventsServerAddress lets the Electron app update the address the tsh daemon is // supposed to use when connecting to the tshd events gRPC service. This RPC needs to be made @@ -215,8 +222,9 @@ func NewTerminalServiceClient(cc grpc.ClientConnInterface) TerminalServiceClient } func (c *terminalServiceClient) UpdateTshdEventsServerAddress(ctx context.Context, in *UpdateTshdEventsServerAddressRequest, opts ...grpc.CallOption) (*UpdateTshdEventsServerAddressResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpdateTshdEventsServerAddressResponse) - err := c.cc.Invoke(ctx, TerminalService_UpdateTshdEventsServerAddress_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_UpdateTshdEventsServerAddress_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -224,8 +232,9 @@ func (c *terminalServiceClient) UpdateTshdEventsServerAddress(ctx context.Contex } func (c *terminalServiceClient) ListRootClusters(ctx context.Context, in *ListClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListClustersResponse) - err := c.cc.Invoke(ctx, TerminalService_ListRootClusters_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ListRootClusters_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -233,8 +242,9 @@ func (c *terminalServiceClient) ListRootClusters(ctx context.Context, in *ListCl } func (c *terminalServiceClient) ListLeafClusters(ctx context.Context, in *ListLeafClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListClustersResponse) - err := c.cc.Invoke(ctx, TerminalService_ListLeafClusters_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ListLeafClusters_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -242,8 +252,9 @@ func (c *terminalServiceClient) ListLeafClusters(ctx context.Context, in *ListLe } func (c *terminalServiceClient) GetDatabases(ctx context.Context, in *GetDatabasesRequest, opts ...grpc.CallOption) (*GetDatabasesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetDatabasesResponse) - err := c.cc.Invoke(ctx, TerminalService_GetDatabases_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetDatabases_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -251,8 +262,9 @@ func (c *terminalServiceClient) GetDatabases(ctx context.Context, in *GetDatabas } func (c *terminalServiceClient) ListDatabaseUsers(ctx context.Context, in *ListDatabaseUsersRequest, opts ...grpc.CallOption) (*ListDatabaseUsersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListDatabaseUsersResponse) - err := c.cc.Invoke(ctx, TerminalService_ListDatabaseUsers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ListDatabaseUsers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -260,8 +272,9 @@ func (c *terminalServiceClient) ListDatabaseUsers(ctx context.Context, in *ListD } func (c *terminalServiceClient) GetServers(ctx context.Context, in *GetServersRequest, opts ...grpc.CallOption) (*GetServersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetServersResponse) - err := c.cc.Invoke(ctx, TerminalService_GetServers_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetServers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -269,8 +282,9 @@ func (c *terminalServiceClient) GetServers(ctx context.Context, in *GetServersRe } func (c *terminalServiceClient) GetAccessRequests(ctx context.Context, in *GetAccessRequestsRequest, opts ...grpc.CallOption) (*GetAccessRequestsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAccessRequestsResponse) - err := c.cc.Invoke(ctx, TerminalService_GetAccessRequests_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetAccessRequests_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -278,8 +292,9 @@ func (c *terminalServiceClient) GetAccessRequests(ctx context.Context, in *GetAc } func (c *terminalServiceClient) GetAccessRequest(ctx context.Context, in *GetAccessRequestRequest, opts ...grpc.CallOption) (*GetAccessRequestResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAccessRequestResponse) - err := c.cc.Invoke(ctx, TerminalService_GetAccessRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetAccessRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -287,8 +302,9 @@ func (c *terminalServiceClient) GetAccessRequest(ctx context.Context, in *GetAcc } func (c *terminalServiceClient) DeleteAccessRequest(ctx context.Context, in *DeleteAccessRequestRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_DeleteAccessRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_DeleteAccessRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -296,8 +312,9 @@ func (c *terminalServiceClient) DeleteAccessRequest(ctx context.Context, in *Del } func (c *terminalServiceClient) CreateAccessRequest(ctx context.Context, in *CreateAccessRequestRequest, opts ...grpc.CallOption) (*CreateAccessRequestResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateAccessRequestResponse) - err := c.cc.Invoke(ctx, TerminalService_CreateAccessRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_CreateAccessRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -305,8 +322,9 @@ func (c *terminalServiceClient) CreateAccessRequest(ctx context.Context, in *Cre } func (c *terminalServiceClient) ReviewAccessRequest(ctx context.Context, in *ReviewAccessRequestRequest, opts ...grpc.CallOption) (*ReviewAccessRequestResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReviewAccessRequestResponse) - err := c.cc.Invoke(ctx, TerminalService_ReviewAccessRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ReviewAccessRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -314,8 +332,9 @@ func (c *terminalServiceClient) ReviewAccessRequest(ctx context.Context, in *Rev } func (c *terminalServiceClient) GetRequestableRoles(ctx context.Context, in *GetRequestableRolesRequest, opts ...grpc.CallOption) (*GetRequestableRolesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetRequestableRolesResponse) - err := c.cc.Invoke(ctx, TerminalService_GetRequestableRoles_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetRequestableRoles_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -323,8 +342,9 @@ func (c *terminalServiceClient) GetRequestableRoles(ctx context.Context, in *Get } func (c *terminalServiceClient) AssumeRole(ctx context.Context, in *AssumeRoleRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_AssumeRole_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_AssumeRole_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -332,8 +352,9 @@ func (c *terminalServiceClient) AssumeRole(ctx context.Context, in *AssumeRoleRe } func (c *terminalServiceClient) PromoteAccessRequest(ctx context.Context, in *PromoteAccessRequestRequest, opts ...grpc.CallOption) (*PromoteAccessRequestResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PromoteAccessRequestResponse) - err := c.cc.Invoke(ctx, TerminalService_PromoteAccessRequest_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_PromoteAccessRequest_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -341,8 +362,9 @@ func (c *terminalServiceClient) PromoteAccessRequest(ctx context.Context, in *Pr } func (c *terminalServiceClient) GetSuggestedAccessLists(ctx context.Context, in *GetSuggestedAccessListsRequest, opts ...grpc.CallOption) (*GetSuggestedAccessListsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetSuggestedAccessListsResponse) - err := c.cc.Invoke(ctx, TerminalService_GetSuggestedAccessLists_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetSuggestedAccessLists_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -350,8 +372,9 @@ func (c *terminalServiceClient) GetSuggestedAccessLists(ctx context.Context, in } func (c *terminalServiceClient) GetKubes(ctx context.Context, in *GetKubesRequest, opts ...grpc.CallOption) (*GetKubesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetKubesResponse) - err := c.cc.Invoke(ctx, TerminalService_GetKubes_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetKubes_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -359,8 +382,9 @@ func (c *terminalServiceClient) GetKubes(ctx context.Context, in *GetKubesReques } func (c *terminalServiceClient) GetApps(ctx context.Context, in *GetAppsRequest, opts ...grpc.CallOption) (*GetAppsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetAppsResponse) - err := c.cc.Invoke(ctx, TerminalService_GetApps_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetApps_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -368,8 +392,9 @@ func (c *terminalServiceClient) GetApps(ctx context.Context, in *GetAppsRequest, } func (c *terminalServiceClient) AddCluster(ctx context.Context, in *AddClusterRequest, opts ...grpc.CallOption) (*Cluster, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Cluster) - err := c.cc.Invoke(ctx, TerminalService_AddCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_AddCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -377,8 +402,9 @@ func (c *terminalServiceClient) AddCluster(ctx context.Context, in *AddClusterRe } func (c *terminalServiceClient) RemoveCluster(ctx context.Context, in *RemoveClusterRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_RemoveCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_RemoveCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -386,8 +412,9 @@ func (c *terminalServiceClient) RemoveCluster(ctx context.Context, in *RemoveClu } func (c *terminalServiceClient) ListGateways(ctx context.Context, in *ListGatewaysRequest, opts ...grpc.CallOption) (*ListGatewaysResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListGatewaysResponse) - err := c.cc.Invoke(ctx, TerminalService_ListGateways_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ListGateways_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -395,8 +422,9 @@ func (c *terminalServiceClient) ListGateways(ctx context.Context, in *ListGatewa } func (c *terminalServiceClient) CreateGateway(ctx context.Context, in *CreateGatewayRequest, opts ...grpc.CallOption) (*Gateway, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Gateway) - err := c.cc.Invoke(ctx, TerminalService_CreateGateway_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_CreateGateway_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -404,8 +432,9 @@ func (c *terminalServiceClient) CreateGateway(ctx context.Context, in *CreateGat } func (c *terminalServiceClient) RemoveGateway(ctx context.Context, in *RemoveGatewayRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_RemoveGateway_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_RemoveGateway_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -413,8 +442,9 @@ func (c *terminalServiceClient) RemoveGateway(ctx context.Context, in *RemoveGat } func (c *terminalServiceClient) SetGatewayTargetSubresourceName(ctx context.Context, in *SetGatewayTargetSubresourceNameRequest, opts ...grpc.CallOption) (*Gateway, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Gateway) - err := c.cc.Invoke(ctx, TerminalService_SetGatewayTargetSubresourceName_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_SetGatewayTargetSubresourceName_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -422,8 +452,9 @@ func (c *terminalServiceClient) SetGatewayTargetSubresourceName(ctx context.Cont } func (c *terminalServiceClient) SetGatewayLocalPort(ctx context.Context, in *SetGatewayLocalPortRequest, opts ...grpc.CallOption) (*Gateway, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Gateway) - err := c.cc.Invoke(ctx, TerminalService_SetGatewayLocalPort_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_SetGatewayLocalPort_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -431,8 +462,9 @@ func (c *terminalServiceClient) SetGatewayLocalPort(ctx context.Context, in *Set } func (c *terminalServiceClient) GetAuthSettings(ctx context.Context, in *GetAuthSettingsRequest, opts ...grpc.CallOption) (*AuthSettings, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AuthSettings) - err := c.cc.Invoke(ctx, TerminalService_GetAuthSettings_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetAuthSettings_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -440,8 +472,9 @@ func (c *terminalServiceClient) GetAuthSettings(ctx context.Context, in *GetAuth } func (c *terminalServiceClient) GetCluster(ctx context.Context, in *GetClusterRequest, opts ...grpc.CallOption) (*Cluster, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Cluster) - err := c.cc.Invoke(ctx, TerminalService_GetCluster_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -449,8 +482,9 @@ func (c *terminalServiceClient) GetCluster(ctx context.Context, in *GetClusterRe } func (c *terminalServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_Login_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_Login_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -458,11 +492,12 @@ func (c *terminalServiceClient) Login(ctx context.Context, in *LoginRequest, opt } func (c *terminalServiceClient) LoginPasswordless(ctx context.Context, opts ...grpc.CallOption) (TerminalService_LoginPasswordlessClient, error) { - stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[0], TerminalService_LoginPasswordless_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[0], TerminalService_LoginPasswordless_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &terminalServiceLoginPasswordlessClient{stream} + x := &terminalServiceLoginPasswordlessClient{ClientStream: stream} return x, nil } @@ -489,8 +524,9 @@ func (x *terminalServiceLoginPasswordlessClient) Recv() (*LoginPasswordlessRespo } func (c *terminalServiceClient) Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_Logout_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_Logout_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -498,11 +534,12 @@ func (c *terminalServiceClient) Logout(ctx context.Context, in *LogoutRequest, o } func (c *terminalServiceClient) TransferFile(ctx context.Context, in *FileTransferRequest, opts ...grpc.CallOption) (TerminalService_TransferFileClient, error) { - stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[1], TerminalService_TransferFile_FullMethodName, opts...) + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[1], TerminalService_TransferFile_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &terminalServiceTransferFileClient{stream} + x := &terminalServiceTransferFileClient{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -530,8 +567,9 @@ func (x *terminalServiceTransferFileClient) Recv() (*FileTransferProgress, error } func (c *terminalServiceClient) ReportUsageEvent(ctx context.Context, in *ReportUsageEventRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EmptyResponse) - err := c.cc.Invoke(ctx, TerminalService_ReportUsageEvent_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ReportUsageEvent_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -539,8 +577,9 @@ func (c *terminalServiceClient) ReportUsageEvent(ctx context.Context, in *Report } func (c *terminalServiceClient) UpdateHeadlessAuthenticationState(ctx context.Context, in *UpdateHeadlessAuthenticationStateRequest, opts ...grpc.CallOption) (*UpdateHeadlessAuthenticationStateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpdateHeadlessAuthenticationStateResponse) - err := c.cc.Invoke(ctx, TerminalService_UpdateHeadlessAuthenticationState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_UpdateHeadlessAuthenticationState_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -548,8 +587,9 @@ func (c *terminalServiceClient) UpdateHeadlessAuthenticationState(ctx context.Co } func (c *terminalServiceClient) CreateConnectMyComputerRole(ctx context.Context, in *CreateConnectMyComputerRoleRequest, opts ...grpc.CallOption) (*CreateConnectMyComputerRoleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateConnectMyComputerRoleResponse) - err := c.cc.Invoke(ctx, TerminalService_CreateConnectMyComputerRole_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_CreateConnectMyComputerRole_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -557,8 +597,9 @@ func (c *terminalServiceClient) CreateConnectMyComputerRole(ctx context.Context, } func (c *terminalServiceClient) CreateConnectMyComputerNodeToken(ctx context.Context, in *CreateConnectMyComputerNodeTokenRequest, opts ...grpc.CallOption) (*CreateConnectMyComputerNodeTokenResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CreateConnectMyComputerNodeTokenResponse) - err := c.cc.Invoke(ctx, TerminalService_CreateConnectMyComputerNodeToken_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_CreateConnectMyComputerNodeToken_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -566,8 +607,9 @@ func (c *terminalServiceClient) CreateConnectMyComputerNodeToken(ctx context.Con } func (c *terminalServiceClient) WaitForConnectMyComputerNodeJoin(ctx context.Context, in *WaitForConnectMyComputerNodeJoinRequest, opts ...grpc.CallOption) (*WaitForConnectMyComputerNodeJoinResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(WaitForConnectMyComputerNodeJoinResponse) - err := c.cc.Invoke(ctx, TerminalService_WaitForConnectMyComputerNodeJoin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_WaitForConnectMyComputerNodeJoin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -575,8 +617,9 @@ func (c *terminalServiceClient) WaitForConnectMyComputerNodeJoin(ctx context.Con } func (c *terminalServiceClient) DeleteConnectMyComputerNode(ctx context.Context, in *DeleteConnectMyComputerNodeRequest, opts ...grpc.CallOption) (*DeleteConnectMyComputerNodeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeleteConnectMyComputerNodeResponse) - err := c.cc.Invoke(ctx, TerminalService_DeleteConnectMyComputerNode_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_DeleteConnectMyComputerNode_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -584,8 +627,9 @@ func (c *terminalServiceClient) DeleteConnectMyComputerNode(ctx context.Context, } func (c *terminalServiceClient) GetConnectMyComputerNodeName(ctx context.Context, in *GetConnectMyComputerNodeNameRequest, opts ...grpc.CallOption) (*GetConnectMyComputerNodeNameResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetConnectMyComputerNodeNameResponse) - err := c.cc.Invoke(ctx, TerminalService_GetConnectMyComputerNodeName_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetConnectMyComputerNodeName_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -593,8 +637,9 @@ func (c *terminalServiceClient) GetConnectMyComputerNodeName(ctx context.Context } func (c *terminalServiceClient) ListUnifiedResources(ctx context.Context, in *ListUnifiedResourcesRequest, opts ...grpc.CallOption) (*ListUnifiedResourcesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListUnifiedResourcesResponse) - err := c.cc.Invoke(ctx, TerminalService_ListUnifiedResources_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_ListUnifiedResources_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -602,8 +647,9 @@ func (c *terminalServiceClient) ListUnifiedResources(ctx context.Context, in *Li } func (c *terminalServiceClient) GetUserPreferences(ctx context.Context, in *GetUserPreferencesRequest, opts ...grpc.CallOption) (*GetUserPreferencesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUserPreferencesResponse) - err := c.cc.Invoke(ctx, TerminalService_GetUserPreferences_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_GetUserPreferences_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -611,8 +657,9 @@ func (c *terminalServiceClient) GetUserPreferences(ctx context.Context, in *GetU } func (c *terminalServiceClient) UpdateUserPreferences(ctx context.Context, in *UpdateUserPreferencesRequest, opts ...grpc.CallOption) (*UpdateUserPreferencesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpdateUserPreferencesResponse) - err := c.cc.Invoke(ctx, TerminalService_UpdateUserPreferences_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_UpdateUserPreferences_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -620,8 +667,9 @@ func (c *terminalServiceClient) UpdateUserPreferences(ctx context.Context, in *U } func (c *terminalServiceClient) AuthenticateWebDevice(ctx context.Context, in *AuthenticateWebDeviceRequest, opts ...grpc.CallOption) (*AuthenticateWebDeviceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AuthenticateWebDeviceResponse) - err := c.cc.Invoke(ctx, TerminalService_AuthenticateWebDevice_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TerminalService_AuthenticateWebDevice_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -631,6 +679,13 @@ func (c *terminalServiceClient) AuthenticateWebDevice(ctx context.Context, in *A // TerminalServiceServer is the server API for TerminalService service. // All implementations must embed UnimplementedTerminalServiceServer // for forward compatibility +// +// TerminalService is used by the Electron app to communicate with the tsh daemon. +// +// While we aim to preserve backwards compatibility in order to satisfy CI checks and follow the +// proto practices used within the company, this service is not guaranteed to be stable across +// versions. The packaging process of Teleport Connect ensures that the server and the client use +// the same version of the service. type TerminalServiceServer interface { // UpdateTshdEventsServerAddress lets the Electron app update the address the tsh daemon is // supposed to use when connecting to the tshd events gRPC service. This RPC needs to be made @@ -1383,7 +1438,7 @@ func _TerminalService_Login_Handler(srv interface{}, ctx context.Context, dec fu } func _TerminalService_LoginPasswordless_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TerminalServiceServer).LoginPasswordless(&terminalServiceLoginPasswordlessServer{stream}) + return srv.(TerminalServiceServer).LoginPasswordless(&terminalServiceLoginPasswordlessServer{ServerStream: stream}) } type TerminalService_LoginPasswordlessServer interface { @@ -1431,7 +1486,7 @@ func _TerminalService_TransferFile_Handler(srv interface{}, stream grpc.ServerSt if err := stream.RecvMsg(m); err != nil { return err } - return srv.(TerminalServiceServer).TransferFile(m, &terminalServiceTransferFileServer{stream}) + return srv.(TerminalServiceServer).TransferFile(m, &terminalServiceTransferFileServer{ServerStream: stream}) } type TerminalService_TransferFileServer interface { diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go index c4fb9657f6d10..bd558541e9342 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/lib/teleterm/v1/tshd_events_service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( TshdEventsService_Relogin_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/Relogin" @@ -47,6 +47,9 @@ const ( // TshdEventsServiceClient is the client API for TshdEventsService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// TshdEventsService is served by the Electron app. The tsh daemon calls this service to notify the +// app about actions that happen outside of the app itself. type TshdEventsServiceClient interface { // Relogin makes the Electron app display a login modal for the specific root cluster. The request // returns a response after the relogin procedure has been successfully finished. @@ -83,8 +86,9 @@ func NewTshdEventsServiceClient(cc grpc.ClientConnInterface) TshdEventsServiceCl } func (c *tshdEventsServiceClient) Relogin(ctx context.Context, in *ReloginRequest, opts ...grpc.CallOption) (*ReloginResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReloginResponse) - err := c.cc.Invoke(ctx, TshdEventsService_Relogin_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_Relogin_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -92,8 +96,9 @@ func (c *tshdEventsServiceClient) Relogin(ctx context.Context, in *ReloginReques } func (c *tshdEventsServiceClient) SendNotification(ctx context.Context, in *SendNotificationRequest, opts ...grpc.CallOption) (*SendNotificationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SendNotificationResponse) - err := c.cc.Invoke(ctx, TshdEventsService_SendNotification_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_SendNotification_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -101,8 +106,9 @@ func (c *tshdEventsServiceClient) SendNotification(ctx context.Context, in *Send } func (c *tshdEventsServiceClient) SendPendingHeadlessAuthentication(ctx context.Context, in *SendPendingHeadlessAuthenticationRequest, opts ...grpc.CallOption) (*SendPendingHeadlessAuthenticationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SendPendingHeadlessAuthenticationResponse) - err := c.cc.Invoke(ctx, TshdEventsService_SendPendingHeadlessAuthentication_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_SendPendingHeadlessAuthentication_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -110,8 +116,9 @@ func (c *tshdEventsServiceClient) SendPendingHeadlessAuthentication(ctx context. } func (c *tshdEventsServiceClient) PromptMFA(ctx context.Context, in *PromptMFARequest, opts ...grpc.CallOption) (*PromptMFAResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(PromptMFAResponse) - err := c.cc.Invoke(ctx, TshdEventsService_PromptMFA_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_PromptMFA_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -119,8 +126,9 @@ func (c *tshdEventsServiceClient) PromptMFA(ctx context.Context, in *PromptMFARe } func (c *tshdEventsServiceClient) GetUsageReportingSettings(ctx context.Context, in *GetUsageReportingSettingsRequest, opts ...grpc.CallOption) (*GetUsageReportingSettingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetUsageReportingSettingsResponse) - err := c.cc.Invoke(ctx, TshdEventsService_GetUsageReportingSettings_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_GetUsageReportingSettings_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -128,8 +136,9 @@ func (c *tshdEventsServiceClient) GetUsageReportingSettings(ctx context.Context, } func (c *tshdEventsServiceClient) ReportUnexpectedVnetShutdown(ctx context.Context, in *ReportUnexpectedVnetShutdownRequest, opts ...grpc.CallOption) (*ReportUnexpectedVnetShutdownResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReportUnexpectedVnetShutdownResponse) - err := c.cc.Invoke(ctx, TshdEventsService_ReportUnexpectedVnetShutdown_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, TshdEventsService_ReportUnexpectedVnetShutdown_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -139,6 +148,9 @@ func (c *tshdEventsServiceClient) ReportUnexpectedVnetShutdown(ctx context.Conte // TshdEventsServiceServer is the server API for TshdEventsService service. // All implementations must embed UnimplementedTshdEventsServiceServer // for forward compatibility +// +// TshdEventsService is served by the Electron app. The tsh daemon calls this service to notify the +// app about actions that happen outside of the app itself. type TshdEventsServiceServer interface { // Relogin makes the Electron app display a login modal for the specific root cluster. The request // returns a response after the relogin procedure has been successfully finished. diff --git a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go index 258c3f567900d..8bbf28768a63a 100644 --- a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/lib/teleterm/vnet/v1/vnet_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( VnetService_Start_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Start" @@ -42,6 +42,8 @@ const ( // VnetServiceClient is the client API for VnetService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// VnetService provides methods to manage a VNet instance. type VnetServiceClient interface { // Start starts VNet. Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) @@ -58,8 +60,9 @@ func NewVnetServiceClient(cc grpc.ClientConnInterface) VnetServiceClient { } func (c *vnetServiceClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(StartResponse) - err := c.cc.Invoke(ctx, VnetService_Start_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetService_Start_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -67,8 +70,9 @@ func (c *vnetServiceClient) Start(ctx context.Context, in *StartRequest, opts .. } func (c *vnetServiceClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(StopResponse) - err := c.cc.Invoke(ctx, VnetService_Stop_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, VnetService_Stop_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -78,6 +82,8 @@ func (c *vnetServiceClient) Stop(ctx context.Context, in *StopRequest, opts ...g // VnetServiceServer is the server API for VnetService service. // All implementations must embed UnimplementedVnetServiceServer // for forward compatibility +// +// VnetService provides methods to manage a VNet instance. type VnetServiceServer interface { // Start starts VNet. Start(context.Context, *StartRequest) (*StartResponse, error) diff --git a/go.mod b/go.mod index 217fe26308e81..7ae09d7c16cb8 100644 --- a/go.mod +++ b/go.mod @@ -211,7 +211,7 @@ require ( google.golang.org/api v0.180.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 google.golang.org/grpc v1.64.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 google.golang.org/protobuf v1.34.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/dnaeon/go-vcr.v3 v3.2.0 diff --git a/go.sum b/go.sum index c70e9ddcb829a..16aa0b6858697 100644 --- a/go.sum +++ b/go.sum @@ -3138,8 +3138,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integrations/event-handler/go.mod b/integrations/event-handler/go.mod index 2dad56b45dab6..4937dd2611eca 100644 --- a/integrations/event-handler/go.mod +++ b/integrations/event-handler/go.mod @@ -301,7 +301,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect google.golang.org/grpc v1.64.0 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect diff --git a/integrations/event-handler/go.sum b/integrations/event-handler/go.sum index 7c8ce9ca80e25..127d0865b8f60 100644 --- a/integrations/event-handler/go.sum +++ b/integrations/event-handler/go.sum @@ -2357,8 +2357,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index 301d36d08f447..6e65fea1e29d6 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -420,7 +420,7 @@ require ( google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index c958715f0e3c6..e09253bd4a552 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -2914,8 +2914,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/lib/multiplexer/test/ping_grpc.pb.go b/lib/multiplexer/test/ping_grpc.pb.go index 488b8f6ebde55..4c882f1a4aae8 100644 --- a/lib/multiplexer/test/ping_grpc.pb.go +++ b/lib/multiplexer/test/ping_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: teleport/lib/multiplexer/test/ping.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( Pinger_Ping_FullMethodName = "/teleport.lib.multiplexer.test.Pinger/Ping" @@ -42,6 +42,8 @@ const ( // PingerClient is the client API for Pinger service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// Pinger is a service used in tests type PingerClient interface { Ping(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) } @@ -55,8 +57,9 @@ func NewPingerClient(cc grpc.ClientConnInterface) PingerClient { } func (c *pingerClient) Ping(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Response) - err := c.cc.Invoke(ctx, Pinger_Ping_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, Pinger_Ping_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -66,6 +69,8 @@ func (c *pingerClient) Ping(ctx context.Context, in *Request, opts ...grpc.CallO // PingerServer is the server API for Pinger service. // All implementations must embed UnimplementedPingerServer // for forward compatibility +// +// Pinger is a service used in tests type PingerServer interface { Ping(context.Context, *Request) (*Response, error) mustEmbedUnimplementedPingerServer() From 8bb31751bf6ddd3a229ea8968665368663d2b7d9 Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Wed, 5 Jun 2024 16:09:10 -0600 Subject: [PATCH 16/33] Remove old websocket endpoints (#42461) These endpoints passed the bearer token insecurely over a URL query param rather than over an authenticated websocket, and are no longer necessary as Teleport 15 only requires the newer version. --- lib/web/apiserver.go | 55 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 4a3ec9ee7172f..bda39d9bab936 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -771,13 +771,10 @@ func (h *Handler) bindDefaultEndpoints() { h.DELETE("/webapi/sites/:site/locks/:uuid", h.WithClusterAuth(h.deleteClusterLock)) // active sessions handlers - // Deprecated: The connect/ws variant should be used instead. - // TODO(lxea): DELETE in v16 - h.GET("/webapi/sites/:site/connect", h.WithClusterAuthWebSocket(false, h.siteNodeConnect)) // connect to an active session (via websocket) - h.GET("/webapi/sites/:site/connect/ws", h.WithClusterAuthWebSocket(true, h.siteNodeConnect)) // connect to an active session (via websocket, with auth over websocket) + h.GET("/webapi/sites/:site/connect/ws", h.WithClusterAuthWebSocket(h.siteNodeConnect)) // connect to an active session (via websocket, with auth over websocket) h.GET("/webapi/sites/:site/sessions", h.WithClusterAuth(h.clusterActiveAndPendingSessionsGet)) // get list of active and pending sessions - h.GET("/webapi/sites/:site/kube/:clusterName/connect/ws", h.WithClusterAuthWebSocket(true, h.podConnect)) // connect to a pod with exec (via websocket, with auth over websocket) + h.GET("/webapi/sites/:site/kube/:clusterName/connect/ws", h.WithClusterAuthWebSocket(h.podConnect)) // connect to a pod with exec (via websocket, with auth over websocket) // Audit events handlers. h.GET("/webapi/sites/:site/events/search", h.WithClusterAuth(h.clusterSearchEvents)) // search site events @@ -894,18 +891,10 @@ func (h *Handler) bindDefaultEndpoints() { h.GET("/webapi/sites/:site/desktops", h.WithClusterAuth(h.clusterDesktopsGet)) h.GET("/webapi/sites/:site/desktopservices", h.WithClusterAuth(h.clusterDesktopServicesGet)) h.GET("/webapi/sites/:site/desktops/:desktopName", h.WithClusterAuth(h.getDesktopHandle)) - // GET /webapi/sites/:site/desktops/:desktopName/connect?access_token=&username=&width=&height= - // Deprecated: The connect/ws variant should be used instead. - // TODO(lxea): DELETE in v16 - h.GET("/webapi/sites/:site/desktops/:desktopName/connect", h.WithClusterAuthWebSocket(false, h.desktopConnectHandle)) // GET /webapi/sites/:site/desktops/:desktopName/connect?username=&width=&height= - h.GET("/webapi/sites/:site/desktops/:desktopName/connect/ws", h.WithClusterAuthWebSocket(true, h.desktopConnectHandle)) - // GET /webapi/sites/:site/desktopplayback/:sid?access_token= - // Deprecated: The desktopplayback/ws variant should be used instead. - // TODO(lxea): DELETE in v16 - h.GET("/webapi/sites/:site/desktopplayback/:sid", h.WithClusterAuthWebSocket(false, h.desktopPlaybackHandle)) + h.GET("/webapi/sites/:site/desktops/:desktopName/connect/ws", h.WithClusterAuthWebSocket(h.desktopConnectHandle)) // GET /webapi/sites/:site/desktopplayback/:sid/ws - h.GET("/webapi/sites/:site/desktopplayback/:sid/ws", h.WithClusterAuthWebSocket(true, h.desktopPlaybackHandle)) + h.GET("/webapi/sites/:site/desktopplayback/:sid/ws", h.WithClusterAuthWebSocket(h.desktopPlaybackHandle)) h.GET("/webapi/sites/:site/desktops/:desktopName/active", h.WithClusterAuth(h.desktopIsActive)) // GET a Connection Diagnostics by its name @@ -974,7 +963,7 @@ func (h *Handler) bindDefaultEndpoints() { h.GET("/webapi/sites/:site/user-groups", h.WithClusterAuth(h.getUserGroups)) // WebSocket endpoint for the chat conversation, websocket auth - h.GET("/webapi/sites/:site/assistant/ws", h.WithClusterAuthWebSocket(true, h.assistant)) + h.GET("/webapi/sites/:site/assistant/ws", h.WithClusterAuthWebSocket(h.assistant)) // Fetches the user's preferences h.GET("/webapi/user/preferences", h.WithAuth(h.getUserPreferences)) @@ -4151,24 +4140,11 @@ var authnWsUpgrader = websocket.Upgrader{ } // WithClusterAuthWebSocket wraps a ClusterWebsocketHandler to ensure that a request is authenticated -// to this proxy via websocket if websocketAuth is true, or via query parameter if false (the same as WithAuth), as -// well as to grab the remoteSite (which can represent this local cluster or a remote trusted cluster) -// as specified by the ":site" url parameter. -// -// TODO(lxea): remove the 'websocketAuth' bool once the deprecated websocket handlers are removed -func (h *Handler) WithClusterAuthWebSocket(websocketAuth bool, fn ClusterWebsocketHandler) httprouter.Handle { +// to this proxy via websocket, as well as to grab the remoteSite (which can represent this local +// cluster or a remote trusted cluster) as specified by the ":site" url parameter. +func (h *Handler) WithClusterAuthWebSocket(fn ClusterWebsocketHandler) httprouter.Handle { return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (any, error) { - var sctx *SessionContext - var ws *websocket.Conn - var site reversetunnelclient.RemoteSite - var err error - - if websocketAuth { - sctx, ws, site, err = h.authenticateWSRequestWithCluster(w, r, p) - } else { - sctx, ws, site, err = h.authenticateWSRequestWithClusterDeprecated(w, r, p) - } - + sctx, ws, site, err := h.authenticateWSRequestWithCluster(w, r, p) if err != nil { return nil, trace.Wrap(err) } @@ -4201,19 +4177,6 @@ func (h *Handler) authenticateWSRequestWithCluster(w http.ResponseWriter, r *htt return sctx, ws, site, nil } -// TODO(lxea): remove once the deprecated websocket handlers are removed -func (h *Handler) authenticateWSRequestWithClusterDeprecated(w http.ResponseWriter, r *http.Request, p httprouter.Params) (*SessionContext, *websocket.Conn, reversetunnelclient.RemoteSite, error) { - sctx, site, err := h.authenticateRequestWithCluster(w, r, p) - if err != nil { - return nil, nil, nil, trace.Wrap(err) - } - ws, err := authnWsUpgrader.Upgrade(w, r, nil) - if err != nil { - return nil, nil, nil, trace.Wrap(err) - } - return sctx, ws, site, nil -} - // authenticateRequestWithCluster ensures that a request is authenticated // to this proxy, returning the *SessionContext (same as AuthenticateRequest), // and also grabs the remoteSite (which can represent this local cluster or a From 0c5bd96a4fc89c512dfdf1542295e41202a46701 Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Wed, 5 Jun 2024 18:30:51 -0400 Subject: [PATCH 17/33] Add a guide for economic buyers (#42460) * Add a guide for economic buyers Introduce the three Teleport products, spell out use cases, and provide a very brief architectural overview. In each section that describes a product, include links to relevant docs guides. The plan is to feature this guide more centrally once we reorganize the docs. For now, this change adds the guide to the docs without including navigation links to make it possible to review the text. * Turn the economic buyer guide into the home page Also respond to xinding33 feedback. The economic buyer guide will serve as the home page until we have a more purpose-built home page that provides information on the three main Teleport products. Also edit the introduction to the "Teleport Agents" section to make it more immediately useful as a guide to enrolling resources with Teleport that we can link to from the "Teleport Access" discussion in the economic buyer guide. Moves the original home page to "Deploy a Cluster" as a guide to deploying a demo cluster. --- docs/config.json | 10 +- docs/pages/agents/introduction.mdx | 109 +++--- docs/pages/deploy-a-cluster/introduction.mdx | 4 + docs/pages/deploy-a-cluster/linux-demo.mdx | 208 +++++++++++ docs/pages/index.mdx | 337 +++++++----------- .../management/admin/self-signed-certs.mdx | 2 +- 6 files changed, 395 insertions(+), 275 deletions(-) create mode 100644 docs/pages/deploy-a-cluster/linux-demo.mdx diff --git a/docs/config.json b/docs/config.json index a064e6d4f3290..77c2b180e492f 100644 --- a/docs/config.json +++ b/docs/config.json @@ -5,7 +5,7 @@ "title": "Home", "entries": [ { - "title": "Get Started with Teleport", + "title": "Introduction to Teleport", "slug": "/" }, { @@ -157,6 +157,10 @@ "slug": "/deploy-a-cluster/introduction/", "forScopes": ["oss", "enterprise"] }, + { + "title": "Linux Demo", + "slug": "/deploy-a-cluster/linux-demo/" + }, { "title": "High Availability Deployments", "slug": "/deploy-a-cluster/high-availability/", @@ -865,7 +869,7 @@ ] }, { - "title": "Run Teleport Agents", + "title": "Protect Infrastructure", "icon": "layers", "entries": [ { @@ -873,7 +877,7 @@ "slug": "/agents/introduction/" }, { - "title": "Deploy via Terraform", + "title": "Deploy Agents via Terraform", "slug": "/agents/deploy-agents-terraform/" }, { diff --git a/docs/pages/agents/introduction.mdx b/docs/pages/agents/introduction.mdx index 7da3a2440af5e..ff5ad4513fdb2 100644 --- a/docs/pages/agents/introduction.mdx +++ b/docs/pages/agents/introduction.mdx @@ -1,19 +1,40 @@ --- -title: "Teleport Agents" -description: Deploy agents to enroll resources in your infrastructure with Teleport. You can run multiple Teleport services per agent." +title: Protect Infrastructure with Teleport +description: Deploy Agents to enroll resources in your infrastructure with Teleport. You can run multiple Teleport services per Agent." --- -Teleport agents are Teleport instances that are configured to proxy traffic to +You can use Teleport to protect infrastructure resources like servers and +databases by deploying **Teleport Agents**. + +Teleport Agents are Teleport instances that are configured to proxy traffic to resources in your infrastructure, such as servers, databases, and Kubernetes clusters. -This section shows you how to use Teleport agents to enable secure access to +This section shows you how to use Teleport Agents to enable secure access to your infrastructure. +## Enroll infrastructure resources + +To protect infrastructure resources with Teleport, you deploy Teleport Agents +and configure them to proxy traffic to and from the resources. + +We recommend getting started with [Teleport +Auto-Discovery](../auto-discovery/introduction.mdx), in which the Teleport +Discovery Service registers infrastructure resources with your cluster by +polling service discovery endpoints. For information on enrolling a specific +type of infrastructure resource, read the following sections of the +documentation: + +- [Servers](../server-access/introduction.mdx) +- [Databases](../database-access/introduction.mdx) +- [Kubernetes clusters](../kubernetes-access/introduction.mdx) +- [Windows desktops](../desktop-access/introduction.mdx) +- [Applications](../application-access/introduction.mdx) + ## Architecture overview -This section provides a brief outline of how Teleport agents run in a Teleport -cluster. For more information on the architecture of Teleport agents, read +This section provides a brief outline of how Teleport Agents run in a Teleport +cluster. For more information on the architecture of Teleport Agents, read [Teleport Agent Architecture](../architecture/agents.mdx). ### Services @@ -29,25 +50,31 @@ services are enabled by default and how to enable a particular service. Agents typically run in the same private networks as the resources they proxy. They should be the only clients that can access a resource without Teleport. -In this setup, agents dial the Teleport Proxy Service in order to establish +In this setup, Agents dial the Teleport Proxy Service in order to establish reverse SSH tunnels. While the Proxy Service remains open to the public internet -via its HTTPS port, agents require no open ports or public address. +via its HTTPS port, Agents require no open ports or public address. The Teleport Proxy Service uses these reverse tunnels to forward traffic in -Teleport's supported protocols to an available agent. Agents apply RBAC +Teleport's supported protocols to an available Agent. Agents apply RBAC rules and forward the traffic to resources in your infrastructure. -![Diagram showing the architecture of an agent pool](../../img/agent-pool-diagram.png) +![Diagram showing the architecture of an Agent pool](../../img/agent-pool-diagram.png) Read our guide for how to use Terraform to [deploy a pool of -agents](deploy-agents-terraform.mdx). +Agents](deploy-agents-terraform.mdx). + +## Joining Agents -## Joining agents +Teleport Agents run one or more services, such as the Teleport SSH Service and +Teleport Database Service. To establish trust between an Agent and your Teleport +cluster, you use one of several **join methods**. When joining an Agent, you can +configure the services that run on the agent. You can also edit the +configuration of an Agent to change the services that run on it. ### Initially joining a cluster -Teleport agents need to establish trust with the Teleport Auth Service in order -to join a cluster. There are several ways to join an agent to your Teleport +Teleport Agents need to establish trust with the Teleport Auth Service in order +to join a cluster. There are several ways to join an Agent to your Teleport cluster, making it possible to automate the join process for your environment. Read about the available join methods in our [Join Services to your Cluster](./join-services-to-your-cluster.mdx) guides. @@ -55,59 +82,33 @@ Cluster](./join-services-to-your-cluster.mdx) guides. When a Teleport process first runs, it checks its configuration file to determine which services are enabled. Each service then connects separately to the Teleport Auth Service, which checks whether it has created a **join token** -for that service. If so, the Auth Service issues the agent credentials signed +for that service. If so, the Auth Service issues the Agent credentials signed for that service. -### Joining a new service on an existing agent +### Joining a new service on an existing Agent -The credentials that the Auth Service issues to agents are signed for specific -services. To run new services on an agent, you must repeat the initial join +The credentials that the Auth Service issues to Agents are signed for specific +services. To run new services on an Agent, you must repeat the initial join procedure for those services. -Generate a new join token for all services running on an agent, including the -new services. Then make the new join token available to the agent. The method to +Generate a new join token for all services running on an Agent, including the +new services. Then make the new join token available to the Agent. The method to use depends on the value of either `teleport.join_params` or -`teleport.auth_token` in the agent's configuration file: +`teleport.auth_token` in the Agent's configuration file: - If the value of the configuration field is a token, update the token. - If the value is a file path, edit the file at that path to refer to the new token. -Delete the agent's state directory, which is `/var/lib/teleport` by default. -(Check the `teleport.data_dir` field of the agent's configuration file.) With no -data directory, the agent will obtain its initial credentials from the Auth +Delete the Agent's state directory, which is `/var/lib/teleport` by default. +(Check the `teleport.data_dir` field of the Agent's configuration file.) With no +data directory, the Agent will obtain its initial credentials from the Auth Service instead of reading existing credentials. -Finally, restart the agent. +Finally, restart the Agent. -We recommend deploying Teleport agents via infrastructure-as-code approaches, +We recommend deploying Teleport Agents via infrastructure-as-code approaches, e.g., [using a Terraform module](./deploy-agents-terraform.mdx). To modify the -services that an agent runs, you can edit the configuration of your agents -within your infrastructure-as-code project, then redeploy the agents. - -## Enrolling infrastructure - -There are two ways to enroll infrastructure resources with Teleport agents: - -- **Static**: Edit an agent's configuration file to configure a specific - infrastructure resource to proxy. -- **Dynamic**: Apply a [configuration - resource](../management/dynamic-resources.mdx) that configures a resource to - proxy. - -The dynamic method allows Teleport to discover resources automatically. The -Discovery Service polls your cloud provider APIs and modifies dynamic -infrastructure resources as required. - -[Read our guide](deploy-agents-terraform.mdx) to deploying a pool of agents -via Terraform and enrolling infrastructure resources dynamically. - -To learn how to enroll resources via static configuration files, plus all the -ways Teleport supports enrolling infrastructure, consult our guides to each of -Teleport's services: +services that an Agent runs, you can edit the configuration of your Agents +within your infrastructure-as-code project, then redeploy the Agents. -- [SSH Service](../server-access/introduction.mdx) -- [Database Service](../database-access/introduction.mdx) -- [Kubernetes Service](../kubernetes-access/introduction.mdx) -- [Windows Desktop Service](../desktop-access/introduction.mdx) -- [Application Service](../application-access/introduction.mdx) diff --git a/docs/pages/deploy-a-cluster/introduction.mdx b/docs/pages/deploy-a-cluster/introduction.mdx index a0e3c9d2d401f..84994d8257762 100644 --- a/docs/pages/deploy-a-cluster/introduction.mdx +++ b/docs/pages/deploy-a-cluster/introduction.mdx @@ -6,6 +6,10 @@ description: "Guides to running Teleport in production." These guides show you how to run a self-hosted Teleport Enterprise or Teleport Community Edition cluster in production. +Before deploying a production Teleport cluster on your own infrastructure, you +may want to deploy a demo cluster on a single Linux server by following the +[Linux Demo Cluster](./linux-demo.mdx) guide. + Read our [High Availability Guide](./high-availability.mdx) for the general principles behind deploying a scalable, fault-tolerant Teleport cluster. Once you understand what is required to run Teleport in production, choose your diff --git a/docs/pages/deploy-a-cluster/linux-demo.mdx b/docs/pages/deploy-a-cluster/linux-demo.mdx new file mode 100644 index 0000000000000..f507c3582384b --- /dev/null +++ b/docs/pages/deploy-a-cluster/linux-demo.mdx @@ -0,0 +1,208 @@ +--- +title: Run a Self-Hosted Demo Cluster +description: This tutorial will guide you through the steps needed to install and run Teleport on a Linux server +videoBanner: BJWbSqiDLeU +tocDepth: 3 +--- + +See how a self-hosted Teleport deployment works by completing the tutorial +below. This shows you how to spin up a single-instance Teleport cluster on a +Linux server using Teleport Community Edition. Once you deploy the cluster, you +can configure RBAC, register resources, and protect your small-scale demo +environments or home lab. + +You can also get started right away with a production-ready Teleport cluster by +signing up for a [free trial of Teleport Enterprise +Cloud](https://goteleport.com/signup/). + +
+![Architecture of the setup you will complete in this +guide](../../img/linux-server-diagram.png) +
+ +We will run the following Teleport services: + +- **Teleport Auth Service:** The certificate authority for your cluster. It + issues certificates and conducts authentication challenges. The Auth Service + is typically inaccessible outside your private network. +- **Teleport Proxy Service:** The cluster frontend, which handles user requests, + forwards user credentials to the Auth Service, and communicates with Teleport + instances that enable access to specific resources in your infrastructure. +- **Teleport SSH Service:** An SSH server implementation that takes advantage of + Teleport's short-lived certificates, sophisticated RBAC, session recording, + and other features. + +## Prerequisites + +You will need the following to deploy a demo Teleport cluster. If your +environment doesn't meet the prerequisites, you can get started with Teleport by +signing up for a [free trial of Teleport Enterprise +Cloud](https://goteleport.com/signup/). + +If you want to get a feel for Teleport commands and capabilities without setting +up any infrastructure, take a look at the browser-based [Teleport +Labs](https://goteleport.com/labs). + +- A Linux host with only port `443` open to ingress traffic. You must be able + to install and run software on the host. Either configure access to the host + via SSH for the initial setup (and open an SSH port in addition port `443`) + or enter the commands in this guide into an Amazon EC2 [user data + script](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html), + Google Compute Engine [startup + script](https://cloud.google.com/compute/docs/instances/startup-scripts), + or similar. + + For a quick demo environment you can use to follow this guide, consider + installing our DigitalOcean 1-Click droplet. View the installation page on + [DigitalOcean + Marketplace](https://marketplace.digitalocean.com/apps/teleport). Once your + droplet is ready, SSH into the droplet and follow the configuration wizard. + +- A multi-factor authenticator app such as [Authy](https://authy.com/download/), + [Google Authenticator](https://www.google.com/landing/2step/), or + [1Password](https://support.1password.com/one-time-passwords/). + +You must also have **one** of the following: +- A registered domain name. +- An authoritative DNS nameserver managed by your organization, plus an existing + certificate authority. If using this approach, ensure that your browser is + configured to use your organization's nameserver. + +## Step 1/4. Configure DNS + +Teleport uses TLS to provide secure access to its Proxy Service and Auth +Service, and this requires a domain name that clients can use to verify +Teleport's certificate. Set up two DNS `A` records, each pointing to the IP +address of your Linux host. Assuming `teleport.example.com` is your domain name, +set up records for: + +|Domain|Reason| +|---|---| +|`teleport.example.com`|Traffic to the Proxy Service from users and services.| +|`*.teleport.example.com`|Traffic to web applications registered with Teleport. Teleport issues a subdomain of your cluster's domain name to each application.| + +## Step 2/4. Set up Teleport on your Linux host + +### Install Teleport + +On your Linux host, run the following command to install the Teleport binary: + +```code +$ curl https://goteleport.com/static/install.sh | bash -s (=teleport.version=) +``` + +### Configure Teleport + +Generate a configuration file for Teleport using the `teleport configure` command. +This command requires information about a TLS certificate and private key. + +(!docs/pages/includes/tls-certificate-setup.mdx!) + +### Start Teleport + +(!docs/pages/includes/start-teleport.mdx!) + +Access Teleport's Web UI via HTTPS at the domain you created earlier (e.g., +`https://teleport.example.com`). You should see a welcome screen similar to the +following: + +![Teleport Welcome Screen](../../img/quickstart/welcome.png) + +## Step 3/4. Create a Teleport user and set up multi-factor authentication + +In this step, we'll create a new Teleport user, `teleport-admin`, which is +allowed to log into SSH hosts as any of the principals `root`, `ubuntu`, or +`ec2-user`. + +On your Linux host, run the following command: + +```code +# tctl is an administrative tool that is used to configure Teleport's auth service. +$ sudo tctl users add teleport-admin --roles=editor,access --logins=root,ubuntu,ec2-user +``` + +The command prints a message similar to the following: + +```text +User "teleport-admin" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h: +https://teleport.example.com:443/web/invite/123abc456def789ghi123abc456def78 + +NOTE: Make sure teleport.example.com:443 points at a Teleport proxy which users can access. +``` + +Visit the provided URL in order to create your Teleport user. + + + + The users that you specify in the `logins` flag (e.g., `root`, `ubuntu` and + `ec2-user` in our examples) must exist on your Linux host. Otherwise, you + will get authentication errors later in this tutorial. + + If a user does not already exist, you can create it with `adduser ` or + use [host user creation](../server-access/guides/host-user-creation.mdx). + + If you do not have the permission to create new users on the Linux host, run + `tctl users add teleport $(whoami)` to explicitly allow Teleport to + authenticate as the user that you have currently logged in as. + + + +Teleport enforces the use of multi-factor authentication by default. It supports +one-time passwords (OTP) and multi-factor authenticators (WebAuthn). In this +guide, you will need to enroll an OTP authenticator application using the QR +code on the Teleport welcome screen. + +
+ +In addition to Teleport's Web UI, you can access resources in your +infrastructure via the `tsh` client tool. + +Install `tsh` on your local workstation: + +(!docs/pages/includes/install-tsh.mdx!) + +Log in to receive short-lived certificates from Teleport: + +```code +# Replace teleport.example.com with your Teleport cluster's public address as configured above. +$ tsh login --proxy= --user=teleport-admin +> Profile URL: https://teleport.example.com:443 + Logged in as: teleport-admin + Cluster: teleport.example.com + Roles: access, editor + Logins: root, ubuntu, ec2-user + Kubernetes: enabled + Valid until: 2022-04-26 03:04:46 -0400 EDT [valid for 12h0m0s] + Extensions: permit-agent-forwarding, permit-port-forwarding, permit-pty +``` + +
+ +## Step 4/4. Enroll your infrastructure + +With Teleport, you can protect all of the resources in your infrastructure +behind a single identity-aware access proxy, including servers, databases, +applications, Kubernetes clusters, Windows desktops, and cloud provider APIs. + +To enroll a resource with Teleport, visit the Web UI and click **Enroll New +Resource**. The Web UI will show you the steps you can take to enroll your new +resource. + +![Adding resources](../../img/add-resources.png) + +On the home page of the Web UI, you can see that you have already enrolled your +Linux server. + +## Next step: deploy agents + +Teleport **agents** proxy traffic to infrastructure resources like servers, +databases, Kubernetes clusters, cloud provider APIs, and Windows desktops. + +Step 4 showed you how to install agents manually, and you can also launch agents +and enroll resources with them using infrastructure-as-code tools. For example, +you can use Terraform to declare a pool of Teleport agents and configure them to +proxy your infrastructure. Read [Deploy Teleport Agents with +Terraform](../agents/deploy-agents-terraform.mdx) to get started. diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index 9fb54ee0d3d93..924811b0eb69b 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -1,221 +1,124 @@ --- -title: Get Started with Teleport -description: This tutorial will guide you through the steps needed to install and run Teleport on a Linux server -videoBanner: BJWbSqiDLeU +title: Introduction to Teleport +description: Provides an overview of the purpose and benefits of Teleport Access Platform, which allows you to implement Zero Trust for all of your infrastructure. tocDepth: 3 --- -Teleport provides connectivity, authentication, access controls -and audit for infrastructure. - -It includes an identity-aware access proxy, a CA that issues short-lived certificates, -a unified access control system and a tunneling system to access resources -behind the firewall. - -Teleport understands the SSH, HTTPS, RDP, Kubernetes API, MySQL, MongoDB and PostgreSQL wire -protocols, plus many others. It can integrate with Single Sign-On providers and -enables you to apply access policies using infrastructure-as-code and GitOps -tools. - -See how Teleport works by completing the tutorial below. This shows you how to -spin up a single-instance Teleport cluster on a Linux server using Teleport -Community Edition. Once you deploy the cluster, you can configure RBAC, register -resources, and protect your small-scale demo environments or home lab. - -You can also get started right away with a production-ready Teleport cluster by -signing up for a [free trial of Teleport Enterprise -Cloud](https://goteleport.com/signup/). - -## Set up a demo cluster - -
-![Architecture of the setup you will complete in this -guide](../img/linux-server-diagram.png) -
- -We will run the following Teleport services: - -- **Teleport Auth Service:** The certificate authority for your cluster. It - issues certificates and conducts authentication challenges. The Auth Service - is typically inaccessible outside your private network. -- **Teleport Proxy Service:** The cluster frontend, which handles user requests, - forwards user credentials to the Auth Service, and communicates with Teleport - instances that enable access to specific resources in your infrastructure. -- **Teleport SSH Service:** An SSH server implementation that takes advantage of - Teleport's short-lived certificates, sophisticated RBAC, session recording, - and other features. - -### Prerequisites - -You will need the following to deploy a demo Teleport cluster. If your -environment doesn't meet the prerequisites, you can get started with Teleport by -signing up for a [free trial of Teleport Enterprise -Cloud](https://goteleport.com/signup/). - -If you want to get a feel for Teleport commands and capabilities without setting -up any infrastructure, take a look at the browser-based [Teleport -Labs](https://goteleport.com/labs). - -- A Linux host with only port `443` open to ingress traffic. You must be able - to install and run software on the host. Either configure access to the host - via SSH for the initial setup (and open an SSH port in addition port `443`) - or enter the commands in this guide into an Amazon EC2 [user data - script](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html), - Google Compute Engine [startup - script](https://cloud.google.com/compute/docs/instances/startup-scripts), - or similar. - - For a quick demo environment you can use to follow this guide, consider - installing our DigitalOcean 1-Click droplet. View the installation page on - [DigitalOcean - Marketplace](https://marketplace.digitalocean.com/apps/teleport). Once your - droplet is ready, SSH into the droplet and follow the configuration wizard. - -- A multi-factor authenticator app such as [Authy](https://authy.com/download/), - [Google Authenticator](https://www.google.com/landing/2step/), or - [1Password](https://support.1password.com/one-time-passwords/). - -You must also have **one** of the following: -- A registered domain name. -- An authoritative DNS nameserver managed by your organization, plus an existing - certificate authority. If using this approach, ensure that your browser is - configured to use your organization's nameserver. - -### Step 1/4. Configure DNS - -Teleport uses TLS to provide secure access to its Proxy Service and Auth -Service, and this requires a domain name that clients can use to verify -Teleport's certificate. Set up two DNS `A` records, each pointing to the IP -address of your Linux host. Assuming `teleport.example.com` is your domain name, -set up records for: - -|Domain|Reason| -|---|---| -|`teleport.example.com`|Traffic to the Proxy Service from users and services.| -|`*.teleport.example.com`|Traffic to web applications registered with Teleport. Teleport issues a subdomain of your cluster's domain name to each application.| - -### Step 2/4. Set up Teleport on your Linux host - -#### Install Teleport - -On your Linux host, run the following command to install the Teleport binary: - -```code -$ curl https://goteleport.com/static/install.sh | bash -s (=teleport.version=) -``` - -#### Configure Teleport - -Generate a configuration file for Teleport using the `teleport configure` command. -This command requires information about a TLS certificate and private key. - -(!docs/pages/includes/tls-certificate-setup.mdx!) - -#### Start Teleport - -(!docs/pages/includes/start-teleport.mdx!) - -Access Teleport's Web UI via HTTPS at the domain you created earlier (e.g., -`https://teleport.example.com`). You should see a welcome screen similar to the -following: - -![Teleport Welcome Screen](../img/quickstart/welcome.png) - -### Step 3/4. Create a Teleport user and set up multi-factor authentication - -In this step, we'll create a new Teleport user, `teleport-admin`, which is -allowed to log into SSH hosts as any of the principals `root`, `ubuntu`, or -`ec2-user`. - -On your Linux host, run the following command: - -```code -# tctl is an administrative tool that is used to configure Teleport's auth service. -$ sudo tctl users add teleport-admin --roles=editor,access --logins=root,ubuntu,ec2-user -``` - -The command prints a message similar to the following: - -```text -User "teleport-admin" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h: -https://teleport.example.com:443/web/invite/123abc456def789ghi123abc456def78 - -NOTE: Make sure teleport.example.com:443 points at a Teleport proxy which users can access. -``` - -Visit the provided URL in order to create your Teleport user. - - - - The users that you specify in the `logins` flag (e.g., `root`, `ubuntu` and - `ec2-user` in our examples) must exist on your Linux host. Otherwise, you - will get authentication errors later in this tutorial. - - If a user does not already exist, you can create it with `adduser ` or - use [host user creation](./server-access/guides/host-user-creation.mdx). - - If you do not have the permission to create new users on the Linux host, run - `tctl users add teleport $(whoami)` to explicitly allow Teleport to - authenticate as the user that you have currently logged in as. - - - -Teleport enforces the use of multi-factor authentication by default. It supports -one-time passwords (OTP) and multi-factor authenticators (WebAuthn). In this -guide, you will need to enroll an OTP authenticator application using the QR -code on the Teleport welcome screen. - -
- -In addition to Teleport's Web UI, you can access resources in your -infrastructure via the `tsh` client tool. - -Install `tsh` on your local workstation: - -(!docs/pages/includes/install-tsh.mdx!) - -Log in to receive short-lived certificates from Teleport: - -```code -# Replace teleport.example.com with your Teleport cluster's public address as configured above. -$ tsh login --proxy= --user=teleport-admin -> Profile URL: https://teleport.example.com:443 - Logged in as: teleport-admin - Cluster: teleport.example.com - Roles: access, editor - Logins: root, ubuntu, ec2-user - Kubernetes: enabled - Valid until: 2022-04-26 03:04:46 -0400 EDT [valid for 12h0m0s] - Extensions: permit-agent-forwarding, permit-port-forwarding, permit-pty -``` - -
- -### Step 4/4. Enroll your infrastructure - -With Teleport, you can protect all of the resources in your infrastructure -behind a single identity-aware access proxy, including servers, databases, -applications, Kubernetes clusters, Windows desktops, and cloud provider APIs. - -To enroll a resource with Teleport, visit the Web UI and click **Enroll New -Resource**. The Web UI will show you the steps you can take to enroll your new -resource. - -![Adding resources](../img/add-resources.png) - -On the home page of the Web UI, you can see that you have already enrolled your -Linux server. - -### Next step: deploy agents - -Teleport **agents** proxy traffic to infrastructure resources like servers, -databases, Kubernetes clusters, cloud provider APIs, and Windows desktops. - -Step 4 showed you how to install agents manually, and you can also launch agents -and enroll resources with them using infrastructure-as-code tools. For example, -you can use Terraform to declare a pool of Teleport agents and configure them to -proxy your infrastructure. Read [Deploy Teleport Agents with -Terraform](agents/deploy-agents-terraform.mdx) to get started. +Teleport is the easiest and most secure way to access and protect all your +infrastructure. + +The Teleport Access Platform is a suite of software and managed services that +delivers on-demand, least-privileged access to infrastructure on a foundation of +cryptographic identity and Zero Trust, with built-in identity security and +policy governance. + +## Use cases + +Organizations use the Teleport Access Platform to: + +- **Eliminate infrastructure and access silos:** Teleport provides a single + system for role-based access controls, audit, and access for all of your + infrastructure, from cloud provider APIs to Kubernetes clusters. +- **Introduce Zero Trust with theft-resistant credentials:** Teleport + authenticates access to all of your infrastructure with short-lived + certificates, verified at every endpoint. You can roll out a Zero Trust + strategy for your organization by setting up Teleport alone. +- **Address complex compliance needs:** Teleport allows you to satisfy + compliance frameworks like SOC 2 and FedRAMP with no need for additional + tooling or process changes. + +## Products + +The Teleport Access Platform consists of three products: + +- **Teleport Access** provides on-demand, least privileged access, on a + foundation of cryptographic identity and Zero Trust. +- **Teleport Identity** hardens your infrastructure with identity governance and + security. +- **Teleport Policy** unifies and controls access policies across all of your + infrastructure. + +### Teleport Access + +**Teleport Access** provides Zero Trust connectivity to all of your +infrastructure. You can enable users to access servers, databases, and other +infrastructure components over the public internet, even if those components are +protected behind a firewall. + +All infrastructure resources belong to a unified inventory, with a single +role-based access controls system to allow for least-privilege access. You can +enable users to authenticate to resources using Single Sign-On providers like +Okta, and Teleport itself can act as an identity provider for external services. +Connectivity takes place through short-lived credentials that Teleport +components verify through strong cryptographic techniques. + +Get started with Teleport Access: + +- [Enroll resources](agents/introduction.mdx) to protect with Teleport using + cryptographic identity. +- [Set up passwordless authentication](access-controls/guides/passwordless.mdx) + to enable users to access resources with hardware keys, including biometric + credentials like Touch ID and YubiKey Bio. +- [Integrate your Single Sign-On provider](access-controls/sso.mdx): Allow users + to access infrastructure resources with IdPs like Okta. +- [Use Teleport as an identity provider](access-controls/idps/saml-guide.mdx) to + authenticate to external services. +- [Issue and govern identities for automated + systems](machine-id/introduction.mdx) using Machine ID. + +### Teleport Identity + +**Teleport Identity** is an add-on to Teleport Access that offers identity +governance and security for all of your infrastructure. You can provide users +less-privileged roles by default, requiring any user who wants additional +permissions to request them for a limited time. This approach leaves no +permanent admin roles for attackers to target. + +As an additional layer of protection against phishing and exfiltration, you can +restrict access to users with trusted devices. And during active security +incidents, you can lock down specific users, roles, infrastructure resources and +more, letting you contain the security incident with minimal disruption. An +audit log provides visibility into access patterns, so you can identify weak +restrictions and potential security breaches. + +Get started with Teleport Identity: + +- [Access Requests](./access-controls/access-requests.mdx): Temporarily + provision minimal privileges to complete a task. +- [Access Lists](./access-controls/access-lists.mdx): Regularly audit and + control membership to specific roles and traits, which then tie easily back + into Teleport's existing RBAC system. +- [Device Trust](./access-controls/device-trust.mdx): Require an up-to-date, + registered device for each authentication by giving every device a + cryptographic identity. +- [Session & Identity Locks](./access-controls/guides/locking.mdx): Lock + suspicious or compromised identities and stop all their activity across all + protocols and services. +- [Access Monitoring](./access-controls/access-monitoring.mdx): Detect overly + broad privileges and inspect sessions that are not using strong protection, + such as multi-factor authentication and Device Trust. + +### Teleport Policy + +**Teleport Policy** unifies and controls access policies across all of your +infrastructure. With Teleport Access Graph, you can get insight into role-based +access control policies, including in Teleport and your cloud provider. + +Get started with [Teleport Access Graph](access-controls/access-graph.mdx). + +## Architecture + +The Teleport Access Platform consists of a certificate authority and +identity-aware access proxy that run either on the Teleport-managed cloud or, in +special cases, a self-hosted private network. + +Teleport Agents, which the user deploys on Linux servers or Kubernetes, proxy +access to infrastructure resources and cloud provider APIs. Users authenticate +to infrastructure resources through Teleport agents using short-lived +certificates. Certificates indicate Teleport role membership, allowing Teleport +Agents to enforce role-based access-controls. + +Learn more: + +- [Teleport Core Concepts](./core-concepts.mdx) +- [Architecture Guides](./architecture/introduction.mdx) diff --git a/docs/pages/management/admin/self-signed-certs.mdx b/docs/pages/management/admin/self-signed-certs.mdx index 2a3f52828a491..218ca2feb6a98 100644 --- a/docs/pages/management/admin/self-signed-certs.mdx +++ b/docs/pages/management/admin/self-signed-certs.mdx @@ -204,7 +204,7 @@ flag](../../connect-your-client/teleport-connect.mdx#skipping-tls-certificate-ve ## Further reading -- [Configuring Teleport TLS Certs](../../index.mdx#configure-teleport) +- [Configuring Teleport TLS Certs](../../deploy-a-cluster/linux-demo.mdx#configure-teleport) - [Run Teleport as a systemd Daemon](./daemon.mdx) - [Teleport Proxy Service](../../architecture/proxy.mdx) - [Teleport Authentication](../../architecture/authentication.mdx) From 67a3d64f381393bb6eba42c400a8415713c10117 Mon Sep 17 00:00:00 2001 From: Nic Klaassen Date: Wed, 5 Jun 2024 17:15:24 -0700 Subject: [PATCH 18/33] [vnet] clean up osconfig on startup (#42518) --- lib/vnet/osconfig.go | 6 ++---- lib/vnet/setup.go | 13 +++++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/vnet/osconfig.go b/lib/vnet/osconfig.go index f566fe8620a17..1ef9a91a7df11 100644 --- a/lib/vnet/osconfig.go +++ b/lib/vnet/osconfig.go @@ -130,11 +130,9 @@ func (c *osConfigurator) updateOSConfiguration(ctx context.Context) error { return trace.Wrap(err, "configuring OS") } -func (c *osConfigurator) deconfigureOS() error { +func (c *osConfigurator) deconfigureOS(ctx context.Context) error { // configureOS is meant to be called with an empty config to deconfigure anything necessary. - // Pass context.Background() because we are likely deconfiguring because we received a signal to terminate - // and all contexts have been canceled. - return trace.Wrap(configureOS(context.Background(), &osConfig{})) + return trace.Wrap(configureOS(ctx, &osConfig{})) } func (c *osConfigurator) setTunIPv4FromCIDR(cidrRange string) error { diff --git a/lib/vnet/setup.go b/lib/vnet/setup.go index 55e9f93520472..d778fdf5c69b3 100644 --- a/lib/vnet/setup.go +++ b/lib/vnet/setup.go @@ -235,10 +235,19 @@ func createAndSetupTUNDeviceAsRoot(ctx context.Context, ipv6Prefix, dnsAddr stri return tunCh, errCh } + // Clean up any stale configuration left by a previous VNet instance that may have failed to clean up. + // This is necessary in case any stale /etc/resolver/ entries are still present, we need to + // be able to reach the proxy in order to fetch the vnet_config. + if err := osConfigurator.deconfigureOS(ctx); err != nil { + errCh <- trace.Wrap(err, "cleaning up OS configuration on startup") + return tunCh, errCh + } + go func() { defer func() { - // Shutting down, deconfigure OS. - errCh <- trace.Wrap(osConfigurator.deconfigureOS()) + // Shutting down, deconfigure OS. Pass context.Background because [ctx] has likely been canceled + // already but we still need to clean up. + errCh <- trace.Wrap(osConfigurator.deconfigureOS(context.Background())) }() if err := osConfigurator.updateOSConfiguration(ctx); err != nil { From e513f46d7663df575559941e8d844080fd7fb32d Mon Sep 17 00:00:00 2001 From: Marco Dinis Date: Thu, 6 Jun 2024 08:53:57 +0100 Subject: [PATCH 19/33] Fix typo in Edit Integration screen (#42499) --- .../teleport/src/Integrations/EditAwsOidcIntegrationDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/teleport/src/Integrations/EditAwsOidcIntegrationDialog.tsx b/web/packages/teleport/src/Integrations/EditAwsOidcIntegrationDialog.tsx index d1ced2fea7b6a..e5b0dd47c0ece 100644 --- a/web/packages/teleport/src/Integrations/EditAwsOidcIntegrationDialog.tsx +++ b/web/packages/teleport/src/Integrations/EditAwsOidcIntegrationDialog.tsx @@ -218,7 +218,7 @@ export function EditAwsOidcIntegrationDialog(props: Props) { setConfirmed(e.target.checked); }} /> - I have ran the command + I ran the command )} From 9288a76bed7e26a90e2b33b882bdc73c2fdb7788 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 6 Jun 2024 11:36:13 +0100 Subject: [PATCH 20/33] debug: debug ephemeral container creation (#42068) * debug: debug ephemeral container creation Signed-off-by: Tiago Silva * remove fmt * deflake tests and simplify code --------- Signed-off-by: Tiago Silva --- lib/kube/proxy/sess.go | 108 ++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/kube/proxy/sess.go b/lib/kube/proxy/sess.go index bbdcbc1fc57b1..2bbb95ffa4a6f 100644 --- a/lib/kube/proxy/sess.go +++ b/lib/kube/proxy/sess.go @@ -40,7 +40,6 @@ import ( apimachinerytypes "k8s.io/apimachinery/pkg/types" kubeapitypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/watch" - clientv1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/remotecommand" watchtools "k8s.io/client-go/tools/watch" @@ -518,7 +517,7 @@ func (s *session) checkPresence() error { // launch waits until the session meets access requirements and then transitions the session // to a running state. -func (s *session) launch(isEphemeralCont bool) (returnErr error) { +func (s *session) launch(ephemeralContainerStatus *corev1.ContainerStatus) (returnErr error) { defer func() { err := s.Close() if err != nil { @@ -640,44 +639,29 @@ func (s *session) launch(isEphemeralCont bool) (returnErr error) { } s.io.On() - if streamErr := executor.StreamWithContext(s.streamContext, options); streamErr != nil { - if !isEphemeralCont { - return trace.Wrap(streamErr) - } - - // If attaching to the container failed, check if the container - // is terminated. If it is, try to stream the logs. If it's not - // terminated or can't be found return the original error. - clientSet, _, err := s.forwarder.impersonatedKubeClient(&s.sess.authContext, s.req.Header) - if err != nil { - return trace.Wrap(err) - } - podClient := clientSet.CoreV1().Pods(namespace) + // If the container is ephemeral and already terminated, we should + // retrieve the logs and return early. + if ephemeralContainerStatus != nil && ephemeralContainerStatus.State.Terminated != nil { + err := s.retrieveAlreadyStoppedPodLogs( + namespace, + podName, + container, + ) + return trace.Wrap(err) + } - pod, err := podClient.Get(s.forwarder.ctx, podName, metav1.GetOptions{}) - if err != nil { - return trace.Wrap(err) - } - status := getEphemeralContainerStatusByName(pod, container) - if status == nil { - // the container couldn't be found in the pod, return the - // original command streaming error + if streamErr := executor.StreamWithContext(s.streamContext, options); streamErr != nil { + // If the container isn't ephemeral, return the error. + if ephemeralContainerStatus == nil { return trace.Wrap(streamErr) } - if status.State.Terminated != nil { - if err := s.retrieveAlreadyStoppedPodLogs( - podClient, - namespace, - podName, - container, - ); err != nil { - return trace.Wrap(err) - } - - return nil - } - - return trace.Wrap(streamErr) + fmt.Fprintf(s.io, "\r\nwarning: couldn't attach to pod/%s, falling back to streaming logs: %v\r\n", podName, streamErr) + err := s.retrieveAlreadyStoppedPodLogs( + namespace, + podName, + container, + ) + return trace.Wrap(err) } return nil @@ -1052,7 +1036,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error { } // createEphemeralContainer creates an ephemeral container and waits for it to start. -func (s *session) createEphemeralContainer() (bool, error) { +func (s *session) createEphemeralContainer() (*corev1.ContainerStatus, error) { initUser := s.parties[s.initiator] username := initUser.Ctx.Identity.GetIdentity().Username namespace := s.params.ByName("podNamespace") @@ -1070,9 +1054,9 @@ func (s *session) createEphemeralContainer() (bool, error) { }, ) if trace.IsNotFound(err) { - return false, nil + return nil, nil } else if err != nil { - return false, trace.Wrap(err) + return nil, trace.Wrap(err) } if err = s.forwarder.cfg.AuthClient.DeleteKubernetesWaitingContainer( @@ -1085,16 +1069,12 @@ func (s *session) createEphemeralContainer() (bool, error) { ContainerName: container, }, ); err != nil { - return false, trace.Wrap(err) + return nil, trace.Wrap(err) } s.log.Debugf("Creating ephemeral container %s on pod %s", container, podName) - err = s.patchAndWaitForPodEphemeralContainer(s.forwarder.ctx, &initUser.Ctx, s.req.Header, waitingCont) - if err != nil { - return false, trace.Wrap(err) - } - - return true, nil + containerStatus, err := s.patchAndWaitForPodEphemeralContainer(s.forwarder.ctx, &initUser.Ctx, s.req.Header, waitingCont) + return containerStatus, trace.Wrap(err) } func (s *session) BroadcastMessage(format string, args ...any) { @@ -1399,22 +1379,27 @@ func (s *session) getSessionMetadata() apievents.SessionMetadata { // patchPodWithEphemeralContainer creates an ephemeral container and waits // for it to start. -func (s *session) patchAndWaitForPodEphemeralContainer(ctx context.Context, authCtx *authContext, headers http.Header, waitingCont *kubewaitingcontainerpb.KubernetesWaitingContainer) error { +func (s *session) patchAndWaitForPodEphemeralContainer( + ctx context.Context, + authCtx *authContext, + headers http.Header, + waitingCont *kubewaitingcontainerpb.KubernetesWaitingContainer, +) (containerStatus *corev1.ContainerStatus, err error) { fmt.Fprintf(s.io, "\r\nCreating ephemeral container %s in pod %s/%s\r\n", waitingCont.Spec.ContainerName, waitingCont.Spec.Namespace, waitingCont.Spec.PodName) clientSet, _, err := s.forwarder.impersonatedKubeClient(authCtx, headers) if err != nil { - return trace.Wrap(err) + return nil, trace.Wrap(err) } podClient := clientSet.CoreV1().Pods(authCtx.kubeResource.Namespace) - _, err = podClient.Patch(ctx, + result, err := podClient.Patch(ctx, waitingCont.Spec.PodName, kubeapitypes.StrategicMergePatchType, waitingCont.Spec.Patch, metav1.PatchOptions{}, "ephemeralcontainers") if err != nil { - return trace.Wrap(err) + return nil, trace.Wrap(err) } fmt.Fprintf(s.io, "Pod %s/%s successfully patched. Waiting for container to become ready.\r\n", @@ -1425,10 +1410,14 @@ func (s *session) patchAndWaitForPodEphemeralContainer(ctx context.Context, auth lw := &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { options.FieldSelector = fieldSelector + options.ResourceVersion = result.GetResourceVersion() + options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan return podClient.List(ctx, options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { options.FieldSelector = fieldSelector + options.ResourceVersion = result.GetResourceVersion() + options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan return podClient.Watch(ctx, options) }, } @@ -1448,21 +1437,31 @@ func (s *session) patchAndWaitForPodEphemeralContainer(ctx context.Context, auth return false, nil } if s.State.Running != nil || s.State.Terminated != nil { + containerStatus = s return true, nil } return false, nil }) if err != nil { - return trace.Wrap(err) + return nil, trace.Wrap(err) } fmt.Fprintf(s.io, "Ephemeral container %s is ready.\r\n", waitingCont.Spec.ContainerName) - return nil + return containerStatus, nil } // retrieveAlreadyStoppedPodLogs retrieves the logs of a stopped pod and writes them to the session's io writer. -func (s *session) retrieveAlreadyStoppedPodLogs(podClient clientv1.PodInterface, namespace, podName, container string) error { +func (s *session) retrieveAlreadyStoppedPodLogs(namespace, podName, container string) error { + // If attaching to the container failed, check if the container + // is terminated. If it is, try to stream the logs. If it's not + // terminated or can't be found return the original error. + clientSet, _, err := s.forwarder.impersonatedKubeClient(&s.sess.authContext, s.req.Header) + if err != nil { + return trace.Wrap(err) + } + podClient := clientSet.CoreV1().Pods(namespace) + fmt.Fprintf(s.io, "Failed to attach to the container, attempting to stream logs instead...\r\n") req := podClient.GetLogs(podName, &corev1.PodLogOptions{Container: container}) r, err := req.Stream(s.streamContext) @@ -1470,6 +1469,7 @@ func (s *session) retrieveAlreadyStoppedPodLogs(podClient clientv1.PodInterface, return trace.Wrap(err) } if _, err := io.Copy(s.io, r); err != nil { + _ = r.Close() return trace.Wrap(err) } return trace.Wrap(r.Close()) From f63aa826b5c36c77f56c8aa258b585b2a4aae687 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 6 Jun 2024 11:39:31 +0100 Subject: [PATCH 21/33] fix: cleanup routine when semaphore lease is lost (#42534) This PR fixes an edge case where the semaphore lock can be lost due to a backend error and the routine can continue running. Signed-off-by: Tiago Silva --- lib/srv/discovery/access_graph.go | 26 ++++++++++++++++++++------ lib/srv/discovery/access_graph_test.go | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/srv/discovery/access_graph.go b/lib/srv/discovery/access_graph.go index 80ef511b56e66..3fb339e346dfc 100644 --- a/lib/srv/discovery/access_graph.go +++ b/lib/srv/discovery/access_graph.go @@ -23,6 +23,7 @@ import ( "crypto/tls" "crypto/x509" "errors" + "sync" "time" "github.com/gravitational/trace" @@ -45,10 +46,8 @@ const ( batchSize = 500 ) -var ( - // errNoAccessGraphFetchers is returned when there are no TAG fetchers. - errNoAccessGraphFetchers = errors.New("no Access Graph fetchers") -) +// errNoAccessGraphFetchers is returned when there are no TAG fetchers. +var errNoAccessGraphFetchers = errors.New("no Access Graph fetchers") func (s *Server) reconcileAccessGraph(ctx context.Context, currentTAGResources *aws_sync.Resources, stream accessgraphv1alpha.AccessGraphService_AWSEventsStreamClient, features aws_sync.Features) error { type fetcherResult struct { @@ -269,6 +268,21 @@ func (s *Server) initializeAndWatchAccessGraph(ctx context.Context, reloadCh <-c if err != nil { return trace.Wrap(err) } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(1) + go func() { + defer wg.Done() + select { + case <-ctx.Done(): + return + case <-lease.Done(): + cancel() + } + }() defer func() { lease.Stop() if err := lease.Wait(); err != nil { @@ -310,12 +324,12 @@ func (s *Server) initializeAndWatchAccessGraph(ctx context.Context, reloadCh <-c } features := aws_sync.BuildFeatures(supportedKinds...) - ctx, cancel := context.WithCancel(ctx) - defer cancel() // Start a goroutine to watch the access graph service connection state. // If the connection is closed, cancel the context to stop the event watcher // before it tries to send any events to the access graph service. + wg.Add(1) go func() { + defer wg.Done() defer cancel() if !accessGraphConn.WaitForStateChange(ctx, connectivity.Ready) { s.Log.Info("access graph service connection was closed") diff --git a/lib/srv/discovery/access_graph_test.go b/lib/srv/discovery/access_graph_test.go index ad8af07f5986c..b7ab66960dce0 100644 --- a/lib/srv/discovery/access_graph_test.go +++ b/lib/srv/discovery/access_graph_test.go @@ -31,7 +31,7 @@ import ( ) func TestServer_updateDiscoveryConfigStatus(t *testing.T) { - var testErr = "test error" + testErr := "test error" clock := clockwork.NewFakeClock() type args struct { fetchers []aws_sync.AWSSync From 0320e9f9e252448ed26c219b825815a04cbfce06 Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Thu, 6 Jun 2024 09:46:58 -0400 Subject: [PATCH 22/33] docs: remove sudo usage in troubleshooting (#42536) Co-authored-by: Steven Martin --- docs/pages/machine-id/troubleshooting.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/machine-id/troubleshooting.mdx b/docs/pages/machine-id/troubleshooting.mdx index 925d5c014b102..4f46e7a8351c4 100644 --- a/docs/pages/machine-id/troubleshooting.mdx +++ b/docs/pages/machine-id/troubleshooting.mdx @@ -135,8 +135,8 @@ server-side data and issues a new joining token. Remove and recreate the bot, replacing the name and role list as desired: ```code -$ sudo tctl bots rm example -$ sudo tctl bots add example --roles=access +$ tctl bots rm example +$ tctl bots add example --roles=access ``` Copy the resulting join token into the existing bot config—either the From 7e67beb2238ac5c83abe8c5046e8bd1921e7a8aa Mon Sep 17 00:00:00 2001 From: Evan Freed <2314084+evanfreed@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:08:00 -0500 Subject: [PATCH 23/33] bump docs to 15.4.0 (#42369) Signed-off-by: Evan Freed --- docs/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.json b/docs/config.json index 77c2b180e492f..725ca6a33c1fb 100644 --- a/docs/config.json +++ b/docs/config.json @@ -1926,7 +1926,7 @@ "aws_secret_access_key": "zyxw9876-this-is-an-example" }, "cloud": { - "version": "15.3.7", + "version": "15.4.0", "major_version": "15", "sla": { "monthly_percentage": "99.9%", From e6e33bfaba00bf43d63f34fd835dd8493e8f0c21 Mon Sep 17 00:00:00 2001 From: Grzegorz Zdunek Date: Thu, 6 Jun 2024 16:38:17 +0200 Subject: [PATCH 24/33] Fix removing resources from access request when they share the same name (#42475) * Allow passing more information to the list items in the request checkout Thanks to this, we can know what cluster the resource belongs to. Also, we don't have to convert the "shared" and "Connect" requested resource. * Make `key` type in `Table` work properly when `data` extends another type * Fix `key` generation in access request bar * Conditionally show cluster name column in the request checkout * Display a pretty resource kind name * Remodel `PendingListItemWithOriginalItem` type * Handle `pendingAccessRequest` being undefined * Always return a string from `getPrettyResourceKind` * Make cluster name the first item --- web/packages/design/src/DataTable/types.ts | 2 +- .../RequestCheckout/RequestCheckout.tsx | 137 +++++++++++------- .../RequestCheckout.story.test.tsx.snap | 20 +-- .../NewRequest/RequestCheckout/index.ts | 2 +- .../AccessRequestCheckout.tsx | 12 +- .../useAccessRequestCheckout.ts | 64 ++++---- 6 files changed, 141 insertions(+), 96 deletions(-) diff --git a/web/packages/design/src/DataTable/types.ts b/web/packages/design/src/DataTable/types.ts index a1937bd74cb4c..7f850cf7a075b 100644 --- a/web/packages/design/src/DataTable/types.ts +++ b/web/packages/design/src/DataTable/types.ts @@ -105,7 +105,7 @@ export type ServersideProps = { // Makes it so either key or altKey is required type TableColumnWithKey = TableColumnBase & { - key: Extract; + key: keyof T & string; // altSortKey is the alternative field to sort column by, // if provided. Otherwise it falls back to sorting by field // "key". diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx index 7ba4125f08fea..9de44e13b7616 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx @@ -61,10 +61,9 @@ import type { TransitionStatus } from 'react-transition-group'; import type { AccessRequest } from 'shared/services/accessRequests'; import type { ResourceKind } from '../resource'; -export function RequestCheckoutWithSlider({ - transitionState, - ...props -}: RequestCheckoutWithSliderProps) { +export function RequestCheckoutWithSlider< + T extends PendingListItem = PendingListItem, +>({ transitionState, ...props }: RequestCheckoutWithSliderProps) { const ref = useRef(); // Listeners are attached to enable overflow on the parent container after @@ -120,7 +119,7 @@ export function RequestCheckoutWithSlider({ ); } -export function RequestCheckout({ +export function RequestCheckout({ toggleResource, onClose, reset, @@ -138,6 +137,7 @@ export function RequestCheckout({ setRequestTTL, dryRunResponse, data, + showClusterNameColumn, createAttempt, fetchResourceRequestRolesAttempt, createRequest, @@ -148,7 +148,7 @@ export function RequestCheckout({ isResourceRequest, selectedResourceRequestRoles, Header, -}: RequestCheckoutProps) { +}: RequestCheckoutProps) { // Specifies the start date/time a requestor requested for. const [start, setStart] = useState(); const [reason, setReason] = useState(''); @@ -243,9 +243,17 @@ export function RequestCheckout({ ( + {getPrettyResourceKind(item.kind)} + ), }, { key: 'name', @@ -261,11 +269,7 @@ export function RequestCheckout({ p={2} onClick={() => { clearAttempt(); - toggleResource( - resource.kind, - resource.id, - resource.name - ); + toggleResource(resource); }} disabled={createAttempt.status === 'processing'} css={` @@ -623,6 +627,30 @@ function TextBox({ ); } +function getPrettyResourceKind(kind: ResourceKind): string { + switch (kind) { + case 'role': + return 'Role'; + case 'app': + return 'Application'; + case 'node': + return 'Server'; + case 'resource': + return 'Resource'; + case 'db': + return 'Database'; + case 'kube_cluster': + return 'Kubernetes'; + case 'user_group': + return 'User Group'; + case 'windows_desktop': + return 'Desktop'; + default: + kind satisfies never; + return kind; + } +} + const requireText = (value: string, requireReason: boolean) => () => { if (requireReason && (!value || value.trim().length === 0)) { return { @@ -687,49 +715,52 @@ const StyledTable = styled(Table)` overflow: hidden; ` as typeof Table; -export type RequestCheckoutWithSliderProps = { +export type RequestCheckoutWithSliderProps< + T extends PendingListItem = PendingListItem, +> = { transitionState: TransitionStatus; -} & RequestCheckoutProps; - -export type RequestCheckoutProps = { - onClose(): void; - toggleResource: ( - kind: ResourceKind, - resourceId: string, - resourceName?: string - ) => void; - appsGrantedByUserGroup?: string[]; - userGroupFetchAttempt?: Attempt; - reset: () => void; - SuccessComponent?: (params: SuccessComponentParams) => JSX.Element; - isResourceRequest: boolean; - requireReason: boolean; - selectedReviewers: ReviewerOption[]; - data: { - kind: ResourceKind; - /** Name of the resource, for presentation purposes only. */ - name: string; - /** Identifier of the resource. Should be sent in requests. */ - id: string; - }[]; - setRequestTTL: (value: Option) => void; - createRequest: (req: CreateRequest) => void; - fetchStatus: 'loading' | 'loaded'; - fetchResourceRequestRolesAttempt: Attempt; - requestTTL: Option; - resourceRequestRoles: string[]; - reviewers: string[]; - setSelectedReviewers: (value: ReviewerOption[]) => void; - setMaxDuration: (value: Option) => void; - clearAttempt: () => void; - createAttempt: Attempt; - setSelectedResourceRequestRoles: (value: string[]) => void; - numRequestedResources: number; - selectedResourceRequestRoles: string[]; - dryRunResponse: AccessRequest; - maxDuration: Option; - Header?: () => JSX.Element; -}; +} & RequestCheckoutProps; + +export interface PendingListItem { + kind: ResourceKind; + /** Name of the resource, for presentation purposes only. */ + name: string; + /** Identifier of the resource. Should be sent in requests. */ + id: string; + clusterName?: string; +} + +export type RequestCheckoutProps = + { + onClose(): void; + toggleResource: (resource: T) => void; + appsGrantedByUserGroup?: string[]; + userGroupFetchAttempt?: Attempt; + reset: () => void; + SuccessComponent?: (params: SuccessComponentParams) => JSX.Element; + isResourceRequest: boolean; + requireReason: boolean; + selectedReviewers: ReviewerOption[]; + data: T[]; + showClusterNameColumn?: boolean; + setRequestTTL: (value: Option) => void; + createRequest: (req: CreateRequest) => void; + fetchStatus: 'loading' | 'loaded'; + fetchResourceRequestRolesAttempt: Attempt; + requestTTL: Option; + resourceRequestRoles: string[]; + reviewers: string[]; + setSelectedReviewers: (value: ReviewerOption[]) => void; + setMaxDuration: (value: Option) => void; + clearAttempt: () => void; + createAttempt: Attempt; + setSelectedResourceRequestRoles: (value: string[]) => void; + numRequestedResources: number; + selectedResourceRequestRoles: string[]; + dryRunResponse: AccessRequest; + maxDuration: Option; + Header?: () => JSX.Element; + }; type SuccessComponentParams = { reset: () => void; diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap index a20ef093d350d..12be5f63a98f6 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap @@ -928,7 +928,7 @@ exports[`failed state 1`] = ` - app + Application app-name @@ -954,7 +954,7 @@ exports[`failed state 1`] = ` - db + Database app-name @@ -980,7 +980,7 @@ exports[`failed state 1`] = ` - kube_cluster + Kubernetes kube-name @@ -1006,7 +1006,7 @@ exports[`failed state 1`] = ` - user_group + User Group user-group-name @@ -1032,7 +1032,7 @@ exports[`failed state 1`] = ` - windows_desktop + Desktop desktop-name @@ -2394,7 +2394,7 @@ exports[`loaded state 1`] = ` - app + Application app-name @@ -2420,7 +2420,7 @@ exports[`loaded state 1`] = ` - db + Database app-name @@ -2446,7 +2446,7 @@ exports[`loaded state 1`] = ` - kube_cluster + Kubernetes kube-name @@ -2472,7 +2472,7 @@ exports[`loaded state 1`] = ` - user_group + User Group user-group-name @@ -2498,7 +2498,7 @@ exports[`loaded state 1`] = ` - windows_desktop + Desktop desktop-name diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/index.ts b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/index.ts index 4b3c7ba47f9db..95712e2755a15 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/index.ts +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/index.ts @@ -17,7 +17,7 @@ */ export { RequestCheckoutWithSlider, RequestCheckout } from './RequestCheckout'; -export type { RequestCheckoutProps } from './RequestCheckout'; +export type { RequestCheckoutProps, PendingListItem } from './RequestCheckout'; export * from './utils'; export type { ReviewerOption } from './types'; diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx index 14f468c5778e5..5e2678e967e20 100644 --- a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx +++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx @@ -86,6 +86,7 @@ export function AccessRequestCheckout() { setSelectedResourceRequestRoles, clearCreateAttempt, data, + shouldShowClusterNameColumn, suggestedReviewers, selectedReviewers, setSelectedReviewers, @@ -138,7 +139,11 @@ export function AccessRequestCheckout() { {data .slice(0, MAX_RESOURCES_IN_BAR_TO_SHOW) .map(c => { - let resource = { name: c.name, Icon: undefined }; + let resource = { + name: c.name, + key: `${c.clusterName}-${c.kind}-${c.id}`, + Icon: undefined, + }; switch (c.kind) { case 'app': resource.Icon = Icon.Application; @@ -155,14 +160,14 @@ export function AccessRequestCheckout() { case 'role': break; default: - c.kind satisfies never; + c satisfies never; } return resource; }) .map(c => (