9dc28d66b62697507c314110d3d82ef2c8e8b1b4
[arvados-workbench2.git] / src / views / link-account-panel / link-account-panel-root.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from 'react';
6 import {
7     StyleRulesCallback,
8     WithStyles,
9     withStyles,
10     Card,
11     CardContent,
12     Button,
13     Grid,
14     Select,
15     CircularProgress
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';
23
24 type CssRules = 'root';
25
26 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
27     root: {
28         width: '100%',
29         overflow: 'auto',
30         display: 'flex'
31     }
32 });
33
34 export interface LinkAccountPanelRootDataProps {
35     targetUser?: UserResource;
36     userToLink?: UserResource;
37     remoteHostsConfig: { [key: string]: Config };
38     hasRemoteHosts: boolean;
39     localCluster: string;
40     loginCluster: string;
41     status: LinkAccountPanelStatus;
42     error: LinkAccountPanelError;
43     selectedCluster?: string;
44     isProcessing: boolean;
45 }
46
47 export interface LinkAccountPanelRootActionProps {
48     startLinking: (type: LinkAccountType) => void;
49     cancelLinking: () => void;
50     linkAccount: () => void;
51     setSelectedCluster: (cluster: string) => void;
52 }
53
54 function displayUser(user: UserResource, showCreatedAt: boolean = false, showCluster: boolean = false) {
55     const disp: JSX.Element[] = [];
56     disp.push(<span><b>{user.email}</b> ({user.username}, {user.uuid})</span>);
57     if (showCluster) {
58         const homeCluster = user.uuid.substring(0, 5);
59         disp.push(<span> hosted on cluster <b>{homeCluster}</b> and </span>);
60     }
61     if (showCreatedAt) {
62         disp.push(<span> created on <b>{formatDate(user.createdAt)}</b></span>);
63     }
64     return disp;
65 }
66
67 function isLocalUser(uuid: string, localCluster: string) {
68     return uuid.substring(0, 5) === localCluster;
69 }
70
71 type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles<CssRules>;
72
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}><CardContent>
77             { isProcessing && <Grid container item direction="column" alignContent="center" spacing={24}>
78                 <Grid item>
79                     Loading user info. Please wait.
80                 </Grid>
81                 <Grid item style={{ alignSelf: 'center' }}>
82                     <CircularProgress />
83                 </Grid>
84             </Grid> }
85
86             { !isProcessing && status === LinkAccountPanelStatus.INITIAL && targetUser && <div>
87                 { isLocalUser(targetUser.uuid, localCluster)
88                 ? <Grid container spacing={24}>
89                     <Grid container item direction="column" spacing={24}>
90                         <Grid item>
91                             You are currently logged in as {displayUser(targetUser, true)}
92                         </Grid>
93                         <Grid item>
94                             You can link Arvados accounts. After linking, either login will take you to the same account.
95                         </Grid >
96                     </Grid>
97                     <Grid container item direction="row" spacing={24}>
98                         <Grid item>
99                             <Button disabled={!targetUser.isActive} color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_OTHER_LOGIN)}>
100                                 Add another login to this account
101                             </Button>
102                         </Grid>
103                         <Grid item>
104                             <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_ACCOUNT)}>
105                                 Use this login to access another account
106                             </Button>
107                         </Grid>
108                     </Grid>
109                     {hasRemoteHosts && selectedCluster && <Grid container item direction="column" spacing={24}>
110                         <Grid item>
111                             You can also link {displayUser(targetUser, false)} with an account from a remote cluster.
112                         </Grid>
113                         <Grid item>
114                             Please select the cluster that hosts the account you want to link with:
115                             <Select id="remoteHostsDropdown" native defaultValue={selectedCluster} style={{ marginLeft: "1em" }}
116                                 onChange={(event) => setSelectedCluster(event.target.value)}>
117                                 {Object.keys(remoteHostsConfig).map((k) => k !== localCluster ? <option key={k} value={k}>{k}</option> : null)}
118                             </Select>
119                         </Grid>
120                         <Grid item>
121                             <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_REMOTE_ACCOUNT)}>
122                                 Link with an account on&nbsp;{hasRemoteHosts ? <label>{selectedCluster} </label> : null}
123                             </Button>
124                         </Grid>
125                     </Grid>}
126                 </Grid>
127                 : <Grid container spacing={24}>
128                     <Grid container item direction="column" spacing={24}>
129                         <Grid item>
130                             You are currently logged in as {displayUser(targetUser, true, true)}
131                         </Grid>
132                         { targetUser.isActive
133                         ? (loginCluster === ""
134                             ? <> <Grid item>
135                                 This a remote account. You can link a local Arvados account to this one.
136                                 After linking, you can access the local account's data by logging into the
137                                 <b>{localCluster}</b> cluster as user <b>{targetUser.email}</b>
138                                 from <b>{targetUser.uuid.substring(0, 5)}</b>.
139                             </Grid >
140                             <Grid item>
141                                 <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_LOCAL_TO_REMOTE)}>
142                                     Link an account from {localCluster} to this account
143                                 </Button>
144                             </Grid></>
145                             : <Grid item>Please visit cluster
146                                 <a href={remoteHostsConfig[loginCluster].workbench2Url + "/link_account"}>{loginCluster}</a> to perform account linking.
147                             </Grid> )
148                         : <Grid item>
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> cluster to this one.
152                         </Grid> }
153                     </Grid>
154                 </Grid> }
155             </div> }
156
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}>
160                         <Grid item>
161                             Clicking 'Link accounts' will link {displayUser(userToLink, true, !isLocalUser(targetUser.uuid, localCluster))} to {displayUser(targetUser, true, !isLocalUser(targetUser.uuid, localCluster))}.
162                         </Grid>
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)}.
165                         </Grid>}
166                         <Grid item>
167                             Any object owned by {displayUser(userToLink)} will be transfered to {displayUser(targetUser)}.
168                         </Grid>
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.
171                         </Grid>}
172                     </Grid>}
173                     {error === LinkAccountPanelError.NON_ADMIN && <Grid item>
174                         Cannot link admin account {displayUser(userToLink)} to non-admin account {displayUser(targetUser)}.
175                     </Grid>}
176                     {error === LinkAccountPanelError.SAME_USER && <Grid item>
177                         Cannot link {displayUser(targetUser)} to the same account.
178                     </Grid>}
179                     {error === LinkAccountPanelError.INACTIVE && <Grid item>
180                         Cannot link account {displayUser(userToLink)} to inactive account {displayUser(targetUser)}.
181                     </Grid>}
182                     <Grid container item direction="row" spacing={24}>
183                         <Grid item>
184                             <Button variant="contained" onClick={() => cancelLinking()}>
185                                 Cancel
186                             </Button>
187                         </Grid>
188                         <Grid item>
189                             <Button disabled={status === LinkAccountPanelStatus.ERROR} color="primary" variant="contained" onClick={() => linkAccount()}>
190                                 Link accounts
191                             </Button>
192                         </Grid>
193                     </Grid>
194                 </Grid>}
195         </CardContent></Card>;
196     });