From 96b8dcf5fb498252250f409c0de9d5cf6c26cc91 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Thu, 1 Feb 2024 20:12:21 -0800 Subject: [PATCH 1/2] Converts `AuthenticatedWebsocket` into drop-in replacement for `WebSocket` that automatically goes through Teleport's custom authentication process before facilitating any caller-defined communication. This also reverts previous-`WebSocket` users to their original state (sans the code for passing the bearer token in the query string), swapping in `AuthenticatedWebsocket` in place of `WebSocket`. --- .../src/Assist/context/AssistContext.tsx | 47 ++- .../TerminalAssist/TerminalAssistContext.tsx | 20 +- .../src/lib/AuthenticatedWebsoscket.ts | 316 +++++++++++++----- web/packages/teleport/src/lib/tdp/client.ts | 36 +- web/packages/teleport/src/lib/term/tty.ts | 20 +- web/packages/teleport/src/types.ts | 2 +- 6 files changed, 293 insertions(+), 148 deletions(-) diff --git a/web/packages/teleport/src/Assist/context/AssistContext.tsx b/web/packages/teleport/src/Assist/context/AssistContext.tsx index 23bcd53fcf8a7..8a136c41d7636 100644 --- a/web/packages/teleport/src/Assist/context/AssistContext.tsx +++ b/web/packages/teleport/src/Assist/context/AssistContext.tsx @@ -31,9 +31,7 @@ import { AssistStateActionType, reducer } from 'teleport/Assist/context/state'; import { convertServerMessages } from 'teleport/Assist/context/utils'; import useStickyClusterId from 'teleport/useStickyClusterId'; import cfg from 'teleport/config'; -import { getAccessToken, getHostName } from 'teleport/services/api'; - -import { WebsocketStatus } from 'teleport/types'; +import { getHostName } from 'teleport/services/api'; import { AccessRequestClientMessage, @@ -50,6 +48,7 @@ import { makeMfaAuthenticateChallenge, WebauthnAssertionResponse, } from 'teleport/services/auth'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; import * as service from '../service'; import { @@ -65,7 +64,6 @@ import type { ServerMessage, } from 'teleport/Assist/types'; import type { AssistState } from 'teleport/Assist/context/state'; -import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; interface AssistContextValue { cancelMfaChallenge: () => void; @@ -127,7 +125,13 @@ export function AssistContextProvider(props: PropsWithChildren) { } function setupWebSocket(conversationId: string, initialMessage?: string) { - + activeWebSocket.current = new AuthenticatedWebSocket( + cfg.getAssistConversationWebSocketUrl( + getHostName(), + clusterId, + conversationId + ) + ); window.clearTimeout(refreshWebSocketTimeout.current); @@ -137,21 +141,22 @@ export function AssistContextProvider(props: PropsWithChildren) { TEN_MINUTES * 0.8 ); - const onopen = () => { + activeWebSocket.current.onopen = () => { if (initialMessage) { activeWebSocket.current.send(initialMessage); } - } + }; - const onclose = () => { + activeWebSocket.current.onclose = () => { dispatch({ type: AssistStateActionType.SetStreaming, streaming: false, }); }; - const onmessage = event => { + activeWebSocket.current.onmessage = async event => { const data = JSON.parse(event.data) as ServerMessage; + switch (data.type) { case ServerMessageType.Assist: dispatch({ @@ -245,14 +250,6 @@ export function AssistContextProvider(props: PropsWithChildren) { break; } }; - - activeWebSocket.current = new AuthenticatedWebSocket( - cfg.getAssistConversationWebSocketUrl( - getHostName(), - clusterId, - conversationId - ), onopen, onmessage, null, onclose - ); } async function createConversation() { @@ -353,7 +350,7 @@ export function AssistContextProvider(props: PropsWithChildren) { if ( !activeWebSocket.current || - activeWebSocket.current.readyState === WebSocket.CLOSED + activeWebSocket.current.readyState === AuthenticatedWebSocket.CLOSED ) { setupWebSocket(state.conversations.selectedId, data); } else { @@ -383,7 +380,8 @@ export function AssistContextProvider(props: PropsWithChildren) { function sendMfaChallenge(data: WebauthnAssertionResponse) { if ( !executeCommandWebSocket.current || - executeCommandWebSocket.current.readyState !== WebSocket.OPEN || + executeCommandWebSocket.current.readyState !== + AuthenticatedWebSocket.OPEN || !data ) { console.warn( @@ -455,8 +453,10 @@ export function AssistContextProvider(props: PropsWithChildren) { ); const proto = new Protobuf(); - const onmessage = (event: MessageEvent) => { - executeCommandWebSocket.current.binaryType = 'arraybuffer'; + executeCommandWebSocket.current = new AuthenticatedWebSocket(url); + executeCommandWebSocket.current.binaryType = 'arraybuffer'; + + executeCommandWebSocket.current.onmessage = event => { const uintArray = new Uint8Array(event.data); const msg = proto.decode(uintArray); @@ -533,8 +533,9 @@ export function AssistContextProvider(props: PropsWithChildren) { } }; - const onclose = () => { + executeCommandWebSocket.current.onclose = () => { executeCommandWebSocket.current = null; + // If the execution failed, we won't get a SESSION_END message, so we // need to mark all the results as finished here. for (const nodeId of nodeIdToResultId.keys()) { @@ -546,8 +547,6 @@ export function AssistContextProvider(props: PropsWithChildren) { } nodeIdToResultId.clear(); }; - - executeCommandWebSocket.current = new AuthenticatedWebSocket(url, null, onmessage, null, onclose); } async function deleteConversation(conversationId: string) { diff --git a/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx b/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx index 9820351446396..6dd92409aeeef 100644 --- a/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx +++ b/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx @@ -26,7 +26,7 @@ import React, { } from 'react'; import { Author, ServerMessage } from 'teleport/Assist/types'; -import { getAccessToken, getHostName } from 'teleport/services/api'; +import { getHostName } from 'teleport/services/api'; import useStickyClusterId from 'teleport/useStickyClusterId'; import cfg from 'teleport/config'; import { @@ -72,7 +72,9 @@ export function TerminalAssistContextProvider( const [messages, setMessages] = useState([]); useEffect(() => { - let onmessage = (e: MessageEvent) => { + socketRef.current = new AuthenticatedWebSocket(socketUrl); + + socketRef.current.onmessage = e => { const data = JSON.parse(e.data) as ServerMessage; const payload = JSON.parse(data.payload) as { action: string; @@ -93,8 +95,6 @@ export function TerminalAssistContextProvider( setLoading(false); setMessages(m => [message, ...m]); }; - - socketRef.current = new AuthenticatedWebSocket(socketUrl, null, onmessage); }, []); function close() { @@ -120,14 +120,15 @@ export function TerminalAssistContextProvider( 'ssh-explain' ); + const ws = new AuthenticatedWebSocket(socketUrl); - - let onopen = () => { - ws.send(encodedOutput); + ws.onopen = () => { + ws.send(encodedOutput); }; - let onmessage = (event: MessageEvent) => { - const msg = JSON.parse(event.data) as ServerMessage; + ws.onmessage = event => { + const message = event.data; + const msg = JSON.parse(message) as ServerMessage; const explanation: ExplanationMessage = { author: Author.Teleport, @@ -140,7 +141,6 @@ export function TerminalAssistContextProvider( ws.close(); }; - const ws = new AuthenticatedWebSocket(socketUrl, onopen, onmessage); } function send(message: string) { diff --git a/web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts b/web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts index 8f717e7ddf3c9..7986a9aae1ea1 100644 --- a/web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts +++ b/web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts @@ -1,113 +1,261 @@ +import { getAccessToken } from 'teleport/services/api'; +import { WebsocketStatus } from 'teleport/types'; + /** - * 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 . + * `AuthenticatedWebSocket` is a drop-in replacement for + * the `WebSocket` class that handles Teleport's websocket + * authentication process. */ +export class AuthenticatedWebSocket extends WebSocket { + private authenticated: boolean = false; + private openListeners: ((this: WebSocket, ev: Event) => any)[] = []; + private onopenInternal: ((this: WebSocket, ev: Event) => any) | null = null; + private messageListeners: ((this: WebSocket, ev: MessageEvent) => any)[] = []; + private onmessageInternal: + | ((this: WebSocket, ev: MessageEvent) => any) + | null = null; + private oncloseListeners: ((this: WebSocket, ev: CloseEvent) => any)[] = []; + private oncloseInternal: ((this: WebSocket, ev: CloseEvent) => any) | null = + null; + private onerrorListeners: ((this: WebSocket, ev: Event) => any)[] = []; + private onerrorInternal: ((this: WebSocket, ev: Event) => any) | null = null; + private binaryTypeInternal: BinaryType = 'blob'; // Default binaryType + private onopenEvent: Event | null = null; -import { getAccessToken } from 'teleport/services/api'; -import { WebsocketStatus } from 'teleport/types'; + constructor(url: string | URL, protocols?: string | string[]) { + super(url, protocols); + // Set the binaryType to 'arraybuffer' to handle the authentication process. + super.binaryType = 'arraybuffer'; -export class AuthenticatedWebSocket { - ws: WebSocket | undefined; + // The open event listener should immediately send the authentication token + super.onopen = (onopenEvent: Event) => { + super.send(JSON.stringify({ token: getAccessToken() })); + // Don't call the user defined onopen messages yet, wait for the authentication response. + this.onopenEvent = onopenEvent; + }; - onopenAfterAuth: (ev: Event) => void | undefined; - onmessageAfterAuth: (ev: MessageEvent) => void | undefined; - oncloseAfterAuth: (ev: CloseEvent) => void | undefined; + // The message event listener should handle the authentication response, + // and if it succeeds, set the binaryType to the user-defined value and + // trigger any user-added open listeners. + super.onmessage = (ev: MessageEvent) => { + // If not yet authenticated, handle the authentication response. + if (!this.authenticated) { + // Parse the message as a WebsocketStatus. + let authResponse: WebsocketStatus; + try { + authResponse = JSON.parse(ev.data) as WebsocketStatus; + } catch (e) { + this.triggerError('Error parsing JSON from websocket message: ' + e); + return; + } - private authenticated: boolean; + // Validate the WebsocketStatus. + if ( + !authResponse.type || + !authResponse.status || + !(authResponse.type === 'create_session_response') || + !(authResponse.status === 'ok' || authResponse.status === 'error') + ) { + this.triggerError( + 'Invalid auth response: ' + JSON.stringify(authResponse) + ); + return; + } - constructor( - socketAddr: string, - onopen: (ev: Event) => void | null = null, - onmessage: (ev: MessageEvent) => void | null = null, - onerror: (ev: Event) => void | null = null, - onclose: (ev: CloseEvent) => void | null = null, - ) { - this.onopen = this.onopen.bind(this); - this.onmessage = this.onmessage.bind(this); - this.onclose = this.onclose.bind(this); - - this.authenticated = false; - this.onmessageAfterAuth = onmessage; - this.onopenAfterAuth = onopen; - this.oncloseAfterAuth = onclose; - - this.ws = new WebSocket(socketAddr); - this.ws.binaryType = 'arraybuffer'; - - this.ws.onopen = this.onopen; - this.ws.onmessage = this.onmessage; - this.ws.onerror = onerror; - this.ws.onclose = this.onclose; + // Authentication succeeded. + if (authResponse.status === 'ok') { + this.authenticated = true; + // Set the binaryType to the value set by the user (or back to the default 'blob'). + super.binaryType = this.binaryTypeInternal; + // Now that authentication is complete, trigger any user-added open listeners + // with the original onopen event. + this.openListeners.forEach(listener => + listener.call(this, this.onopenEvent) + ); + this.onopenInternal?.call(this, this.onopenEvent); + return; + } else { + // Authentication failed, authResponse.status === 'error'. + this.triggerError( + 'auth error connecting to websocket: ' + authResponse.message + ); + return; + } + } else { + // If authenticated, pass messages to user-added listeners. + this.messageListeners.forEach(listener => { + listener.call(this, ev); + }); + this.onmessageInternal?.call(this, ev); + } + }; + + // Set the 'close' event for cleanup. + super.onclose = (ev: CloseEvent) => { + // Trigger any user-added close listeners + this.oncloseListeners.forEach(listener => listener.call(this, ev)); + this.oncloseInternal?.call(this, ev); + this.authenticated = false; + }; + + // Set the 'error' event for cleanup. + super.onerror = (ev: Event) => { + // Trigger any user-added error listeners + this.onerrorListeners.forEach(listener => listener.call(this, ev)); + this.onerrorInternal?.call(this, ev); + this.authenticated = false; + }; } - set binaryType(btype: BinaryType) { - this.ws.binaryType = btype + // Authenticated send + override send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void { + if (!this.authenticated) { + // This should be unreachable, but just in case. + this.triggerError( + 'Cannot send data before authentication is complete. Data: ' + data + ); + return; + } + super.send(data); } - get readyState(): number { - return this.ws.readyState + // Override addEventListener to intercept these listeners and store them in + // our appropriate arrays. They are called in the appropriate places in the + // `onopen`, `onmessage`, `onclose`, and `onerror` methods set in the constructor. + override addEventListener( + type: K, + listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any + ): void { + if (type === 'open') { + this.openListeners.push( + listener as (this: WebSocket, ev: WebSocketEventMap['open']) => any + ); + } else if (type === 'message') { + this.messageListeners.push( + listener as (this: WebSocket, ev: WebSocketEventMap['message']) => any + ); + } else if (type === 'close') { + this.oncloseListeners.push( + listener as (this: WebSocket, ev: WebSocketEventMap['close']) => any + ); + } else if (type === 'error') { + this.onerrorListeners.push( + listener as (this: WebSocket, ev: WebSocketEventMap['error']) => any + ); + } else { + // This should be unreachable, but just in case. + super.addEventListener(type, listener); + } } - private onopen(): void { - this.ws.send(JSON.stringify({ token: getAccessToken() })); + // Override the onopen, onmessage, onclose, and onerror properties to store the user-defined + // listeners in the appropriate internal properties. These are called in the appropriate places + // in the `onopen`, `onmessage`, `onclose`, and `onerror` methods set in the constructor. + + override set onopen(listener: (this: WebSocket, ev: Event) => any | null) { + this.onopenInternal = listener; } - private onmessage(ev: MessageEvent): void { - if (!this.authenticated) { - const authResponse = JSON.parse(ev.data) as WebsocketStatus; - if (authResponse.type != 'create_session_response') { - this.ws.close(); - console.log('invalid auth response type: ' + authResponse.message); - return; - } + override get onopen(): ((this: WebSocket, ev: Event) => any) | null { + return this.onopenInternal; + } - if (authResponse.status == 'error') { - this.ws.close(); - console.log( - 'auth error connecting to websocket: ' + authResponse.message - ); - return; - } - this.authenticated = true; + override set onmessage( + listener: ((this: WebSocket, ev: MessageEvent) => any) | null + ) { + this.onmessageInternal = listener; + } - if (this.onopenAfterAuth) { - this.onopenAfterAuth(ev); - } + override get onmessage(): + | ((this: WebSocket, ev: MessageEvent) => any) + | null { + return this.onmessageInternal; + } + + override set onclose( + listener: ((this: WebSocket, ev: CloseEvent) => any) | null + ) { + this.oncloseInternal = listener; + } + override get onclose(): ((this: WebSocket, ev: CloseEvent) => any) | null { + return this.oncloseInternal; + } + + override set onerror(listener: ((this: WebSocket, ev: Event) => any) | null) { + this.onerrorInternal = listener; + } + + override get onerror(): ((this: WebSocket, ev: Event) => any) | null { + return this.onerrorInternal; + } + + // Override the binaryType property to store the user-defined binaryType in the appropriate internal property. + // This is because we need to set the binaryType to 'arraybuffer' for the authentication process (see constructor), + // and only then can we set it to the user-defined value. + override set binaryType(binaryType: BinaryType) { + if (this.authenticated) { + super.binaryType = binaryType; return; } - if (this.onmessageAfterAuth) { - this.onmessageAfterAuth(ev); - } + this.binaryTypeInternal = binaryType; } - private onclose(ev: CloseEvent): void { - if (this.oncloseAfterAuth) { - this.oncloseAfterAuth(ev); - } - this.authenticated = false; - this.ws = null; + override get binaryType(): BinaryType { + return this.binaryTypeInternal; } - send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void { - this.ws.send(data); + // Override removeEventListener to support listeners removal for 'open', 'message', and 'close' events + override removeEventListener( + type: K, + listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any + ): void { + if (type === 'open') { + const index = this.openListeners.indexOf( + listener as (this: WebSocket, ev: WebSocketEventMap['open']) => any + ); + if (index !== -1) { + this.openListeners.splice(index, 1); + } + } else if (type === 'message') { + const index = this.messageListeners.indexOf( + listener as (this: WebSocket, ev: WebSocketEventMap['message']) => any + ); + if (index !== -1) { + this.messageListeners.splice(index, 1); + } + } else if (type === 'close') { + const index = this.oncloseListeners.indexOf( + listener as (this: WebSocket, ev: WebSocketEventMap['close']) => any + ); + if (index !== -1) { + this.oncloseListeners.splice(index, 1); + } + } else if (type === 'error') { + const index = this.onerrorListeners.indexOf( + listener as (this: WebSocket, ev: WebSocketEventMap['error']) => any + ); + if (index !== -1) { + this.onerrorListeners.splice(index, 1); + } + } else { + // This should be unreachable, but just in case. + super.removeEventListener( + type, + listener as EventListenerOrEventListenerObject + ); + } } - close(code?: number, reason?: string): void { - this.ws.close(code, reason); + // Method to manually trigger an error event. + private triggerError(errorMessage: string): void { + const errorEvent = new ErrorEvent('error', { + error: new Error(errorMessage), + message: errorMessage, + }); + + // Dispatch the event to trigger all listeners attached for 'error' events. + this.dispatchEvent(errorEvent); } } diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index 44df2ccb8f563..5d98c3803abfd 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -25,8 +25,7 @@ import init, { import { WebsocketCloseCode, TermEvent } from 'teleport/lib/term/enums'; import { EventEmitterWebAuthnSender } from 'teleport/lib/EventEmitterWebAuthnSender'; - -import { AuthenticatedWebSocket } from '../AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; import Codec, { MessageType, @@ -116,37 +115,36 @@ export default class Client extends EventEmitterWebAuthnSender { async connect(spec?: ClientScreenSpec) { await this.initWasm(); - let onopen = () => { - this.logger.info('websocket is open'); + this.socket = new AuthenticatedWebSocket(this.socketAddr); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = () => { + this.logger.info('websocket is open'); this.emit(TdpClientEvent.WS_OPEN); if (spec) { this.sendClientScreenSpec(spec); } }; - let onmessage = async (ev: MessageEvent) => { + this.socket.onmessage = async (ev: MessageEvent) => { await this.processMessage(ev.data as ArrayBuffer); }; // The socket 'error' event will only ever be emitted by the socket // prior to a socket 'close' event (https://stackoverflow.com/a/40084550/6277051). // Therefore, we can rely on our onclose handler to account for any websocket errors. - let onerror = null; - let onclose = () => { + this.socket.onerror = null; + this.socket.onclose = () => { this.logger.info('websocket is closed'); + // Clean up all of our socket's listeners and the socket itself. + this.socket.onopen = null; + this.socket.onmessage = null; + this.socket.onclose = null; this.socket = null; + this.emit(TdpClientEvent.WS_CLOSE); }; - - this.socket = new AuthenticatedWebSocket( - this.socketAddr, - onopen, - onmessage, - onerror, - onclose - ); } private async initWasm() { @@ -565,9 +563,9 @@ export default class Client extends EventEmitterWebAuthnSender { protected send( data: string | ArrayBufferLike | Blob | ArrayBufferView ): void { - if (this.socket && this.socket.ws.readyState === 1) { + if (this.socket && this.socket.readyState === 1) { try { - this.socket.ws.send(data); + this.socket.send(data); } catch (e) { this.handleError(e, TdpClientEvent.CLIENT_ERROR); } @@ -690,7 +688,7 @@ export default class Client extends EventEmitterWebAuthnSender { ) { this.logger.error(err); this.emit(errType, err); - this.socket?.ws?.close(); + this.socket?.close(); } // Emits an warnType event @@ -709,7 +707,7 @@ export default class Client extends EventEmitterWebAuthnSender { // will simply do nothing. shutdown(closeCode = WebsocketCloseCode.NORMAL) { this.removeAllListeners(); - this.socket?.ws?.close(closeCode); + this.socket?.close(closeCode); } } diff --git a/web/packages/teleport/src/lib/term/tty.ts b/web/packages/teleport/src/lib/term/tty.ts index 39ac5e5da6dde..3acf861c675c7 100644 --- a/web/packages/teleport/src/lib/term/tty.ts +++ b/web/packages/teleport/src/lib/term/tty.ts @@ -20,8 +20,7 @@ import Logger from 'shared/libs/logger'; import { EventEmitterWebAuthnSender } from 'teleport/lib/EventEmitterWebAuthnSender'; import { WebauthnAssertionResponse } from 'teleport/services/auth'; - -import { AuthenticatedWebSocket } from '../AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; import { EventType, TermEvent, WebsocketCloseCode } from './enums'; import { Protobuf, MessageTypeEnum } from './protobuf'; @@ -33,7 +32,7 @@ const defaultOptions = { }; class Tty extends EventEmitterWebAuthnSender { - socket: AuthenticatedWebSocket = null; + socket = null; _buffered = true; _attachSocketBufferTimer; @@ -64,13 +63,11 @@ class Tty extends EventEmitterWebAuthnSender { connect(w: number, h: number) { const connStr = this._addressResolver.getConnStr(w, h); - this.socket = new AuthenticatedWebSocket( - connStr, - this._onOpenConnection, - this._onMessage, - null, - this._onCloseConnection - ); + this.socket = new AuthenticatedWebSocket(connStr); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = this._onOpenConnection; + this.socket.onmessage = this._onMessage; + this.socket.onclose = this._onCloseConnection; } send(data) { @@ -173,6 +170,9 @@ class Tty extends EventEmitterWebAuthnSender { } _onCloseConnection(e) { + this.socket.onopen = null; + this.socket.onmessage = null; + this.socket.onclose = null; this.socket = null; this.emit(TermEvent.CONN_CLOSE, e); logger.info('websocket is closed'); diff --git a/web/packages/teleport/src/types.ts b/web/packages/teleport/src/types.ts index 02c52361d9791..144db28946953 100644 --- a/web/packages/teleport/src/types.ts +++ b/web/packages/teleport/src/types.ts @@ -203,5 +203,5 @@ export enum RecommendationStatus { export type WebsocketStatus = { type: string; status: string; - message: string; + message?: string; }; From 36ee1fd7e61aa86193b9ef37dbf12961cb02632f Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Mon, 5 Feb 2024 10:36:32 -0800 Subject: [PATCH 2/2] fix file name --- web/packages/teleport/src/Assist/context/AssistContext.tsx | 2 +- .../DocumentSsh/TerminalAssist/TerminalAssistContext.tsx | 2 +- .../{AuthenticatedWebsoscket.ts => AuthenticatedWebSocket.ts} | 0 web/packages/teleport/src/lib/tdp/client.ts | 2 +- web/packages/teleport/src/lib/term/tty.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename web/packages/teleport/src/lib/{AuthenticatedWebsoscket.ts => AuthenticatedWebSocket.ts} (100%) diff --git a/web/packages/teleport/src/Assist/context/AssistContext.tsx b/web/packages/teleport/src/Assist/context/AssistContext.tsx index 8a136c41d7636..fb9319285701d 100644 --- a/web/packages/teleport/src/Assist/context/AssistContext.tsx +++ b/web/packages/teleport/src/Assist/context/AssistContext.tsx @@ -48,7 +48,7 @@ import { makeMfaAuthenticateChallenge, WebauthnAssertionResponse, } from 'teleport/services/auth'; -import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebSocket'; import * as service from '../service'; import { diff --git a/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx b/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx index 6dd92409aeeef..ebdec9bfcf4dc 100644 --- a/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx +++ b/web/packages/teleport/src/Console/DocumentSsh/TerminalAssist/TerminalAssistContext.tsx @@ -36,7 +36,7 @@ import { SuggestedCommandMessage, UserMessage, } from 'teleport/Console/DocumentSsh/TerminalAssist/types'; -import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebSocket'; interface TerminalAssistContextValue { close: () => void; diff --git a/web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts b/web/packages/teleport/src/lib/AuthenticatedWebSocket.ts similarity index 100% rename from web/packages/teleport/src/lib/AuthenticatedWebsoscket.ts rename to web/packages/teleport/src/lib/AuthenticatedWebSocket.ts diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index 5d98c3803abfd..0dcbc8077cde8 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -25,7 +25,7 @@ import init, { import { WebsocketCloseCode, TermEvent } from 'teleport/lib/term/enums'; import { EventEmitterWebAuthnSender } from 'teleport/lib/EventEmitterWebAuthnSender'; -import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebSocket'; import Codec, { MessageType, diff --git a/web/packages/teleport/src/lib/term/tty.ts b/web/packages/teleport/src/lib/term/tty.ts index 3acf861c675c7..fe45eb930d65a 100644 --- a/web/packages/teleport/src/lib/term/tty.ts +++ b/web/packages/teleport/src/lib/term/tty.ts @@ -20,7 +20,7 @@ import Logger from 'shared/libs/logger'; import { EventEmitterWebAuthnSender } from 'teleport/lib/EventEmitterWebAuthnSender'; import { WebauthnAssertionResponse } from 'teleport/services/auth'; -import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebsoscket'; +import { AuthenticatedWebSocket } from 'teleport/lib/AuthenticatedWebSocket'; import { EventType, TermEvent, WebsocketCloseCode } from './enums'; import { Protobuf, MessageTypeEnum } from './protobuf';