diff --git a/packages/teleport/src/AppLauncher/AppLauncher.story.tsx b/packages/teleport/src/AppLauncher/AppLauncher.story.tsx index c4699e0c5..c248c1fcb 100644 --- a/packages/teleport/src/AppLauncher/AppLauncher.story.tsx +++ b/packages/teleport/src/AppLauncher/AppLauncher.story.tsx @@ -16,16 +16,16 @@ import React from 'react'; -import { AppLauncher } from './AppLauncher'; +import { AppLauncherAccessDenied, AppLauncherProcessing } from './AppLauncher'; export default { title: 'Teleport/AppLauncher', }; export const Processing = () => { - return ; + return ; }; export const Failed = () => { - return ; + return ; }; diff --git a/packages/teleport/src/AppLauncher/AppLauncher.tsx b/packages/teleport/src/AppLauncher/AppLauncher.tsx index 75aeb6259..a3ab01054 100644 --- a/packages/teleport/src/AppLauncher/AppLauncher.tsx +++ b/packages/teleport/src/AppLauncher/AppLauncher.tsx @@ -14,25 +14,93 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { useCallback, useEffect } from 'react'; + +import { useLocation, useParams } from 'react-router'; + import { Flex, Indicator } from 'design'; + import { AccessDenied } from 'design/CardError'; -import useAppLauncher from './useAppLauncher'; +import useAttempt from 'shared/hooks/useAttemptNext'; -export default function Container() { - const state = useAppLauncher(); - return ; -} +import { UrlLauncherParams } from 'teleport/config'; +import service from 'teleport/services/apps'; + +export function AppLauncher() { + const { attempt, setAttempt } = useAttempt('processing'); + + const params = useParams(); + const { search } = useLocation(); + const queryParams = new URLSearchParams(search); + + const createAppSession = useCallback(async (params: UrlLauncherParams) => { + try { + let fqdn = params.fqdn; + if (!fqdn) { + const app = await service.getAppFqdn(params); + + fqdn = app.fqdn; + } + + const port = location.port ? `:${location.port}` : ''; + const session = await service.createAppSession(params); + + await fetch(`https://${fqdn}${port}/x-teleport-auth`, { + method: 'POST', + credentials: 'include', + headers: { + 'X-Cookie-Value': session.cookieValue, + 'X-Subject-Cookie-Value': session.subjectCookieValue, + }, + }); + + let path = ''; + if (queryParams.has('path')) { + path = decodeURIComponent(queryParams.get('path')); -export function AppLauncher(props: ReturnType) { - if (props.status === 'failed') { - return ; + if (!path.startsWith('/')) { + path = `/${path}`; + } + } + + window.location.replace(`https://${fqdn}${port}${path}`); + } catch (err) { + let statusText = 'Something went wrong'; + if (err instanceof Error) { + statusText = err.message; + } + + setAttempt({ + status: 'failed', + statusText, + }); + } + }, []); + + useEffect(() => { + createAppSession(params); + }, [params]); + + if (attempt.status === 'failed') { + return ; } + return ; +} + +export function AppLauncherProcessing() { return ( ); } + +interface AppLauncherAccessDeniedProps { + statusText: string; +} + +export function AppLauncherAccessDenied(props: AppLauncherAccessDeniedProps) { + return ; +} diff --git a/packages/teleport/src/AppLauncher/index.ts b/packages/teleport/src/AppLauncher/index.ts index c2164ef30..c0ac6c47d 100644 --- a/packages/teleport/src/AppLauncher/index.ts +++ b/packages/teleport/src/AppLauncher/index.ts @@ -14,5 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -import AppLauncher from './AppLauncher'; -export default AppLauncher; +export { AppLauncher as default } from './AppLauncher'; diff --git a/packages/teleport/src/AppLauncher/useAppLauncher.ts b/packages/teleport/src/AppLauncher/useAppLauncher.ts deleted file mode 100644 index bea986bb3..000000000 --- a/packages/teleport/src/AppLauncher/useAppLauncher.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2020 Gravitational, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React from 'react'; -import { useParams } from 'react-router'; - -import useAttempt from 'shared/hooks/useAttemptNext'; - -import service from 'teleport/services/apps'; -import { UrlLauncherParams } from 'teleport/config'; -import { getUrlParameter } from 'teleport/services/history'; - -export default function useAppLauncher() { - const params = useParams(); - const { attempt, setAttempt } = useAttempt('processing'); - - React.useEffect(() => { - resolveRedirectUrl(params) - .then(url => { - window.location.replace(url); - }) - .catch((err: Error) => { - setAttempt({ - status: 'failed', - statusText: err.message, - }); - }); - }, []); - - return { - ...attempt, - }; -} - -function resolveRedirectUrl(params: UrlLauncherParams) { - const location = window.location; - const port = location.port ? ':' + location.port : ''; - const state = getUrlParameter('state', location.search); - const arn = getUrlParameter('awsrole', location.search); - // redirectPath captures and pass any requested App Access path during the `x-teleport-auth` process - const redirectPath = getUrlParameter('path', location.search); - - // no state value: let the target app know of a new auth exchange - if (!state) { - return service.getAppFqdn(params).then(result => { - const url = new URL(`https://${result.fqdn}${port}/x-teleport-auth`); - if (params.clusterId) { - url.searchParams.set('cluster', params.clusterId); - } - if (params.publicAddr) { - url.searchParams.set('addr', params.publicAddr); - } - if (params.arn) { - url.searchParams.set('awsrole', decodeURIComponent(params.arn)); - } - if (redirectPath) { - url.searchParams.set('path', redirectPath); - } - return url.toString(); - }); - } - - // state value received: create new session for the target app - if (arn) { - params.arn = arn; - } - return service.createAppSession(params).then(result => { - const url = new URL(`https://${result.fqdn}${port}/x-teleport-auth`); - url.searchParams.set('state', state); - url.searchParams.append('subject', result.subject); - url.hash = `#value=${result.value}`; - if (redirectPath) { - url.searchParams.set('path', redirectPath); - } - return url.toString(); - }); -} diff --git a/packages/teleport/src/services/apps/apps.ts b/packages/teleport/src/services/apps/apps.ts index 6b6de97a3..a9ecec2f6 100644 --- a/packages/teleport/src/services/apps/apps.ts +++ b/packages/teleport/src/services/apps/apps.ts @@ -50,6 +50,8 @@ const service = { fqdn: json.fqdn as string, value: json.value as string, subject: json.subject as string, + cookieValue: json.cookie_value as string, + subjectCookieValue: json.subject_cookie_value as string, })); },