From 6e1ba15c736bacce7dc187f24a33216bb9ad37f3 Mon Sep 17 00:00:00 2001 From: Eric Biagiotti Date: Wed, 15 May 2019 15:46:40 -0400 Subject: [PATCH] 15088: Adds federated account linking Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti --- .../link-account-panel-actions.ts | 13 ++++++-- .../link-account-panel-reducer.ts | 10 +++++- .../link-account-panel-root.tsx | 31 ++++++++++++++----- .../link-account-panel/link-account-panel.tsx | 8 +++-- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/store/link-account-panel/link-account-panel-actions.ts b/src/store/link-account-panel/link-account-panel-actions.ts index e63da1a4..b531b82d 100644 --- a/src/store/link-account-panel/link-account-panel-actions.ts +++ b/src/store/link-account-panel/link-account-panel-actions.ts @@ -18,7 +18,8 @@ import { progressIndicatorActions } from "~/store/progress-indicator/progress-in import { WORKBENCH_LOADING_SCREEN } from '~/store/workbench/workbench-actions'; export const linkAccountPanelActions = unionize({ - LINK_INIT: ofType<{ targetUser: UserResource | undefined }>(), + LINK_INIT: ofType<{ + targetUser: UserResource | undefined }>(), LINK_LOAD: ofType<{ originatingUser: OriginatingUser | undefined, targetUser: UserResource | undefined, @@ -30,6 +31,8 @@ export const linkAccountPanelActions = unionize({ targetUser: UserResource | undefined, userToLink: UserResource | undefined, error: LinkAccountPanelError }>(), + SET_SELECTED_CLUSTER: ofType<{ + selectedCluster: string }>(), HAS_SESSION_DATA: {} }); @@ -97,6 +100,10 @@ export const linkFailed = () => export const loadLinkAccountPanel = () => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + if (getState().linkAccountPanel.selectedCluster === undefined) { + dispatch(linkAccountPanelActions.SET_SELECTED_CLUSTER({ selectedCluster: getState().auth.localCluster })); + } + // First check if an account link operation has completed dispatch(checkForLinkStatus()); @@ -171,8 +178,10 @@ export const startLinking = (t: LinkAccountType) => const accountToLink = {type: t, userUuid: services.authService.getUuid(), token: services.authService.getApiToken()} as AccountToLink; services.linkAccountService.saveAccountToLink(accountToLink); const auth = getState().auth; + const selectedCluster = getState().linkAccountPanel.selectedCluster; + const homeCluster = selectedCluster ? selectedCluster : auth.homeCluster; dispatch(logout()); - dispatch(login(auth.localCluster, auth.homeCluster, auth.remoteHosts)); + dispatch(login(auth.localCluster, homeCluster, auth.remoteHosts)); }; export const getAccountLinkData = () => diff --git a/src/store/link-account-panel/link-account-panel-reducer.ts b/src/store/link-account-panel/link-account-panel-reducer.ts index 0e61abdd..3d205842 100644 --- a/src/store/link-account-panel/link-account-panel-reducer.ts +++ b/src/store/link-account-panel/link-account-panel-reducer.ts @@ -27,6 +27,7 @@ export enum OriginatingUser { } export interface LinkAccountPanelState { + selectedCluster: string | undefined; originatingUser: OriginatingUser | undefined; targetUser: UserResource | undefined; targetUserToken: string | undefined; @@ -37,6 +38,7 @@ export interface LinkAccountPanelState { } const initialState = { + selectedCluster: undefined, originatingUser: OriginatingUser.NONE, targetUser: undefined, targetUserToken: undefined, @@ -50,22 +52,28 @@ export const linkAccountPanelReducer = (state: LinkAccountPanelState = initialSt linkAccountPanelActions.match(action, { default: () => state, LINK_INIT: ({ targetUser }) => ({ + ...state, targetUser, targetUserToken: undefined, userToLink: undefined, userToLinkToken: undefined, status: LinkAccountPanelStatus.INITIAL, error: LinkAccountPanelError.NONE, originatingUser: OriginatingUser.NONE }), LINK_LOAD: ({ originatingUser, userToLink, targetUser, targetUserToken, userToLinkToken}) => ({ + ...state, originatingUser, targetUser, targetUserToken, userToLink, userToLinkToken, status: LinkAccountPanelStatus.LINKING, error: LinkAccountPanelError.NONE }), - LINK_INVALID: ({originatingUser, targetUser, userToLink, error}) => ({ + LINK_INVALID: ({ originatingUser, targetUser, userToLink, error }) => ({ + ...state, originatingUser, targetUser, targetUserToken: undefined, userToLink, userToLinkToken: undefined, error, status: LinkAccountPanelStatus.ERROR }), + SET_SELECTED_CLUSTER: ({ selectedCluster }) => ({ + ...state, selectedCluster + }), HAS_SESSION_DATA: () => ({ ...state, status: LinkAccountPanelStatus.HAS_SESSION_DATA }) diff --git a/src/views/link-account-panel/link-account-panel-root.tsx b/src/views/link-account-panel/link-account-panel-root.tsx index 19c4b97a..581e86dd 100644 --- a/src/views/link-account-panel/link-account-panel-root.tsx +++ b/src/views/link-account-panel/link-account-panel-root.tsx @@ -11,6 +11,7 @@ import { CardContent, Button, Grid, + Select } from '@material-ui/core'; import { ArvadosTheme } from '~/common/custom-theme'; import { UserResource } from "~/models/user"; @@ -30,19 +31,27 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ export interface LinkAccountPanelRootDataProps { targetUser?: UserResource; userToLink?: UserResource; + remoteHosts: { [key: string]: string }; + hasRemoteHosts: boolean; status : LinkAccountPanelStatus; error: LinkAccountPanelError; + selectedCluster?: string; } export interface LinkAccountPanelRootActionProps { startLinking: (type: LinkAccountType) => void; cancelLinking: () => void; linkAccount: () => void; + setSelectedCluster: (cluster: string) => void; } -function displayUser(user: UserResource, showCreatedAt: boolean = false) { +function displayUser(user: UserResource, showCreatedAt: boolean = false, showCluster: boolean = false) { const disp = []; disp.push({user.email} ({user.username}, {user.uuid})); + if (showCluster) { + const homeCluster = user.uuid.substr(0,5); + disp.push( hosted on cluster {homeCluster} and ); + } if (showCreatedAt) { disp.push( created on {formatDate(user.createdAt, true)}); } @@ -52,28 +61,36 @@ function displayUser(user: UserResource, showCreatedAt: boolean = false) { type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles; export const LinkAccountPanelRoot = withStyles(styles) ( - ({classes, targetUser, userToLink, status, error, startLinking, cancelLinking, linkAccount}: LinkAccountPanelRootProps) => { + ({classes, targetUser, userToLink, status, error, startLinking, cancelLinking, linkAccount, + remoteHosts, hasRemoteHosts, selectedCluster, setSelectedCluster}: LinkAccountPanelRootProps) => { return { status === LinkAccountPanelStatus.INITIAL && targetUser && - You are currently logged in as {displayUser(targetUser, true)} + You are currently logged in as {displayUser(targetUser, true, hasRemoteHosts)} You can link Arvados accounts. After linking, either login will take you to the same account. - + + {hasRemoteHosts && selectedCluster && + Please select the cluster that hosts the account you want to link with: + + } @@ -82,7 +99,7 @@ export const LinkAccountPanelRoot = withStyles(styles) ( { status === LinkAccountPanelStatus.LINKING && - Clicking 'Link accounts' will link {displayUser(userToLink, true)} to {displayUser(targetUser, true)}. + Clicking 'Link accounts' will link {displayUser(userToLink, true, hasRemoteHosts)} to {displayUser(targetUser, true, hasRemoteHosts)}. After linking, logging in as {displayUser(userToLink)} will log you into the same account as {displayUser(targetUser)}. diff --git a/src/views/link-account-panel/link-account-panel.tsx b/src/views/link-account-panel/link-account-panel.tsx index f620b568..06359111 100644 --- a/src/views/link-account-panel/link-account-panel.tsx +++ b/src/views/link-account-panel/link-account-panel.tsx @@ -5,7 +5,7 @@ import { RootState } from '~/store/store'; import { Dispatch } from 'redux'; import { connect } from 'react-redux'; -import { startLinking, cancelLinking, linkAccount } from '~/store/link-account-panel/link-account-panel-actions'; +import { startLinking, cancelLinking, linkAccount, linkAccountPanelActions } from '~/store/link-account-panel/link-account-panel-actions'; import { LinkAccountType } from '~/models/link-account'; import { LinkAccountPanelRoot, @@ -15,6 +15,9 @@ import { const mapStateToProps = (state: RootState): LinkAccountPanelRootDataProps => { return { + remoteHosts: state.auth.remoteHosts, + hasRemoteHosts: Object.keys(state.auth.remoteHosts).length > 1, + selectedCluster: state.linkAccountPanel.selectedCluster, targetUser: state.linkAccountPanel.targetUser, userToLink: state.linkAccountPanel.userToLink, status: state.linkAccountPanel.status, @@ -25,7 +28,8 @@ const mapStateToProps = (state: RootState): LinkAccountPanelRootDataProps => { const mapDispatchToProps = (dispatch: Dispatch): LinkAccountPanelRootActionProps => ({ startLinking: (type: LinkAccountType) => dispatch(startLinking(type)), cancelLinking: () => dispatch(cancelLinking()), - linkAccount: () => dispatch(linkAccount()) + linkAccount: () => dispatch(linkAccount()), + setSelectedCluster: (selectedCluster: string) => dispatch(linkAccountPanelActions.SET_SELECTED_CLUSTER({selectedCluster})) }); export const LinkAccountPanel = connect(mapStateToProps, mapDispatchToProps)(LinkAccountPanelRoot); -- 2.30.2