From a64a09fea88bf1f1146e0bc755e11c87dc72f9bf Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Wed, 4 Sep 2024 14:58:58 -0600 Subject: [PATCH] Clean up desktop access alerts (part 1) (#46201) Rename the TDP notification message to "alert" to avoid confusing them with the notifications introduced with Teleport 16. Update the desktop session storybook to allow adding alerts of different severity levels, which uncovered a bug where multiple alerts stack horizontally instead of vertically (this has been fixed). --- lib/srv/desktop/rdp/rdpclient/client.go | 14 ++++--- lib/srv/desktop/tdp/conn.go | 2 +- lib/srv/desktop/tdp/proto.go | 31 +++++++-------- lib/srv/desktop/tdp/proto_test.go | 8 ++-- lib/srv/desktop/windows_server.go | 2 +- lib/web/apiserver_test.go | 4 +- lib/web/desktop.go | 10 ++--- rfd/0037-desktop-access-protocol.md | 6 +-- ...{WarningDropdown.tsx => AlertDropdown.tsx} | 38 +++++++++---------- .../DesktopSession/DesktopSession.story.tsx | 37 ++++++++++++------ .../src/DesktopSession/DesktopSession.tsx | 8 ++-- .../teleport/src/DesktopSession/TopBar.tsx | 15 +++----- .../src/DesktopSession/useDesktopSession.tsx | 16 ++++---- .../src/DesktopSession/useTdpClientCanvas.tsx | 8 ++-- web/packages/teleport/src/lib/tdp/client.ts | 22 +++++------ web/packages/teleport/src/lib/tdp/codec.ts | 8 ++-- 16 files changed, 120 insertions(+), 109 deletions(-) rename web/packages/teleport/src/DesktopSession/{WarningDropdown.tsx => AlertDropdown.tsx} (80%) diff --git a/lib/srv/desktop/rdp/rdpclient/client.go b/lib/srv/desktop/rdp/rdpclient/client.go index 55d5ab858517e..ff11f641a756b 100644 --- a/lib/srv/desktop/rdp/rdpclient/client.go +++ b/lib/srv/desktop/rdp/rdpclient/client.go @@ -268,7 +268,7 @@ func (c *Client) readClientSize() error { "screen size of %d x %d is greater than the maximum allowed by RDP (%d x %d)", s.Width, s.Height, types.MaxRDPScreenWidth, types.MaxRDPScreenHeight, ) - if err := c.sendTDPNotification(err.Error(), tdp.SeverityError); err != nil { + if err := c.sendTDPAlert(err.Error(), tdp.SeverityError); err != nil { return trace.Wrap(err) } return trace.Wrap(err) @@ -278,8 +278,8 @@ func (c *Client) readClientSize() error { } } -func (c *Client) sendTDPNotification(message string, severity tdp.Severity) error { - return c.cfg.Conn.WriteMessage(tdp.Notification{Message: message, Severity: severity}) +func (c *Client) sendTDPAlert(message string, severity tdp.Severity) error { + return c.cfg.Conn.WriteMessage(tdp.Alert{Message: message, Severity: severity}) } func (c *Client) startRustRDP(ctx context.Context) error { @@ -350,7 +350,7 @@ func (c *Client) startRustRDP(ctx context.Context) error { defer C.free_string(res.message) } - // If the client exited with an error, send a tdp error notification and return it. + // If the client exited with an error, send a TDP notification and return it. if res.err_code != C.ErrCodeSuccess { var err error @@ -360,7 +360,7 @@ func (c *Client) startRustRDP(ctx context.Context) error { err = trace.Errorf("RDP client exited with an unknown error") } - c.sendTDPNotification(err.Error(), tdp.SeverityError) + c.sendTDPAlert(err.Error(), tdp.SeverityError) return err } @@ -371,7 +371,9 @@ func (c *Client) startRustRDP(ctx context.Context) error { } c.cfg.Logger.InfoContext(ctx, message) - c.sendTDPNotification(message, tdp.SeverityInfo) + + // TODO(zmb3): convert this to severity error and ensure it renders in the UI + c.sendTDPAlert(message, tdp.SeverityInfo) return nil } diff --git a/lib/srv/desktop/tdp/conn.go b/lib/srv/desktop/tdp/conn.go index 3d0f7a0bb8a1c..dd4c30ae8e0d6 100644 --- a/lib/srv/desktop/tdp/conn.go +++ b/lib/srv/desktop/tdp/conn.go @@ -128,7 +128,7 @@ func (c *Conn) ReadClientScreenSpec() (*ClientScreenSpec, error) { // SendNotification is a convenience function for sending a Notification message. func (c *Conn) SendNotification(message string, severity Severity) error { - return c.WriteMessage(Notification{Message: message, Severity: severity}) + return c.WriteMessage(Alert{Message: message, Severity: severity}) } // LocalAddr returns local address diff --git a/lib/srv/desktop/tdp/proto.go b/lib/srv/desktop/tdp/proto.go index 332f2837f9635..c7e063b1f8e7c 100644 --- a/lib/srv/desktop/tdp/proto.go +++ b/lib/srv/desktop/tdp/proto.go @@ -75,7 +75,7 @@ const ( TypeSharedDirectoryListRequest = MessageType(25) TypeSharedDirectoryListResponse = MessageType(26) TypePNG2Frame = MessageType(27) - TypeNotification = MessageType(28) + TypeAlert = MessageType(28) TypeRDPFastPathPDU = MessageType(29) TypeRDPResponsePDU = MessageType(30) TypeRDPConnectionInitialized = MessageType(31) @@ -142,8 +142,8 @@ func decodeMessage(firstByte byte, in byteReader) (Message, error) { return decodeClipboardData(in, maxClipboardDataLength) case TypeError: return decodeError(in) - case TypeNotification: - return decodeNotification(in) + case TypeAlert: + return decodeAlert(in) case TypeMFA: return DecodeMFA(in) case TypeSharedDirectoryAnnounce: @@ -571,7 +571,7 @@ func (m Error) Encode() ([]byte, error) { } func decodeError(in io.Reader) (Error, error) { - message, err := decodeString(in, tdpMaxNotificationMessageLength) + message, err := decodeString(in, tdpMaxAlertMessageLength) if err != nil { return Error{}, trace.Wrap(err) } @@ -586,18 +586,19 @@ const ( SeverityError Severity = 2 ) -// Notification is an informational message sent from Teleport +// Alert is an informational message sent from Teleport // to the Web UI. It can be used for fatal errors or non-fatal // warnings. +// // | message type (28) | message_length uint32 | message []byte | severity byte | -type Notification struct { +type Alert struct { Message string Severity Severity } -func (m Notification) Encode() ([]byte, error) { +func (m Alert) Encode() ([]byte, error) { buf := new(bytes.Buffer) - buf.WriteByte(byte(TypeNotification)) + buf.WriteByte(byte(TypeAlert)) if err := encodeString(buf, m.Message); err != nil { return nil, trace.Wrap(err) } @@ -605,16 +606,16 @@ func (m Notification) Encode() ([]byte, error) { return buf.Bytes(), nil } -func decodeNotification(in byteReader) (Notification, error) { - message, err := decodeString(in, tdpMaxNotificationMessageLength) +func decodeAlert(in byteReader) (Alert, error) { + message, err := decodeString(in, tdpMaxAlertMessageLength) if err != nil { - return Notification{}, trace.Wrap(err) + return Alert{}, trace.Wrap(err) } severity, err := in.ReadByte() if err != nil { - return Notification{}, trace.Wrap(err) + return Alert{}, trace.Wrap(err) } - return Notification{Message: message, Severity: Severity(severity)}, nil + return Alert{Message: message, Severity: Severity(severity)}, nil } // MouseWheelAxis identifies a scroll axis on the mouse wheel. @@ -1688,9 +1689,9 @@ func writeUint64(b *bytes.Buffer, v uint64) { } const ( - // tdpMaxNotificationMessageLength is somewhat arbitrary, as it is only sent *to* + // tdpMaxAlertMessageLength is somewhat arbitrary, as it is only sent *to* // the browser (Teleport never receives this message, so won't be decoding it) - tdpMaxNotificationMessageLength = 10240 + tdpMaxAlertMessageLength = 10240 // tdpMaxPathLength is somewhat arbitrary because we weren't able to determine // a precise value to set it to: https://github.com/gravitational/teleport/issues/14950#issuecomment-1341632465 diff --git a/lib/srv/desktop/tdp/proto_test.go b/lib/srv/desktop/tdp/proto_test.go index b483f82a42bcc..2967aadee8ec9 100644 --- a/lib/srv/desktop/tdp/proto_test.go +++ b/lib/srv/desktop/tdp/proto_test.go @@ -223,14 +223,14 @@ func TestSizeLimitsAreNonFatal(t *testing.T) { { name: "rejects long Error", msg: Error{ - Message: string(bytes.Repeat([]byte("a"), tdpMaxNotificationMessageLength+1)), + Message: string(bytes.Repeat([]byte("a"), tdpMaxAlertMessageLength+1)), }, fatal: false, }, { - name: "rejects long Notification", - msg: Notification{ - Message: string(bytes.Repeat([]byte("a"), tdpMaxNotificationMessageLength+1)), + name: "rejects long Alert", + msg: Alert{ + Message: string(bytes.Repeat([]byte("a"), tdpMaxAlertMessageLength+1)), }, fatal: false, }, diff --git a/lib/srv/desktop/windows_server.go b/lib/srv/desktop/windows_server.go index 119b89771566d..2c74a5dbd6361 100644 --- a/lib/srv/desktop/windows_server.go +++ b/lib/srv/desktop/windows_server.go @@ -981,7 +981,7 @@ func (s *WindowsService) makeTDPSendHandler( return func(m tdp.Message, b []byte) { switch b[0] { case byte(tdp.TypeRDPConnectionInitialized), byte(tdp.TypeRDPFastPathPDU), byte(tdp.TypePNG2Frame), - byte(tdp.TypePNGFrame), byte(tdp.TypeError), byte(tdp.TypeNotification): + byte(tdp.TypePNGFrame), byte(tdp.TypeError), byte(tdp.TypeAlert): e := &events.DesktopRecording{ Metadata: events.Metadata{ Type: libevents.DesktopRecordingEvent, diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 9dfd08b3e8357..2f346c21817d5 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -2290,7 +2290,7 @@ func (w *windowsDesktopServiceMock) handleConn(t *testing.T, conn *tls.Conn) { require.NoError(t, err) require.IsType(t, tdp.ClientScreenSpec{}, msg) - err = tdpConn.WriteMessage(tdp.Notification{Message: "test", Severity: tdp.SeverityWarning}) + err = tdpConn.WriteMessage(tdp.Alert{Message: "test", Severity: tdp.SeverityWarning}) require.NoError(t, err) } @@ -2364,7 +2364,7 @@ func TestDesktopAccessMFARequiresMfa(t *testing.T) { msg, err := tdpClient.ReadMessage() require.NoError(t, err) - require.IsType(t, tdp.Notification{}, msg) + require.IsType(t, tdp.Alert{}, msg) }) } } diff --git a/lib/web/desktop.go b/lib/web/desktop.go index 0e6273e57022e..6ad245c049013 100644 --- a/lib/web/desktop.go +++ b/lib/web/desktop.go @@ -99,7 +99,7 @@ func (h *Handler) createDesktopConnection( ctx := r.Context() sendTDPError := func(err error) error { - sendErr := sendTDPNotification(ws, err, tdp.SeverityError) + sendErr := sendTDPAlert(ws, err, tdp.SeverityError) if sendErr != nil { return sendErr } @@ -560,7 +560,7 @@ func proxyWebsocketConn(ws *websocket.Conn, wds net.Conn) error { if !isFatal { severity = tdp.SeverityWarning } - sendErr := sendTDPNotification(ws, err, severity) + sendErr := sendTDPAlert(ws, err, severity) // If the error wasn't fatal and we successfully // sent it back to the client, continue. @@ -753,10 +753,10 @@ func (h *Handler) desktopAccessScriptInstallADCSHandle(w http.ResponseWriter, r return nil, trace.Wrap(err) } -// sendTDPNotification sends a tdp Notification over the supplied websocket with the +// sendTDPAlert sends a TDP alert over the supplied websocket with the // error message of err. -func sendTDPNotification(ws *websocket.Conn, err error, severity tdp.Severity) error { - msg := tdp.Notification{Message: err.Error(), Severity: severity} +func sendTDPAlert(ws *websocket.Conn, err error, severity tdp.Severity) error { + msg := tdp.Alert{Message: err.Error(), Severity: severity} b, err := msg.Encode() if err != nil { return trace.Wrap(err) diff --git a/rfd/0037-desktop-access-protocol.md b/rfd/0037-desktop-access-protocol.md index 0c844a8cb7e61..49cd7807afa48 100644 --- a/rfd/0037-desktop-access-protocol.md +++ b/rfd/0037-desktop-access-protocol.md @@ -222,13 +222,13 @@ This message contains a mouse wheel update. Sent from client to server. This message indicates an error has occurred. -#### 28 - notification +#### 28 - alert ``` | message type (28) | message_length uint32 | message []byte | severity byte | ``` -This message sends a notification message with a severity level. Sent from server to client. +This message sends an alert message along with a severity level. Sent from server to client. `message_length` denotes the length of the `message` byte array. It doesn't include the `severity` byte. @@ -239,7 +239,7 @@ This message sends a notification message with a severity level. Sent from serve - `2` is for an error An error (`2`) means that some fatal problem was encountered and the TDP connection is ending imminently. -A notification with `severity == 2` should be preferred to the `error` message above. +An alert with `severity == 2` should be preferred to the `error` message above. A warning (`1`) means some non-fatal problem was encountered but the TDP connection can still continue. diff --git a/web/packages/teleport/src/DesktopSession/WarningDropdown.tsx b/web/packages/teleport/src/DesktopSession/AlertDropdown.tsx similarity index 80% rename from web/packages/teleport/src/DesktopSession/WarningDropdown.tsx rename to web/packages/teleport/src/DesktopSession/AlertDropdown.tsx index 1a702e48b2968..b00b18de95d6f 100644 --- a/web/packages/teleport/src/DesktopSession/WarningDropdown.tsx +++ b/web/packages/teleport/src/DesktopSession/AlertDropdown.tsx @@ -25,19 +25,19 @@ import { useClickOutside } from 'shared/hooks/useClickOutside'; import type { NotificationItem } from 'shared/components/Notification'; -export function WarningDropdown({ warnings, onRemoveWarning }: Props) { +export function AlertDropdown({ alerts, onRemoveAlert }: Props) { const [showDropdown, setShowDropdown] = useState(false); const ref = useRef(null); const theme = useTheme(); const toggleDropdown = () => { - if (warnings.length > 0) { + if (alerts.length > 0) { setShowDropdown(prevState => !prevState); } }; // Dropdown is always closed if there are no errors to show - if (warnings.length === 0 && showDropdown) setShowDropdown(false); + if (alerts.length === 0 && showDropdown) setShowDropdown(false); // Close the dropdown if it's open and the user clicks outside of it useClickOutside(ref, () => { @@ -51,8 +51,8 @@ export function WarningDropdown({ warnings, onRemoveWarning }: Props) { return ( <> 0} + title={'Alerts'} + hasAlerts={alerts.length > 0} px={2} onClick={toggleDropdown} > @@ -60,12 +60,10 @@ export function WarningDropdown({ warnings, onRemoveWarning }: Props) { alignItems="center" justifyContent="space-between" color={ - warnings.length - ? theme.colors.text.main - : theme.colors.text.disabled + alerts.length ? theme.colors.text.main : theme.colors.text.disabled } > - {warnings.length} + {alerts.length} {showDropdown && ( @@ -73,23 +71,23 @@ export function WarningDropdown({ warnings, onRemoveWarning }: Props) { mt={3} p={2} style={{ - maxHeight: window.innerHeight / 4, + maxHeight: window.innerHeight / 3, }} >

- {warnings.length} {warnings.length > 1 ? 'Warnings' : 'Warning'} + {alerts.length} {alerts.length > 1 ? 'Alerts' : 'Alert'}

- - {warnings.map(warning => ( + + {alerts.map(alert => ( onRemoveWarning(warning.id)} + key={alert.id} + item={alert} + onRemove={() => onRemoveAlert(alert.id)} Icon={Warning} getColor={theme => theme.colors.warning.main} isAutoRemovable={false} @@ -107,13 +105,13 @@ const StyledButton = styled(Button)` min-height: 0; height: ${({ theme }) => theme.fontSizes[7] + 'px'}; background-color: ${props => - props.hasWarnings + props.hasAlerts ? props.theme.colors.warning.main : props.theme.colors.spotBackground[1]}; &:hover, &:focus { background-color: ${props => - props.hasWarnings + props.hasAlerts ? props.theme.colors.warning.hover : props.theme.colors.spotBackground[2]}; } @@ -141,6 +139,6 @@ const StyledOverflow = styled(Flex)` `; type Props = { - warnings: NotificationItem[]; - onRemoveWarning: (id: string) => void; + alerts: NotificationItem[]; + onRemoveAlert: (id: string) => void; }; diff --git a/web/packages/teleport/src/DesktopSession/DesktopSession.story.tsx b/web/packages/teleport/src/DesktopSession/DesktopSession.story.tsx index a6406dc6eb5fb..c81004fd3615f 100644 --- a/web/packages/teleport/src/DesktopSession/DesktopSession.story.tsx +++ b/web/packages/teleport/src/DesktopSession/DesktopSession.story.tsx @@ -90,8 +90,8 @@ const props: State = { }, showAnotherSessionActiveDialog: false, setShowAnotherSessionActiveDialog: () => {}, - warnings: [], - onRemoveWarning: () => {}, + alerts: [], + onRemoveAlert: () => {}, windowOnResize: throttle(() => {}, 1000), }; @@ -314,35 +314,48 @@ export const ClipboardSharingDisabledBrowserPermissions = () => ( /> ); -export const Warnings = () => { +export const Alerts = () => { const client = fakeClient(); client.connect = async () => { client.emit(TdpClientEvent.TDP_PNG_FRAME); }; - const [warnings, setWarnings] = useState([]); + const [alerts, setAlerts] = useState([]); const addWarning = () => { - setWarnings(prevItems => [ + setAlerts(prevItems => [ ...prevItems, { id: crypto.randomUUID(), severity: 'warn', - content: - "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.", + content: `This is a warning message at ${new Date().toLocaleTimeString()}`, }, ]); }; - const removeWarning = (id: string) => { - setWarnings(prevState => prevState.filter(warning => warning.id !== id)); + const addInfo = () => { + setAlerts(prevItems => [ + ...prevItems, + { + id: crypto.randomUUID(), + severity: 'info', + content: `This is an info message at ${new Date().toLocaleTimeString()}`, + }, + ]); + }; + + const removeAlert = (id: string) => { + setAlerts(prevState => prevState.filter(warning => warning.id !== id)); }; return ( <> - + Add Warning + + Add Info + { clientOnPngFrame={(ctx: CanvasRenderingContext2D) => { fillGray(ctx.canvas); }} - warnings={warnings} - onRemoveWarning={removeWarning} + alerts={alerts} + onRemoveAlert={removeAlert} /> ); diff --git a/web/packages/teleport/src/DesktopSession/DesktopSession.tsx b/web/packages/teleport/src/DesktopSession/DesktopSession.tsx index 9d5cb9bb29abf..66a66825a209e 100644 --- a/web/packages/teleport/src/DesktopSession/DesktopSession.tsx +++ b/web/packages/teleport/src/DesktopSession/DesktopSession.tsx @@ -83,8 +83,8 @@ export function DesktopSession(props: State) { setClipboardSharingState, onShareDirectory, onCtrlAltDel, - warnings, - onRemoveWarning, + alerts, + onRemoveAlert, fetchAttempt, tdpConnection, wsConnection, @@ -137,8 +137,8 @@ export function DesktopSession(props: State) { clipboardSharingMessage={clipboardSharingMessage(clipboardSharingState)} onShareDirectory={onShareDirectory} onCtrlAltDel={onCtrlAltDel} - warnings={warnings} - onRemoveWarning={onRemoveWarning} + alerts={alerts} + onRemoveAlert={onRemoveAlert} /> {screenState.screen === 'anotherSessionActive' && ( diff --git a/web/packages/teleport/src/DesktopSession/TopBar.tsx b/web/packages/teleport/src/DesktopSession/TopBar.tsx index b6f0dc6ba1170..ac3f3f0c8b357 100644 --- a/web/packages/teleport/src/DesktopSession/TopBar.tsx +++ b/web/packages/teleport/src/DesktopSession/TopBar.tsx @@ -24,7 +24,7 @@ import { Clipboard, FolderShared } from 'design/Icon'; import { HoverTooltip } from 'shared/components/ToolTip'; import ActionMenu from './ActionMenu'; -import { WarningDropdown } from './WarningDropdown'; +import { AlertDropdown } from './AlertDropdown'; import type { NotificationItem } from 'shared/components/Notification'; @@ -38,8 +38,8 @@ export default function TopBar(props: Props) { isSharingDirectory, onShareDirectory, onCtrlAltDel, - warnings, - onRemoveWarning, + alerts, + onRemoveAlert, } = props; const theme = useTheme(); @@ -80,10 +80,7 @@ export default function TopBar(props: Props) { > - + ([]); - const onRemoveWarning = (id: string) => { - setWarnings(prevState => prevState.filter(warning => warning.id !== id)); + const [alerts, setAlerts] = useState([]); + const onRemoveAlert = (id: string) => { + setAlerts(prevState => prevState.filter(alert => alert.id !== id)); }; const clientCanvasProps = useTdpClientCanvas({ @@ -126,7 +126,7 @@ export default function useDesktopSession() { setClipboardSharingState, setDirectorySharingState, clipboardSharingState, - setWarnings, + setAlerts, }); const tdpClient = clientCanvasProps.tdpClient; @@ -150,7 +150,7 @@ export default function useDesktopSession() { ...prevState, directorySelected: false, })); - setWarnings(prevState => [ + setAlerts(prevState => [ ...prevState, { id: crypto.randomUUID(), @@ -164,7 +164,7 @@ export default function useDesktopSession() { ...prevState, directorySelected: false, })); - setWarnings(prevState => [ + setAlerts(prevState => [ ...prevState, { id: crypto.randomUUID(), @@ -211,8 +211,8 @@ export default function useDesktopSession() { setShowAnotherSessionActiveDialog, onShareDirectory, onCtrlAltDel, - warnings, - onRemoveWarning, + alerts, + onRemoveAlert, ...clientCanvasProps, }; } diff --git a/web/packages/teleport/src/DesktopSession/useTdpClientCanvas.tsx b/web/packages/teleport/src/DesktopSession/useTdpClientCanvas.tsx index 91fbb4d154e7e..00ecb243682b6 100644 --- a/web/packages/teleport/src/DesktopSession/useTdpClientCanvas.tsx +++ b/web/packages/teleport/src/DesktopSession/useTdpClientCanvas.tsx @@ -62,7 +62,7 @@ export default function useTdpClientCanvas(props: Props) { clipboardSharingState, setClipboardSharingState, setDirectorySharingState, - setWarnings, + setAlerts, } = props; const [tdpClient, setTdpClient] = useState(null); const initialTdpConnectionSucceeded = useRef(false); @@ -174,7 +174,7 @@ export default function useTdpClientCanvas(props: Props) { // Default TdpClientEvent.TDP_WARNING and TdpClientEvent.CLIENT_WARNING handler const clientOnTdpWarning = (warning: string) => { - setWarnings(prevState => { + setAlerts(prevState => { return [ ...prevState, { @@ -186,6 +186,8 @@ export default function useTdpClientCanvas(props: Props) { }); }; + // TODO(zmb3): this is not what an info-level alert should do. + // rename it to something like onGracefulDisconnect const clientOnTdpInfo = (info: string) => { setDirectorySharingState(defaultDirectorySharingState); setClipboardSharingState(defaultClipboardSharingState); @@ -347,7 +349,7 @@ type Props = { clipboardSharingState: ClipboardSharingState; setClipboardSharingState: Setter; setDirectorySharingState: Setter; - setWarnings: Setter; + setAlerts: Setter; }; /** diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index a415932b5e45c..098cb9d824fa6 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -220,8 +220,8 @@ export default class Client extends EventEmitterWebAuthnSender { TdpClientEvent.TDP_ERROR ); break; - case MessageType.NOTIFICATION: - this.handleTdpNotification(buffer); + case MessageType.ALERT: + this.handleTdpAlert(buffer); break; case MessageType.MFA_JSON: this.handleMfaChallenge(buffer); @@ -297,17 +297,15 @@ export default class Client extends EventEmitterWebAuthnSender { ); } - handleTdpNotification(buffer: ArrayBuffer) { - const notification = this.codec.decodeNotification(buffer); - if (notification.severity === Severity.Error) { - this.handleError( - new Error(notification.message), - TdpClientEvent.TDP_ERROR - ); - } else if (notification.severity === Severity.Warning) { - this.handleWarning(notification.message, TdpClientEvent.TDP_WARNING); + handleTdpAlert(buffer: ArrayBuffer) { + const alert = this.codec.decodeAlert(buffer); + // TODO(zmb3): info and warning should use the same handler + if (alert.severity === Severity.Error) { + this.handleError(new Error(alert.message), TdpClientEvent.TDP_ERROR); + } else if (alert.severity === Severity.Warning) { + this.handleWarning(alert.message, TdpClientEvent.TDP_WARNING); } else { - this.handleInfo(notification.message); + this.handleInfo(alert.message); } } diff --git a/web/packages/teleport/src/lib/tdp/codec.ts b/web/packages/teleport/src/lib/tdp/codec.ts index b670e8d03dd65..064a3bae97abb 100644 --- a/web/packages/teleport/src/lib/tdp/codec.ts +++ b/web/packages/teleport/src/lib/tdp/codec.ts @@ -48,7 +48,7 @@ export enum MessageType { SHARED_DIRECTORY_LIST_REQUEST = 25, SHARED_DIRECTORY_LIST_RESPONSE = 26, PNG2_FRAME = 27, - NOTIFICATION = 28, + ALERT = 28, RDP_FASTPATH_PDU = 29, RDP_RESPONSE_PDU = 30, RDP_CONNECTION_ACTIVATED = 31, @@ -137,7 +137,7 @@ export function toSeverity(severity: number): Severity { } // | message type (28) | message_length uint32 | message []byte | severity byte -export type Notification = { +export type Alert = { message: string; severity: Severity; }; @@ -758,11 +758,11 @@ export default class Codec { } /** - * decodeNotification decodes a raw tdp Notification message + * decodeAlert decodes a raw TDP alert message * | message type (28) | message_length uint32 | message []byte | severity byte * @throws {Error} if an invalid severity is passed */ - decodeNotification(buffer: ArrayBuffer): Notification { + decodeAlert(buffer: ArrayBuffer): Alert { const dv = new DataView(buffer); let offset = 0;