1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import * as React from 'react';
16 } from '@material-ui/core';
17 import { ArvadosTheme } from '~/common/custom-theme';
18 import { UserResource } from "~/models/user";
19 import { LinkAccountType } from "~/models/link-account";
20 import { formatDate } from "~/common/formatters";
21 import { LinkAccountPanelStatus, LinkAccountPanelError } from "~/store/link-account-panel/link-account-panel-reducer";
22 import { Config } from '~/common/config';
24 type CssRules = 'root';
26 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
34 export interface LinkAccountPanelRootDataProps {
35 targetUser?: UserResource;
36 userToLink?: UserResource;
37 remoteHostsConfig: { [key: string]: Config };
38 hasRemoteHosts: boolean;
41 status: LinkAccountPanelStatus;
42 error: LinkAccountPanelError;
43 selectedCluster?: string;
44 isProcessing: boolean;
47 export interface LinkAccountPanelRootActionProps {
48 startLinking: (type: LinkAccountType) => void;
49 cancelLinking: () => void;
50 linkAccount: () => void;
51 setSelectedCluster: (cluster: string) => void;
54 function displayUser(user: UserResource, showCreatedAt: boolean = false, showCluster: boolean = false) {
56 disp.push(<span><b>{user.email}</b> ({user.username}, {user.uuid})</span>);
58 const homeCluster = user.uuid.substr(0, 5);
59 disp.push(<span> hosted on cluster <b>{homeCluster}</b> and </span>);
62 disp.push(<span> created on <b>{formatDate(user.createdAt)}</b></span>);
67 function isLocalUser(uuid: string, localCluster: string) {
68 return uuid.substring(0, 5) === localCluster;
71 type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles<CssRules>;
73 export const LinkAccountPanelRoot = withStyles(styles)(
74 ({ classes, targetUser, userToLink, status, isProcessing, error, startLinking, cancelLinking, linkAccount,
75 remoteHostsConfig, hasRemoteHosts, selectedCluster, setSelectedCluster, localCluster, loginCluster }: LinkAccountPanelRootProps) => {
76 return <Card className={classes.root}>
78 {isProcessing && <Grid container item direction="column" alignContent="center" spacing={24}>
80 Loading user info. Please wait.
82 <Grid item style={{ alignSelf: 'center' }}>
86 {!isProcessing && status === LinkAccountPanelStatus.INITIAL && targetUser && <div>
87 {isLocalUser(targetUser.uuid, localCluster) ? <Grid container spacing={24}>
88 <Grid container item direction="column" spacing={24}>
90 You are currently logged in as {displayUser(targetUser, true)}
93 You can link Arvados accounts. After linking, either login will take you to the same account.
96 <Grid container item direction="row" spacing={24}>
98 <Button disabled={!targetUser.isActive} color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_OTHER_LOGIN)}>
99 Add another login to this account
103 <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_ACCOUNT)}>
104 Use this login to access another account
108 {hasRemoteHosts && selectedCluster && <Grid container item direction="column" spacing={24}>
110 You can also link {displayUser(targetUser, false)} with an account from a remote cluster.
113 Please select the cluster that hosts the account you want to link with:
114 <Select id="remoteHostsDropdown" native defaultValue={selectedCluster} style={{ marginLeft: "1em" }}
115 onChange={(event) => setSelectedCluster(event.target.value)}>
116 {Object.keys(remoteHostsConfig).map((k) => k !== localCluster ? <option key={k} value={k}>{k}</option> : null)}
120 <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_REMOTE_ACCOUNT)}>
121 Link with an account on {hasRemoteHosts ? <label>{selectedCluster} </label> : null}
126 <Grid container spacing={24}>
127 <Grid container item direction="column" spacing={24}>
129 You are currently logged in as {displayUser(targetUser, true, true)}
131 {targetUser.isActive ?
132 (loginCluster === "" ?
134 This a remote account. You can link a local Arvados account to this one.
135 After linking, you can access the local account's data by logging into the
136 <b>{localCluster}</b> cluster as user <b>{targetUser.email}</b>
137 from <b>{targetUser.uuid.substr(0, 5)}</b>.
140 <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_LOCAL_TO_REMOTE)}>
141 Link an account from {localCluster} to this account
144 : <Grid item>Please visit cluster
145 <a href={remoteHostsConfig[loginCluster].workbench2Url + "/link_account"}>{loginCluster}</a>
146 to perform account linking.</Grid>
149 This an inactive remote account. An administrator must activate your
150 account before you can proceed. After your accounts is activated,
151 you can link a local Arvados account hosted by the <b>{localCluster}</b>
157 {!isProcessing && (status === LinkAccountPanelStatus.LINKING || status === LinkAccountPanelStatus.ERROR) && userToLink && targetUser &&
158 <Grid container spacing={24}>
159 {status === LinkAccountPanelStatus.LINKING && <Grid container item direction="column" spacing={24}>
161 Clicking 'Link accounts' will link {displayUser(userToLink, true, !isLocalUser(targetUser.uuid, localCluster))} to {displayUser(targetUser, true, !isLocalUser(targetUser.uuid, localCluster))}.
163 {(isLocalUser(targetUser.uuid, localCluster)) && <Grid item>
164 After linking, logging in as {displayUser(userToLink)} will log you into the same account as {displayUser(targetUser)}.
167 Any object owned by {displayUser(userToLink)} will be transfered to {displayUser(targetUser)}.
169 {!isLocalUser(targetUser.uuid, localCluster) && <Grid item>
170 You can access <b>{userToLink.email}</b> data by logging into <b>{localCluster}</b> with the <b>{targetUser.email}</b> account.
173 {error === LinkAccountPanelError.NON_ADMIN && <Grid item>
174 Cannot link admin account {displayUser(userToLink)} to non-admin account {displayUser(targetUser)}.
176 {error === LinkAccountPanelError.SAME_USER && <Grid item>
177 Cannot link {displayUser(targetUser)} to the same account.
179 {error === LinkAccountPanelError.INACTIVE && <Grid item>
180 Cannot link account {displayUser(userToLink)} to inactive account {displayUser(targetUser)}.
182 <Grid container item direction="row" spacing={24}>
184 <Button variant="contained" onClick={() => cancelLinking()}>
189 <Button disabled={status === LinkAccountPanelStatus.ERROR} color="primary" variant="contained" onClick={() => linkAccount()}>