Skip to content

Commit

Permalink
Use AuthnDialog for file transfers; Fix json backend logic for file t…
Browse files Browse the repository at this point in the history
…ransfers.
  • Loading branch information
Joerger committed Dec 16, 2024
1 parent fb8c772 commit 4182ae3
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 112 deletions.
9 changes: 6 additions & 3 deletions lib/web/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,12 @@ func (h *Handler) transferFile(w http.ResponseWriter, r *http.Request, p httprou
req.mfaResponse = query.Get("webauthn")
}

mfaResponse, err := client.ParseMFAChallengeResponse([]byte(req.mfaResponse))
if err != nil {
return nil, trace.Wrap(err)
var mfaResponse *proto.MFAAuthenticateResponse
if req.mfaResponse != "" {
var err error
if mfaResponse, err = client.ParseMFAChallengeResponse([]byte(req.mfaResponse)); err != nil {
return nil, trace.Wrap(err)
}
}

// Send an error if only one of these params has been sent. Both should exist or not exist together
Expand Down
37 changes: 19 additions & 18 deletions web/packages/teleport/src/Console/DocumentSsh/DocumentSsh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, { useRef, useEffect, useState, useCallback } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTheme } from 'styled-components';

import { Indicator, Box } from 'design';
import { Box, Indicator } from 'design';

import {
FileTransferActionBar,
FileTransfer,
FileTransferRequests,
FileTransferActionBar,
FileTransferContextProvider,
FileTransferRequests,
} from 'shared/components/FileTransfer';
import { TerminalSearch } from 'shared/components/TerminalSearch';

Expand All @@ -39,8 +39,8 @@ import Document from '../Document';
import { useConsoleContext } from '../consoleContextProvider';

import { Terminal, TerminalRef } from './Terminal';
import useSshSession from './useSshSession';
import { useFileTransfer } from './useFileTransfer';
import useSshSession from './useSshSession';

