Skip to content

Commit

Permalink
Display warning if a user that wants to authorize a web session is no…
Browse files Browse the repository at this point in the history
…t logged in Connect (#49836)

* Display warning if a user that wants to authorize a web session is not logged in Connect

* Make the instructions more specific

* Fix types

* Convert the story to use controls

* Fix test name
  • Loading branch information
gzdunek authored Dec 10, 2024
1 parent 640a6d6 commit cfe482b
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Meta } from '@storybook/react';
import { wait } from 'shared/utils/wait';

import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
Expand All @@ -33,39 +34,41 @@ import { DocumentAuthorizeWebSession } from './DocumentAuthorizeWebSession';

export default {
title: 'Teleterm/DocumentAuthorizeWebSession',
};

const doc: types.DocumentAuthorizeWebSession = {
uri: '/docs/e2hyt5',
rootClusterUri: rootClusterUri,
kind: 'doc.authorize_web_session',
title: 'Authorize Web Session',
webSessionRequest: {
redirectUri: '',
token: '',
id: '',
component: Story,
argTypes: {
isDeviceTrusted: { control: { type: 'boolean' } },
isRequestedUserLoggedIn: { control: { type: 'boolean' } },
},
args: {
isDeviceTrusted: true,
isRequestedUserLoggedIn: true,
},
};
} satisfies Meta<StoryProps>;

export function DeviceNotTrusted() {
const rootCluster = makeRootCluster();
const appContext = new MockAppContext();
appContext.clustersService.setState(draftState => {
draftState.clusters.set(rootCluster.uri, rootCluster);
});
return (
<MockAppContextProvider appContext={appContext}>
<MockWorkspaceContextProvider rootClusterUri={rootCluster.uri}>
<DocumentAuthorizeWebSession doc={doc} visible={true} />
</MockWorkspaceContextProvider>
</MockAppContextProvider>
);
interface StoryProps {
isDeviceTrusted: boolean;
isRequestedUserLoggedIn: boolean;
}

export function DeviceTrusted() {
export function Story(props: StoryProps) {
const rootCluster = makeRootCluster({
loggedInUser: makeLoggedInUser({ isDeviceTrusted: true }),
loggedInUser: makeLoggedInUser({ isDeviceTrusted: props.isDeviceTrusted }),
});
const doc: types.DocumentAuthorizeWebSession = {
uri: '/docs/e2hyt5',
rootClusterUri: rootClusterUri,
kind: 'doc.authorize_web_session',
title: 'Authorize Web Session',
webSessionRequest: {
redirectUri: '',
token: '',
id: '',
username: props.isRequestedUserLoggedIn
? rootCluster.loggedInUser.name
: 'bob',
},
};

const appContext = new MockAppContext();
appContext.clustersService.setState(draftState => {
draftState.clusters.set(rootCluster.uri, rootCluster);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ const doc: types.DocumentAuthorizeWebSession = {
redirectUri: '',
token: '',
id: '',
username: 'alice',
},
};

test('authorize button is disabled when device is not trusted', async () => {
test('warning is visible and authorize button is disabled when device is not trusted', async () => {
const rootCluster = makeRootCluster({
loggedInUser: makeLoggedInUser({ isDeviceTrusted: false }),
});
Expand All @@ -64,6 +65,29 @@ test('authorize button is disabled when device is not trusted', async () => {
expect(await screen.findByText(/Authorize Session/)).toBeDisabled();
});

test('warning is visible and authorize button is disabled when requested user is not logged in', async () => {
const rootCluster = makeRootCluster({
loggedInUser: makeLoggedInUser({ isDeviceTrusted: true, name: 'bob' }),
});
const appContext = new MockAppContext();
appContext.clustersService.setState(draftState => {
draftState.clusters.set(rootCluster.uri, rootCluster);
});

render(
<MockAppContextProvider appContext={appContext}>
<MockWorkspaceContextProvider rootClusterUri={rootCluster.uri}>
<DocumentAuthorizeWebSession doc={doc} visible={true} />
</MockWorkspaceContextProvider>
</MockAppContextProvider>
);

expect(
await screen.findByText(/Requested user is not logged in/)
).toBeVisible();
expect(await screen.findByText(/Authorize Session/)).toBeDisabled();
});

test('authorizing a session opens its URL and closes document', async () => {
jest.spyOn(window, 'open').mockImplementation();
const rootCluster = makeRootCluster({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function DocumentAuthorizeWebSession(props: {
return confirmationToken;
});
const clusterName = routing.parseClusterName(props.doc.rootClusterUri);
const canAuthorize = rootCluster.loggedInUser?.isDeviceTrusted;
const isDeviceTrusted = rootCluster.loggedInUser?.isDeviceTrusted;
const isRequestedUserLoggedIn =
props.doc.webSessionRequest.username === rootCluster.loggedInUser?.name;
const canAuthorize = isDeviceTrusted && isRequestedUserLoggedIn;

async function authorizeAndCloseDocument() {
const [confirmationToken, error] = await authorize();
Expand Down Expand Up @@ -95,7 +98,7 @@ export function DocumentAuthorizeWebSession(props: {
<H1 mb="4">Authorize Web Session</H1>
<Flex flexDirection="column" gap={3}>
{/*It's technically possible to open a deep link to authorize a session on a device that is not enrolled.*/}
{!canAuthorize && (
{!isDeviceTrusted && (
<Alert
mb={0}
details={
Expand All @@ -115,6 +118,31 @@ export function DocumentAuthorizeWebSession(props: {
This device is not trusted
</Alert>
)}
{!isRequestedUserLoggedIn && (
<Alert
mb={0}
primaryAction={{
content: 'Log Out',
onClick: () => {
ctx.commandLauncher.executeCommand('cluster-logout', {
clusterUri: rootCluster.uri,
});
},
}}
details={
<>
You are logged in as <b>{rootCluster.loggedInUser?.name}</b>.
To authorize this web session request, please log out in
Teleport Connect and log in again as{' '}
<b>{props.doc.webSessionRequest.username}</b>.
<br />
Then click Launch Teleport Connect again in the browser.
</>
}
>
Requested user is not logged in
</Alert>
)}
{authorizeAttempt.status === 'error' && (
<Alert mb={0} details={authorizeAttempt.statusText}>
Could not authorize the session
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class DeepLinksService {
webSessionRequest: {
id,
token,
username: url.username,
redirectUri: redirect_uri,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ export interface DocumentAuthorizeWebSession extends DocumentBase {
export interface WebSessionRequest {
id: string;
token: string;
username: string;
redirectUri: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ describe('state persistence', () => {
id: '',
token: '',
redirectUri: '',
username: '',
},
},
],
Expand Down

0 comments on commit cfe482b

Please sign in to comment.