Skip to content

Commit

Permalink
Set MFA context for services from MFA Context provider.
Browse files Browse the repository at this point in the history
  • Loading branch information
Joerger committed Dec 18, 2024
1 parent 2237d89 commit 5fa0ec5
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 46 deletions.
6 changes: 2 additions & 4 deletions web/packages/teleport/src/JoinTokens/JoinTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import {
FeatureHeaderTitle,
} from 'teleport/components/Layout';
import ResourceEditor from 'teleport/components/ResourceEditor';
import { useMfaContext } from 'teleport/MFAContext/MFAContext';
import { JoinToken } from 'teleport/services/joinToken';
import { KindJoinToken, Resource } from 'teleport/services/resources';

Expand All @@ -71,14 +70,13 @@ function makeTokenResource(token: JoinToken): Resource<KindJoinToken> {

export const JoinTokens = () => {
const ctx = useTeleport();
const mfa = useMfaContext();

const [creatingToken, setCreatingToken] = useState(false);
const [editingToken, setEditingToken] = useState<JoinToken | null>(null);
const [tokenToDelete, setTokenToDelete] = useState<JoinToken | null>(null);
const [joinTokensAttempt, runJoinTokensAttempt, setJoinTokensAttempt] =
useAsync(async () => {
const mfaResponse = await mfa.getAdminActionMfaResponse();
return ctx.joinTokenService.fetchJoinTokens(null, mfaResponse);
return ctx.joinTokenService.fetchJoinTokens(null);
});

const resources = useResources(
Expand Down
24 changes: 10 additions & 14 deletions web/packages/teleport/src/MFAContext/MFAContext.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import {
PropsWithChildren,
createContext,
useCallback,
useContext,
useRef,
} from 'react';

import { PropsWithChildren, createContext, useCallback, useRef } from 'react';
import AuthnDialog from 'teleport/components/AuthnDialog';
import { useMfa } from 'teleport/lib/useMfa';
import { MfaChallengeScope } from 'teleport/services/auth/auth';
import { MfaChallengeResponse } from 'teleport/services/mfa';

import { useTeleport } from '..';

export interface MFAContextValue {
getAdminActionMfaResponse(reusable?: boolean): Promise<MfaChallengeResponse>;
}

export const MFAContext = createContext<MFAContextValue>(null);

export const useMfaContext = () => {
return useContext(MFAContext);
};

export const MFAContextProvider = ({ children }: PropsWithChildren) => {
const allowReuse = useRef(false);
const adminMfa = useMfa({
Expand All @@ -36,13 +27,18 @@ export const MFAContextProvider = ({ children }: PropsWithChildren) => {
const getAdminActionMfaResponse = useCallback(
async (reusable: boolean = false) => {
allowReuse.current = reusable;
return adminMfa.getChallengeResponse();
return (await adminMfa.getChallengeResponse()) || {}; // return an empty challenge to prevent mfa retry.
},
[adminMfa, allowReuse]
);

const mfaCtx = { getAdminActionMfaResponse };

const ctx = useTeleport();
ctx.joinTokenService.setMfaContext(mfaCtx);

return (
<MFAContext.Provider value={{ getAdminActionMfaResponse }}>
<MFAContext.Provider value={mfaCtx}>
<AuthnDialog {...adminMfa}></AuthnDialog>
{children}
</MFAContext.Provider>
Expand Down
2 changes: 1 addition & 1 deletion web/packages/teleport/src/lib/useMfa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function useMfa({ req, isMfaRequired }: MfaProps): MfaState {
);

const resetAttempt = () => {
if (mfaResponsePromise.current) mfaResponsePromise.current.reject();
if (mfaResponsePromise.current) mfaResponsePromise.current.resolve(null);
mfaResponsePromise.current = null;
setMfaChallenge(null);
setMfaAttempt(makeEmptyAttempt());
Expand Down
39 changes: 27 additions & 12 deletions web/packages/teleport/src/services/joinToken/joinToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,29 @@
*/

import cfg from 'teleport/config';
import { MFAContextValue } from 'teleport/MFAContext/MFAContext';
import api from 'teleport/services/api';

import { makeLabelMapOfStrArrs } from '../agents/make';
import { MfaChallengeResponse } from '../mfa';

import makeJoinToken from './makeJoinToken';
import { JoinRule, JoinToken, JoinTokenRequest } from './types';

const TeleportTokenNameHeader = 'X-Teleport-TokenName';

class JoinTokenService {
// MFA context is set late by the MFA Context provider.
mfa: MFAContextValue;
setMfaContext(mfa: MFAContextValue) {
this.mfa = mfa;
}

// TODO (avatus) refactor this code to eventually use `createJoinToken`
fetchJoinToken(
async fetchJoinToken(
req: JoinTokenRequest,
signal: AbortSignal = null
): Promise<JoinToken> {
const mfaResponse = await this.mfa.getAdminActionMfaResponse();
return api
.post(
cfg.getJoinTokenUrl(),
Expand All @@ -44,15 +51,17 @@ class JoinTokenService {
req.suggestedAgentMatcherLabels
),
},
signal
signal,
mfaResponse
)
.then(makeJoinToken);
}

upsertJoinTokenYAML(
async upsertJoinTokenYAML(
req: JoinTokenRequest,
tokenName: string
): Promise<JoinToken> {
const mfaResponse = await this.mfa.getAdminActionMfaResponse();
return api
.putWithHeaders(
cfg.getJoinTokenYamlUrl(),
Expand All @@ -62,31 +71,37 @@ class JoinTokenService {
{
[TeleportTokenNameHeader]: tokenName,
'Content-Type': 'application/json',
}
},
mfaResponse
)
.then(makeJoinToken);
}

createJoinToken(req: JoinTokenRequest): Promise<JoinToken> {
return api.post(cfg.getJoinTokensUrl(), req).then(makeJoinToken);
async createJoinToken(req: JoinTokenRequest): Promise<JoinToken> {
const mfaResponse = await this.mfa.getAdminActionMfaResponse();
return api
.post(cfg.getJoinTokensUrl(), req, mfaResponse)
.then(makeJoinToken);
}

fetchJoinTokens(
signal: AbortSignal = null,
mfaResponse?: MfaChallengeResponse
async fetchJoinTokens(
signal: AbortSignal = null
): Promise<{ items: JoinToken[] }> {
const mfaResponse = await this.mfa.getAdminActionMfaResponse();
return api.get(cfg.getJoinTokensUrl(), signal, mfaResponse).then(resp => {
return {
items: resp.items?.map(makeJoinToken) || [],
};
});
}

deleteJoinToken(id: string, signal: AbortSignal = null) {
async deleteJoinToken(id: string, signal: AbortSignal = null) {
const mfaResponse = await this.mfa.getAdminActionMfaResponse();
return api.deleteWithHeaders(
cfg.getJoinTokensUrl(),
{ [TeleportTokenNameHeader]: id },
signal
signal,
mfaResponse
);
}
}
Expand Down
30 changes: 15 additions & 15 deletions web/packages/teleport/src/teleportContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpr

import cfg from 'teleport/config';

import { StoreNav, StoreNotifications, StoreUserContext } from './stores';
import * as types from './types';
import AuditService from './services/audit';
import RecordingsService from './services/recordings';
import NodeService from './services/nodes';
import sessionService from './services/session';
import ResourceService from './services/resources';
import userService from './services/user';
import { notificationContentFactory } from './Notifications';
import { agentService } from './services/agents';
import appService from './services/apps';
import JoinTokenService from './services/joinToken';
import KubeService from './services/kube';
import AuditService from './services/audit';
import ClustersService from './services/clusters/clusters';
import DatabaseService from './services/databases';
import desktopService from './services/desktops';
import userGroupService from './services/userGroups';
import JoinTokenService from './services/joinToken';
import KubeService from './services/kube';
import MfaService from './services/mfa';
import { agentService } from './services/agents';
import { storageService } from './services/storageService';
import ClustersService from './services/clusters/clusters';
import NodeService from './services/nodes';
import { NotificationService } from './services/notifications';
import { notificationContentFactory } from './Notifications';
import RecordingsService from './services/recordings';
import ResourceService from './services/resources';
import sessionService from './services/session';
import { storageService } from './services/storageService';
import userService from './services/user';
import userGroupService from './services/userGroups';
import { StoreNav, StoreNotifications, StoreUserContext } from './stores';
import * as types from './types';

class TeleportContext implements types.Context {
// stores
Expand Down

0 comments on commit 5fa0ec5

Please sign in to comment.