export default function DocumentSshWrapper(props: PropTypes) {
return (
Expand All @@ -57,12 +57,7 @@ function DocumentSsh({ doc, visible }: PropTypes) {
const { tty, status, closeDocument, session } = useSshSession(doc);
const [showSearch, setShowSearch] = useState(false);
const mfa = useMfa(tty);
const {
getMfaResponseAttempt,
getDownloader,
getUploader,
fileTransferRequests,
} = useFileTransfer(tty, session, doc, mfa.mfaRequired);
const ft = useFileTransfer(tty, session, doc, mfa.mfaRequired);
const theme = useTheme();

function handleCloseFileTransfer() {
Expand Down Expand Up @@ -110,21 +105,19 @@ function DocumentSsh({ doc, visible }: PropTypes) {
<FileTransferRequests
onDeny={handleFileTransferDecision}
onApprove={handleFileTransferDecision}
requests={fileTransferRequests}
requests={ft.fileTransferRequests}
/>
}
beforeClose={() =>
window.confirm('Are you sure you want to cancel file transfers?')
}
errorText={
getMfaResponseAttempt.status === 'failed'
? getMfaResponseAttempt.statusText
: null
ft.mfaAttempt.status === 'error' ? ft.mfaAttempt.statusText : null
}
afterClose={handleCloseFileTransfer}
transferHandlers={{
getDownloader,
getUploader,
getDownloader: ft.getDownloader,
getUploader: ft.getUploader,
}}
/>
</>
Expand All @@ -143,7 +136,15 @@ function DocumentSsh({ doc, visible }: PropTypes) {
<Indicator />
</Box>
)}
{mfa.mfaChallenge && <AuthnDialog mfa={mfa} onCancel={closeDocument} />}
{mfa.mfaChallenge && <AuthnDialog {...mfa} onCancel={closeDocument} />}
{ft.mfaChallenge && (
<AuthnDialog
mfaChallenge={ft.mfaChallenge}
submitMfa={ft.submitMfa}
submitAttempt={ft.submitMfaAttempt}
onCancel={ft.clearMfaChallenge}
/>
)}
{status === 'initialized' && terminal}
</Document>
);
Expand Down
88 changes: 77 additions & 11 deletions web/packages/teleport/src/Console/DocumentSsh/useFileTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { useEffect, useState, useCallback } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useFileTransferContext } from 'shared/components/FileTransfer';

import Tty from 'teleport/lib/term/tty';
import { DocumentSsh } from 'teleport/Console/stores';
import { EventType } from 'teleport/lib/term/enums';
import Tty from 'teleport/lib/term/tty';
import { Session } from 'teleport/services/session';
import { DocumentSsh } from 'teleport/Console/stores';

import { useConsoleContext } from '../consoleContextProvider';

import { useAsync } from 'shared/hooks/useAsync';
import cfg from 'teleport/config';
import auth, { MfaChallengeScope } from 'teleport/services/auth/auth';
import {
DeviceType,
MfaAuthenticateChallenge,
MfaChallengeResponse,
} from 'teleport/services/mfa';
import { getHttpFileTransferHandlers } from './httpFileTransferHandlers';
import useGetScpUrl from './useGetScpUrl';

export type FileTransferRequest = {
sid: string;
Expand Down Expand Up @@ -60,25 +67,74 @@ export const useFileTransfer = (
const [fileTransferRequests, setFileTransferRequests] = useState<
FileTransferRequest[]
>([]);
const { getScpUrl, attempt: getMfaResponseAttempt } =
useGetScpUrl(addMfaToScpUrls);
const { clusterId, serverId, login } = currentDoc;

const [mfaChallenge, setMfaChallenge] = useState<MfaAuthenticateChallenge>();
const mfaResponsePromise =
useRef<PromiseWithResolvers<MfaChallengeResponse>>();

const clearMfaChallenge = () => {
setMfaChallenge(null);
};

const [mfaAttempt, getMfaResponse, setMfaAttempt] = useAsync(
useCallback(async () => {
const challenge = await auth.getMfaChallenge({
scope: MfaChallengeScope.USER_SESSION,
isMfaRequiredRequest: {
node: {
node_name: serverId,
login: login,
},
},
});
if (!challenge) return;

setMfaChallenge(challenge);
mfaResponsePromise.current = Promise.withResolvers();
const mfaResponse = await mfaResponsePromise.current.promise;
// clearMfaChallenge();
return mfaResponse;
}, [setMfaChallenge, clearMfaChallenge, mfaResponsePromise])
);

const [submitMfaAttempt, submitMfa] = useAsync(
useCallback(
async (mfaType?: DeviceType) => {
try {
const resp = auth.getMfaChallengeResponse(mfaChallenge, mfaType);
mfaResponsePromise.current.resolve(resp);
} catch (err) {
mfaResponsePromise.current.reject(err);
throw err;
}
},
[mfaChallenge, mfaResponsePromise]
)
);

const download = useCallback(
async (
location: string,
abortController: AbortController,
moderatedSessionParams?: ModeratedSessionParams
) => {
const url = await getScpUrl({
let mfaResponse: MfaChallengeResponse;
if (addMfaToScpUrls) {
[mfaResponse] = await getMfaResponse();
}

const url = cfg.getScpUrl({
location,
clusterId,
serverId,
login,
filename: location,
moderatedSessionId: moderatedSessionParams?.moderatedSessionId,
fileTransferRequestId: moderatedSessionParams?.fileRequestId,
mfaResponse,
});

if (!url) {
// if we return nothing here, the file transfer will not be added to the
// file transfer list. If we add it to the list, the file will continue to
Expand All @@ -88,7 +144,7 @@ export const useFileTransfer = (
}
return getHttpFileTransferHandlers().download(url, abortController);
},
[clusterId, login, serverId, getScpUrl]
[clusterId, login, serverId, addMfaToScpUrls]
);

const upload = useCallback(
Expand All @@ -98,14 +154,20 @@ export const useFileTransfer = (
abortController: AbortController,
moderatedSessionParams?: ModeratedSessionParams
) => {
const url = await getScpUrl({
let mfaResponse: MfaChallengeResponse;
if (addMfaToScpUrls) {
[mfaResponse] = await getMfaResponse();
}

const url = cfg.getScpUrl({
location,
clusterId,
serverId,
login,
filename: file.name,
moderatedSessionId: moderatedSessionParams?.moderatedSessionId,
fileTransferRequestId: moderatedSessionParams?.fileRequestId,
mfaResponse,
});
if (!url) {
// if we return nothing here, the file transfer will not be added to the
Expand All @@ -116,7 +178,7 @@ export const useFileTransfer = (
}
return getHttpFileTransferHandlers().upload(url, file, abortController);
},
[clusterId, serverId, login, getScpUrl]
[clusterId, serverId, login, addMfaToScpUrls]
);

/*
Expand Down Expand Up @@ -255,8 +317,12 @@ export const useFileTransfer = (
}

return {
mfaAttempt,
mfaChallenge,
clearMfaChallenge,
submitMfa,
submitMfaAttempt,
fileTransferRequests,
getMfaResponseAttempt,
getUploader,
getDownloader,
};
Expand Down
63 changes: 0 additions & 63 deletions web/packages/teleport/src/Console/DocumentSsh/useGetScpUrl.ts

This file was deleted.

Loading

0 comments on commit 4182ae3

Please sign in to comment.