From: Peter Amstutz Date: Tue, 5 Nov 2019 19:11:00 +0000 (-0500) Subject: 15736: When validating sessions, save the config X-Git-Tag: 2.0.0~30^2~1 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/087d49d5c43866c8a20e8ac830ccc9b12188408f 15736: When validating sessions, save the config All clusters in 'sessions' (whether part of the federation or not) where we are able to successfully fetch either the new exported public config or the old discovery document will get added to auth.remoteHostsConfig. This means links to other clusters works consistently (sort of, users may still need to log in.) Also, it now updates the title bar with the cluster id. --- diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 5cf472c3..bb518d3f 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -73,7 +73,13 @@ export const getNavUrl = (uuid: string, config: FederationConfig) => { } else if (config.remoteHostsConfig[cls]) { let u: URL; if (config.remoteHostsConfig[cls].workbench2Url) { - u = new URL(config.remoteHostsConfig[cls].workbench2Url || ""); + /* NOTE: wb2 presently doesn't support passing api_token + to arbitrary page to set credentials, only through + api-token route. So for navigation to work, user needs + to already be logged in. In the future we want to just + request the records and display in the current + workbench instance making this redirect unnecessary. */ + u = new URL(config.remoteHostsConfig[cls].workbench2Url); } else { u = new URL(config.remoteHostsConfig[cls].workbenchUrl); u.search = "api_token=" + config.sessions.filter((s) => s.clusterId === cls)[0].token; diff --git a/src/store/auth/auth-action-session.ts b/src/store/auth/auth-action-session.ts index 2a9733f0..1c9d1d14 100644 --- a/src/store/auth/auth-action-session.ts +++ b/src/store/auth/auth-action-session.ts @@ -9,7 +9,10 @@ import { ServiceRepository } from "~/services/services"; import Axios from "axios"; import { getUserFullname, User } from "~/models/user"; import { authActions } from "~/store/auth/auth-action"; -import { Config, ClusterConfigJSON, CLUSTER_CONFIG_PATH, DISCOVERY_DOC_PATH, ARVADOS_API_PATH } from "~/common/config"; +import { + Config, ClusterConfigJSON, CLUSTER_CONFIG_PATH, DISCOVERY_DOC_PATH, + buildConfig, mockClusterConfigJSON +} from "~/common/config"; import { normalizeURLPath } from "~/common/url"; import { Session, SessionStatus } from "~/models/session"; import { progressIndicatorActions } from "~/store/progress-indicator/progress-indicator-actions"; @@ -17,34 +20,36 @@ import { AuthService, UserDetailsResponse } from "~/services/auth-service/auth-s import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions"; import * as jsSHA from "jssha"; -const getClusterInfo = async (origin: string): Promise<{ clusterId: string, baseUrl: string } | null> => { +const getClusterConfig = async (origin: string): Promise => { // Try the new public config endpoint try { const config = (await Axios.get(`${origin}/${CLUSTER_CONFIG_PATH}`)).data; - return { - clusterId: config.ClusterID, - baseUrl: normalizeURLPath(`${config.Services.Controller.ExternalURL}/${ARVADOS_API_PATH}`) - }; + return buildConfig(config); } catch { } // Fall back to discovery document try { const config = (await Axios.get(`${origin}/${DISCOVERY_DOC_PATH}`)).data; return { - clusterId: config.uuidPrefix, - baseUrl: normalizeURLPath(config.baseUrl) + baseUrl: normalizeURLPath(config.baseUrl), + keepWebServiceUrl: config.keepWebServiceUrl, + remoteHosts: config.remoteHosts, + rootUrl: config.rootUrl, + uuidPrefix: config.uuidPrefix, + websocketUrl: config.websocketUrl, + workbenchUrl: config.workbenchUrl, + workbench2Url: config.workbench2Url, + loginCluster: "", + vocabularyUrl: "", + fileViewersConfigUrl: "", + clusterConfig: mockClusterConfigJSON({}) }; } catch { } return null; }; -interface RemoteHostInfo { - clusterId: string; - baseUrl: string; -} - -const getRemoteHostInfo = async (remoteHost: string): Promise => { +const getRemoteHostConfig = async (remoteHost: string): Promise => { let url = remoteHost; if (url.indexOf('://') < 0) { url = 'https://' + url; @@ -52,14 +57,14 @@ const getRemoteHostInfo = async (remoteHost: string): Promise(`${origin}/config.json`)).data.API_HOST); + r = await getClusterConfig((await Axios.get(`${origin}/config.json`)).data.API_HOST); if (r !== null) { return r; } @@ -67,7 +72,7 @@ const getRemoteHostInfo = async (remoteHost: string): Promise(`${origin}/status.json`)).data.apiBaseURL); + r = await getClusterConfig((await Axios.get(`${origin}/status.json`)).data.apiBaseURL); if (r !== null) { return r; } @@ -104,11 +109,11 @@ export const getSaltedToken = (clusterId: string, token: string) => { export const getActiveSession = (sessions: Session[]): Session | undefined => sessions.find(s => s.active); -export const validateCluster = async (info: RemoteHostInfo, useToken: string): +export const validateCluster = async (config: Config, useToken: string): Promise<{ user: User; token: string }> => { - const saltedToken = getSaltedToken(info.clusterId, useToken); - const user = await getUserDetails(info.baseUrl, saltedToken); + const saltedToken = getSaltedToken(config.uuidPrefix, useToken); + const user = await getUserDetails(config.baseUrl, saltedToken); return { user: { firstName: user.first_name, @@ -140,16 +145,17 @@ export const validateSession = (session: Session, activeSession: Session) => }; let fail: Error | null = null; - const info = await getRemoteHostInfo(session.remoteHost); - if (info !== null) { + const config = await getRemoteHostConfig(session.remoteHost); + if (config !== null) { + dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config })); try { - const { user, token } = await validateCluster(info, session.token); - setupSession(info.baseUrl, user, token); + const { user, token } = await validateCluster(config, session.token); + setupSession(config.baseUrl, user, token); } catch (e) { fail = new Error(`Getting current user for ${session.remoteHost}: ${e.message}`); try { - const { user, token } = await validateCluster(info, activeSession.token); - setupSession(info.baseUrl, user, token); + const { user, token } = await validateCluster(config, activeSession.token); + setupSession(config.baseUrl, user, token); fail = null; } catch (e2) { if (e.message === invalidV2Token) { @@ -214,8 +220,8 @@ export const addSession = (remoteHost: string, token?: string, sendToLogin?: boo } if (useToken) { - const info = await getRemoteHostInfo(remoteHost); - if (!info) { + const config = await getRemoteHostConfig(remoteHost); + if (!config) { dispatch(snackbarActions.OPEN_SNACKBAR({ message: `Could not get config for ${remoteHost}`, kind: SnackbarKind.ERROR @@ -224,7 +230,8 @@ export const addSession = (remoteHost: string, token?: string, sendToLogin?: boo } try { - const { user, token } = await validateCluster(info, useToken); + dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config })); + const { user, token } = await validateCluster(config, useToken); const session = { loggedIn: true, status: SessionStatus.VALIDATED, @@ -232,13 +239,13 @@ export const addSession = (remoteHost: string, token?: string, sendToLogin?: boo email: user.email, name: getUserFullname(user), uuid: user.uuid, - baseUrl: info.baseUrl, - clusterId: info.clusterId, + baseUrl: config.baseUrl, + clusterId: config.uuidPrefix, remoteHost, token }; - if (sessions.find(s => s.clusterId === info.clusterId)) { + if (sessions.find(s => s.clusterId === config.uuidPrefix)) { dispatch(authActions.UPDATE_SESSION(session)); } else { dispatch(authActions.ADD_SESSION(session)); @@ -248,7 +255,7 @@ export const addSession = (remoteHost: string, token?: string, sendToLogin?: boo return session; } catch { if (sendToLogin) { - const rootUrl = new URL(info.baseUrl); + const rootUrl = new URL(config.baseUrl); rootUrl.pathname = ""; window.location.href = `${rootUrl.toString()}/login?return_to=` + encodeURI(`${window.location.protocol}//${window.location.host}/add-session?baseURL=` + encodeURI(rootUrl.toString())); return; diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts index e394d62d..9f18b5b0 100644 --- a/src/store/auth/auth-action.ts +++ b/src/store/auth/auth-action.ts @@ -10,11 +10,10 @@ import { ServiceRepository } from "~/services/services"; import { SshKeyResource } from '~/models/ssh-key'; import { User, UserResource } from "~/models/user"; import { Session } from "~/models/session"; -import { getClusterConfigURL, Config, ClusterConfigJSON, buildConfig } from '~/common/config'; +import { Config } from '~/common/config'; import { initSessions } from "~/store/auth/auth-action-session"; import { cancelLinking } from '~/store/link-account-panel/link-account-panel-actions'; import { matchTokenRoute, matchFedTokenRoute } from '~/routes/routes'; -import Axios from "axios"; import { AxiosError } from "axios"; export const authActions = unionize({ @@ -76,6 +75,7 @@ const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState, } dispatch(authActions.CONFIG({ config })); dispatch(authActions.SET_HOME_CLUSTER(config.loginCluster || homeCluster || config.uuidPrefix)); + document.title = `Arvados Workbench (${config.uuidPrefix})`; if (token && user) { dispatch(authActions.INIT({ user, token })); dispatch(initSessions(services.authService, config, user)); @@ -90,14 +90,6 @@ const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState, } }); } - Object.keys(config.remoteHosts).map((k) => { - if (k !== config.uuidPrefix) { - Axios.get(getClusterConfigURL(config.remoteHosts[k])) - .then(response => { - dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config: buildConfig(response.data) })); - }); - } - }); }; export const saveApiToken = (token: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {