From 0348fb4272277593cd12b925779c97c574220313 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Wed, 7 Apr 2021 23:23:45 -0700 Subject: [PATCH 1/7] Add broacaster to trigger reloads for opened tabs --- packages/shared/hooks/useAttemptNext.ts | 2 ++ .../src/Console/useOnExitConfirmation.ts | 7 +++-- packages/teleport/src/config.ts | 6 ++-- .../src/services/localStorage/types.ts | 1 + .../teleport/src/services/session/index.ts | 5 +++- .../teleport/src/services/session/session.ts | 29 +++++++++++++++---- .../teleport/src/services/session/types.ts | 4 +++ 7 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 packages/teleport/src/services/session/types.ts diff --git a/packages/shared/hooks/useAttemptNext.ts b/packages/shared/hooks/useAttemptNext.ts index 6f7b24e72..f347a10c8 100644 --- a/packages/shared/hooks/useAttemptNext.ts +++ b/packages/shared/hooks/useAttemptNext.ts @@ -57,3 +57,5 @@ export type Attempt = { }; type Callback = (fn?: any) => Promise; + +export type State = ReturnType; diff --git a/packages/teleport/src/Console/useOnExitConfirmation.ts b/packages/teleport/src/Console/useOnExitConfirmation.ts index 2bdc2287d..0236b84a4 100644 --- a/packages/teleport/src/Console/useOnExitConfirmation.ts +++ b/packages/teleport/src/Console/useOnExitConfirmation.ts @@ -17,7 +17,7 @@ import React from 'react'; import ConsoleContext from './consoleContext'; import * as stores from './stores/types'; -import session from 'teleport/services/session'; +import session, { forceReload } from 'teleport/services/session'; // TAB_MIN_AGE defines "active terminal" session in ms const TAB_MIN_AGE = 30000; @@ -38,10 +38,11 @@ function useOnExitConfirmation(ctx: ConsoleContext) { * of document opened and how long it has been active for. */ const handleBeforeunload = event => { - // Do not ask for confirmation when session is expired, which may trigger prompt + // Do not ask for confirmation when forcing refresh or when + // session is expired, which may trigger prompt // when browser triggers page reload before it receives session.end event, // which is not guaranteed to happen in that order. - if (!session.isValid()) { + if (!session.isValid() || forceReload) { return; } diff --git a/packages/teleport/src/config.ts b/packages/teleport/src/config.ts index ca517dd2e..8c83e841d 100644 --- a/packages/teleport/src/config.ts +++ b/packages/teleport/src/config.ts @@ -81,7 +81,7 @@ const cfg = { clusterEventsPath: `/v1/webapi/sites/:clusterId/events/search?from=:start?&to=:end?&limit=:limit?`, scp: '/v1/webapi/sites/:clusterId/nodes/:serverId/:login/scp?location=:location&filename=:filename', - renewTokenPath: '/v1/webapi/sessions/renew/:requestId?', + renewTokenPath: '/v1/webapi/sessions/renew', resetPasswordTokenPath: '/v1/webapi/users/password/token', sessionPath: '/v1/webapi/sessions', userContextPath: '/v1/webapi/sites/:clusterId/context', @@ -266,8 +266,8 @@ const cfg = { }); }, - getRenewTokenUrl(requestId?: string) { - return generatePath(cfg.api.renewTokenPath, { requestId }); + getRenewTokenUrl() { + return cfg.api.renewTokenPath; }, getGithubConnectorsUrl(name?: string) { diff --git a/packages/teleport/src/services/localStorage/types.ts b/packages/teleport/src/services/localStorage/types.ts index b56b8468d..21cfd5bcd 100644 --- a/packages/teleport/src/services/localStorage/types.ts +++ b/packages/teleport/src/services/localStorage/types.ts @@ -17,6 +17,7 @@ limitations under the License. export const KeysEnum = { TOKEN: 'grv_teleport_token', TOKEN_RENEW: 'grv_teleport_token_renew', + RELOAD_TABS: 'grv_reload_tabs', }; export class BearerToken { diff --git a/packages/teleport/src/services/session/index.ts b/packages/teleport/src/services/session/index.ts index 29d876e86..e68b3bb2d 100644 --- a/packages/teleport/src/services/session/index.ts +++ b/packages/teleport/src/services/session/index.ts @@ -14,5 +14,8 @@ * limitations under the License. */ -import session from './session'; +import session, { forceReload } from './session'; + +export { forceReload }; +export * from './types'; export default session; diff --git a/packages/teleport/src/services/session/session.ts b/packages/teleport/src/services/session/session.ts index 63c886570..2b0afb969 100644 --- a/packages/teleport/src/services/session/session.ts +++ b/packages/teleport/src/services/session/session.ts @@ -22,15 +22,20 @@ import localStorage, { KeysEnum, BearerToken, } from 'teleport/services/localStorage'; +import { RenewSessionRequest } from './types'; // Time to determine when to renew session which is // when expiry time of token is less than 3 minutes. -const RENEW_TOKEN_TIME = 180 * 1000 +const RENEW_TOKEN_TIME = 180 * 1000; const TOKEN_CHECKER_INTERVAL = 15 * 1000; // every 15 sec const logger = Logger.create('services/session'); let sesstionCheckerTimerId = null; +// forceReload is a global flag used to prevent beforeunload +// event listeners from stopping a page reload (ie: terminal). +export let forceReload = false; + const session = { logout() { api.delete(cfg.api.sessionPath).finally(() => { @@ -68,8 +73,16 @@ const session = { } }, - renewSession(requestId: string) { - return this._renewToken(requestId); + renewSession(req: RenewSessionRequest) { + return this._renewToken(req); + }, + + // reload triggers reloads on all opened tabs to apply new permissions. + // Triggers when user switches back to their default roles, + // or when assuming roles. + reload() { + localStorage.broadcast(KeysEnum.RELOAD_TABS, 'reload'); + history.reload(); }, isValid() { @@ -119,10 +132,10 @@ const session = { return this._timeLeft() < RENEW_TOKEN_TIME; }, - _renewToken(requestId?: string) { + _renewToken(req: RenewSessionRequest) { this._setAndBroadcastIsRenewing(true); return api - .post(cfg.getRenewTokenUrl(requestId)) + .post(cfg.getRenewTokenUrl(), req) .then(this._receiveBearerToken.bind(this)) .finally(() => { this._setAndBroadcastIsRenewing(false); @@ -213,6 +226,12 @@ const session = { function receiveMessage(event) { const { key, newValue } = event; + // check if page reload was triggered from another tab. + if (key === KeysEnum.RELOAD_TABS && newValue) { + forceReload = true; + history.reload(); + } + // check if logout was triggered from other tabs if (localStorage.getBearerToken() === null) { session.logout(); diff --git a/packages/teleport/src/services/session/types.ts b/packages/teleport/src/services/session/types.ts new file mode 100644 index 000000000..0c00eaf43 --- /dev/null +++ b/packages/teleport/src/services/session/types.ts @@ -0,0 +1,4 @@ +export type RenewSessionRequest = { + requestId?: string; + switchback?: boolean; +}; From 06e3be7365a82d3e0dcdd9b0152ac4f95cc709b5 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Thu, 8 Apr 2021 09:34:58 -0700 Subject: [PATCH 2/7] Return session expiry when renewing session --- .../src/services/localStorage/localStorage.ts | 3 +- .../src/services/localStorage/types.ts | 12 ------- .../src/services/session/makeSession.ts | 31 +++++++++++++++++++ .../teleport/src/services/session/session.ts | 24 +++++++------- .../teleport/src/services/session/types.ts | 11 +++++++ 5 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 packages/teleport/src/services/session/makeSession.ts diff --git a/packages/teleport/src/services/localStorage/localStorage.ts b/packages/teleport/src/services/localStorage/localStorage.ts index d75d22006..85908fd16 100644 --- a/packages/teleport/src/services/localStorage/localStorage.ts +++ b/packages/teleport/src/services/localStorage/localStorage.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { BearerToken, KeysEnum } from './types'; +import { BearerToken } from 'teleport/services/session'; +import { KeysEnum } from './types'; const storage = { clear() { diff --git a/packages/teleport/src/services/localStorage/types.ts b/packages/teleport/src/services/localStorage/types.ts index 21cfd5bcd..ee952c8ba 100644 --- a/packages/teleport/src/services/localStorage/types.ts +++ b/packages/teleport/src/services/localStorage/types.ts @@ -19,15 +19,3 @@ export const KeysEnum = { TOKEN_RENEW: 'grv_teleport_token_renew', RELOAD_TABS: 'grv_reload_tabs', }; - -export class BearerToken { - accessToken: string; - expiresIn: string; - created: number; - - constructor(json) { - this.accessToken = json.token; - this.expiresIn = json.expires_in; - this.created = new Date().getTime(); - } -} diff --git a/packages/teleport/src/services/session/makeSession.ts b/packages/teleport/src/services/session/makeSession.ts new file mode 100644 index 000000000..5f89d47b2 --- /dev/null +++ b/packages/teleport/src/services/session/makeSession.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2021 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 { Session, BearerToken } from './types'; + +export function makeSession(json: any): Session { + return { + token: makeBearerToken(json), + expires: json.sessionExpires, + }; +} + +export function makeBearerToken(json: any): BearerToken { + return { + accessToken: json.token, + expiresIn: json.expires_in, + created: new Date().getTime(), + }; +} diff --git a/packages/teleport/src/services/session/session.ts b/packages/teleport/src/services/session/session.ts index 2b0afb969..eef5a363e 100644 --- a/packages/teleport/src/services/session/session.ts +++ b/packages/teleport/src/services/session/session.ts @@ -18,10 +18,8 @@ import Logger from 'shared/libs/logger'; import cfg from 'teleport/config'; import history from 'teleport/services/history'; import api from 'teleport/services/api'; -import localStorage, { - KeysEnum, - BearerToken, -} from 'teleport/services/localStorage'; +import localStorage, { KeysEnum } from 'teleport/services/localStorage'; +import { makeSession, makeBearerToken } from './makeSession'; import { RenewSessionRequest } from './types'; // Time to determine when to renew session which is @@ -73,7 +71,9 @@ const session = { } }, - renewSession(req: RenewSessionRequest) { + // renewSession renews session and returns the + // absolute time the new session expires. + renewSession(req: RenewSessionRequest): Promise { return this._renewToken(req); }, @@ -117,7 +117,7 @@ const session = { el.parentNode.removeChild(el); const decoded = window.atob(el.content); const json = JSON.parse(decoded); - return new BearerToken(json); + return makeBearerToken(json); }, _shouldRenewToken() { @@ -136,17 +136,17 @@ const session = { this._setAndBroadcastIsRenewing(true); return api .post(cfg.getRenewTokenUrl(), req) - .then(this._receiveBearerToken.bind(this)) + .then(res => { + const session = makeSession(res); + localStorage.setBearerToken(session.token); + + return session.expires; + }) .finally(() => { this._setAndBroadcastIsRenewing(false); }); }, - _receiveBearerToken(json) { - const token = new BearerToken(json); - localStorage.setBearerToken(token); - }, - _setAndBroadcastIsRenewing(value) { this._setIsRenewing(value); localStorage.broadcast(KeysEnum.TOKEN_RENEW, value); diff --git a/packages/teleport/src/services/session/types.ts b/packages/teleport/src/services/session/types.ts index 0c00eaf43..70018888c 100644 --- a/packages/teleport/src/services/session/types.ts +++ b/packages/teleport/src/services/session/types.ts @@ -2,3 +2,14 @@ export type RenewSessionRequest = { requestId?: string; switchback?: boolean; }; + +export type BearerToken = { + accessToken: string; + expiresIn: string; + created: number; +}; + +export type Session = { + token: BearerToken; + expires: Date; +}; From ac40868618848dd90a595302635efe52148f1772 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Thu, 8 Apr 2021 22:22:42 -0700 Subject: [PATCH 3/7] Remove tab reload --- .../src/Console/useOnExitConfirmation.ts | 7 +++---- .../teleport/src/services/session/index.ts | 3 +-- .../teleport/src/services/session/session.ts | 18 ------------------ 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/teleport/src/Console/useOnExitConfirmation.ts b/packages/teleport/src/Console/useOnExitConfirmation.ts index 0236b84a4..2bdc2287d 100644 --- a/packages/teleport/src/Console/useOnExitConfirmation.ts +++ b/packages/teleport/src/Console/useOnExitConfirmation.ts @@ -17,7 +17,7 @@ import React from 'react'; import ConsoleContext from './consoleContext'; import * as stores from './stores/types'; -import session, { forceReload } from 'teleport/services/session'; +import session from 'teleport/services/session'; // TAB_MIN_AGE defines "active terminal" session in ms const TAB_MIN_AGE = 30000; @@ -38,11 +38,10 @@ function useOnExitConfirmation(ctx: ConsoleContext) { * of document opened and how long it has been active for. */ const handleBeforeunload = event => { - // Do not ask for confirmation when forcing refresh or when - // session is expired, which may trigger prompt + // Do not ask for confirmation when session is expired, which may trigger prompt // when browser triggers page reload before it receives session.end event, // which is not guaranteed to happen in that order. - if (!session.isValid() || forceReload) { + if (!session.isValid()) { return; } diff --git a/packages/teleport/src/services/session/index.ts b/packages/teleport/src/services/session/index.ts index e68b3bb2d..d80137b05 100644 --- a/packages/teleport/src/services/session/index.ts +++ b/packages/teleport/src/services/session/index.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import session, { forceReload } from './session'; +import session from './session'; -export { forceReload }; export * from './types'; export default session; diff --git a/packages/teleport/src/services/session/session.ts b/packages/teleport/src/services/session/session.ts index eef5a363e..aab794889 100644 --- a/packages/teleport/src/services/session/session.ts +++ b/packages/teleport/src/services/session/session.ts @@ -30,10 +30,6 @@ const logger = Logger.create('services/session'); let sesstionCheckerTimerId = null; -// forceReload is a global flag used to prevent beforeunload -// event listeners from stopping a page reload (ie: terminal). -export let forceReload = false; - const session = { logout() { api.delete(cfg.api.sessionPath).finally(() => { @@ -77,14 +73,6 @@ const session = { return this._renewToken(req); }, - // reload triggers reloads on all opened tabs to apply new permissions. - // Triggers when user switches back to their default roles, - // or when assuming roles. - reload() { - localStorage.broadcast(KeysEnum.RELOAD_TABS, 'reload'); - history.reload(); - }, - isValid() { return this._timeLeft() > 0; }, @@ -226,12 +214,6 @@ const session = { function receiveMessage(event) { const { key, newValue } = event; - // check if page reload was triggered from another tab. - if (key === KeysEnum.RELOAD_TABS && newValue) { - forceReload = true; - history.reload(); - } - // check if logout was triggered from other tabs if (localStorage.getBearerToken() === null) { session.logout(); From 47210944a55032adc87fd8116869c33d1b3b3cbb Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Wed, 14 Apr 2021 09:10:08 -0700 Subject: [PATCH 4/7] Add className to Main.tsx to target style from other components --- packages/teleport/src/Main/Main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/teleport/src/Main/Main.tsx b/packages/teleport/src/Main/Main.tsx index 45ae4eed7..f17d3b8e5 100644 --- a/packages/teleport/src/Main/Main.tsx +++ b/packages/teleport/src/Main/Main.tsx @@ -73,7 +73,7 @@ export function Main(props: State) { - + From 97cb2e489dccfaa6c1ebf583b1033279731dcda7 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Wed, 14 Apr 2021 09:50:12 -0700 Subject: [PATCH 5/7] Export StyledMain instead to add additional styles from Banner --- packages/teleport/src/Main/Main.tsx | 6 +++--- packages/teleport/src/Main/index.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/teleport/src/Main/Main.tsx b/packages/teleport/src/Main/Main.tsx index f17d3b8e5..d92d1503b 100644 --- a/packages/teleport/src/Main/Main.tsx +++ b/packages/teleport/src/Main/Main.tsx @@ -73,18 +73,18 @@ export function Main(props: State) { - + {$features} - + ); } -const VerticalSplit = styled.div` +export const StyledMain = styled.div` width: 100%; height: 100%; display: flex; diff --git a/packages/teleport/src/Main/index.ts b/packages/teleport/src/Main/index.ts index eb3e563ae..3d47650d1 100644 --- a/packages/teleport/src/Main/index.ts +++ b/packages/teleport/src/Main/index.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { hot } from 'react-hot-loader/root'; -import Main from './Main'; +import Main, { StyledMain } from './Main'; +export { StyledMain }; export default hot(Main); From b28242fcbaf521ebcd5372055146b106f05e1fa8 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Thu, 15 Apr 2021 14:12:16 -0700 Subject: [PATCH 6/7] Widen Main min-width --- packages/teleport/src/Main/Main.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/teleport/src/Main/Main.tsx b/packages/teleport/src/Main/Main.tsx index d92d1503b..123c6b08a 100644 --- a/packages/teleport/src/Main/Main.tsx +++ b/packages/teleport/src/Main/Main.tsx @@ -90,7 +90,7 @@ export const StyledMain = styled.div` display: flex; flex: 1; position: absolute; - min-width: 900px; + min-width: 1000px; `; const HorizontalSplit = styled.div` @@ -98,6 +98,9 @@ const HorizontalSplit = styled.div` flex-direction: column; width: 100%; height: 100%; + + // Allows shrinking beyond content size on flexed childrens. + min-width: 0; `; const StyledIndicator = styled(HorizontalSplit)` From ad2f25d28a0cc023e5e51d2960316d4dd5aba26d Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Mon, 19 Apr 2021 18:59:22 -0700 Subject: [PATCH 7/7] Update e-ref --- packages/webapps.e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webapps.e b/packages/webapps.e index ce9070104..524031067 160000 --- a/packages/webapps.e +++ b/packages/webapps.e @@ -1 +1 @@ -Subproject commit ce907010419f32b759d4f9a96c38215e4e45bec2 +Subproject commit 524031067f8d541cebe34f16b67ef354500ec6b1