16848: Moves the "get new token" button up.
[arvados-workbench2.git] / src / views-components / token-dialog / token-dialog.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import {
7     Dialog,
8     DialogActions,
9     DialogTitle,
10     DialogContent,
11     WithStyles,
12     withStyles,
13     StyleRulesCallback,
14     Button,
15     Typography,
16     Tooltip
17 } from '@material-ui/core';
18 import * as CopyToClipboard from 'react-copy-to-clipboard';
19 import { ArvadosTheme } from '~/common/custom-theme';
20 import { withDialog } from '~/store/dialog/with-dialog';
21 import { WithDialogProps } from '~/store/dialog/with-dialog';
22 import { connect, DispatchProp } from 'react-redux';
23 import {
24     TokenDialogData,
25     getTokenDialogData,
26     TOKEN_DIALOG_NAME,
27 } from '~/store/token-dialog/token-dialog-actions';
28 import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
29 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
30 import { getNewExtraToken } from '~/store/auth/auth-action';
31 import { CopyIcon } from '~/components/icon/icon';
32
33 type CssRules = 'link' | 'paper' | 'button' | 'actionButton' | 'copyIcon' | 'inlineMonospaced';
34
35 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
36     link: {
37         color: theme.palette.primary.main,
38         textDecoration: 'none',
39         margin: '0px 4px'
40     },
41     paper: {
42         padding: theme.spacing.unit,
43         marginBottom: theme.spacing.unit * 2,
44         backgroundColor: theme.palette.grey["200"],
45         border: `1px solid ${theme.palette.grey["300"]}`
46     },
47     button: {
48         fontSize: '0.8125rem',
49         fontWeight: 600
50     },
51     actionButton: {
52         boxShadow: 'none',
53         marginTop: theme.spacing.unit * 2,
54         marginBottom: theme.spacing.unit * 2,
55         marginRight: theme.spacing.unit * 2,
56     },
57     copyIcon: {
58         marginLeft: theme.spacing.unit,
59         color: theme.palette.grey["500"],
60         cursor: 'pointer',
61         display: 'inline',
62         '& svg': {
63             fontSize: '1rem'
64         }
65     },
66     inlineMonospaced: {
67         fontFamily: 'Monospace',
68         fontSize: '1rem',
69         display: 'inline-block',
70     }
71 });
72
73 type TokenDialogProps = TokenDialogData & WithDialogProps<{}> & WithStyles<CssRules> & DispatchProp;
74
75 export class TokenDialogComponent extends React.Component<TokenDialogProps> {
76     onCopy = (message: string) => {
77         this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
78             message,
79             hideDuration: 2000,
80             kind: SnackbarKind.SUCCESS
81         }));
82     }
83
84     onGetNewToken = async () => {
85         const newToken = await this.props.dispatch<any>(getNewExtraToken());
86         if (newToken) {
87             this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
88                 message: 'New token retrieved',
89                 hideDuration: 2000,
90                 kind: SnackbarKind.SUCCESS
91             }));
92         } else {
93             this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
94                 message: 'Creating new tokens is not allowed',
95                 hideDuration: 2000,
96                 kind: SnackbarKind.WARNING
97             }));
98         }
99     }
100
101     getSnippet = ({ apiHost, token }: TokenDialogData) =>
102         `HISTIGNORE=$HISTIGNORE:'export ARVADOS_API_TOKEN=*'
103 export ARVADOS_API_TOKEN=${token}
104 export ARVADOS_API_HOST=${apiHost}
105 unset ARVADOS_API_HOST_INSECURE`
106
107     render() {
108         const { classes, open, closeDialog, ...data } = this.props;
109
110         return <Dialog
111             open={open}
112             onClose={closeDialog}
113             fullWidth={true}
114             maxWidth='md'>
115             <DialogTitle>Get API Token</DialogTitle>
116             <DialogContent>
117                 <Typography paragraph={true}>
118                     The Arvados API token is a secret key that enables the Arvados SDKs to access Arvados with the proper permissions.
119                     <Typography component='span'>
120                         For more information see
121                         <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>
122                             Getting an API token.
123                         </a>
124                     </Typography>
125                 </Typography>
126                 <Typography  paragraph={true}>
127                     <Typography component='span'>
128                         Your Arvados API host is: <Typography className={classes.inlineMonospaced} component='span'>{data.apiHost} {<Tooltip title="Copy to clipboard">
129                             <span className={classes.copyIcon}>
130                                 <CopyToClipboard text={data.apiHost} onCopy={() => this.onCopy("API host copied")}>
131                                     <CopyIcon />
132                                 </CopyToClipboard>
133                             </span>
134                         </Tooltip>}
135                         </Typography>
136                     </Typography>
137                     <Typography component='span'>
138                         Your token is: <Typography className={classes.inlineMonospaced} component='span'>{data.token} {<Tooltip title="Copy to clipboard">
139                             <span className={classes.copyIcon}>
140                                 <CopyToClipboard text={data.token} onCopy={() => this.onCopy("Token copied")}>
141                                     <CopyIcon />
142                                 </CopyToClipboard>
143                             </span>
144                             </Tooltip>}
145                         </Typography>
146                     </Typography>
147                     <Typography component='span'>
148                     { data.tokenExpiration
149                         ? `Expires at: ${data.tokenExpiration.toLocaleString()}`
150                         : `This token does not have an expiration date`
151                     }
152                     </Typography>
153                 </Typography>
154                 { this.props.canCreateNewTokens && <Button
155                     onClick={() => this.onGetNewToken()}
156                     color="primary"
157                     size="small"
158                     variant="contained"
159                     className={classes.actionButton}
160                 >
161                     GET NEW TOKEN
162                 </Button> }
163                 <Typography paragraph={true}>
164                     Paste the following lines at a shell prompt to set up the necessary environment for Arvados SDKs to authenticate to your account.
165                 </Typography>
166                 <DefaultCodeSnippet lines={[this.getSnippet(data)]} />
167                 <CopyToClipboard text={this.getSnippet(data)} onCopy={() => this.onCopy('Shell code block copied')}>
168                     <Button
169                         color="primary"
170                         size="small"
171                         variant="contained"
172                         className={classes.actionButton}
173                     >
174                         COPY TO CLIPBOARD
175                     </Button>
176                 </CopyToClipboard>
177                 <Typography >
178                     Arvados
179                             <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>virtual machines</a>
180                     do this for you automatically. This setup is needed only when you use the API remotely (e.g., from your own workstation).
181                 </Typography>
182             </DialogContent>
183             <DialogActions>
184                 <Button onClick={closeDialog} className={classes.button} color="primary">CLOSE</Button>
185             </DialogActions>
186         </Dialog>;
187     }
188 }
189
190 export const TokenDialog =
191     withStyles(styles)(
192         connect(getTokenDialogData)(
193             withDialog(TOKEN_DIALOG_NAME)(TokenDialogComponent)));
194