18559: Disable user profile form fields when not admin or self
[arvados-workbench2.git] / src / views / user-profile-panel / user-profile-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 { Field, InjectedFormProps } from "redux-form";
7 import { TextField } from "components/text-field/text-field";
8 import { DataExplorer } from "views-components/data-explorer/data-explorer";
9 import { NativeSelectField } from "components/select-field/select-field";
10 import {
11     StyleRulesCallback,
12     WithStyles,
13     withStyles,
14     Card,
15     CardContent,
16     Button,
17     Typography,
18     Grid,
19     InputLabel,
20     Tabs, Tab,
21     Paper
22 } from '@material-ui/core';
23 import { ArvadosTheme } from 'common/custom-theme';
24 import { User } from "models/user";
25 import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
26 import { MY_ACCOUNT_VALIDATION } from "validators/validators";
27 import { USER_PROFILE_PANEL_ID } from 'store/user-profile/user-profile-actions';
28 import { noop } from 'lodash';
29 import { GroupsIcon } from 'components/icon/icon';
30 import { DataColumns } from 'components/data-table/data-table';
31 import { ResourceLinkHeadUuid, ResourceLinkHeadPermissionLevel, ResourceLinkHead, ResourceLinkDelete, ResourceLinkTailIsVisible } from 'views-components/data-explorer/renderers';
32 import { createTree } from 'models/tree';
33
34 type CssRules = 'root' | 'adminRoot' | 'gridItem' | 'label' | 'title' | 'description' | 'actions' | 'content';
35
36 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
37     root: {
38         width: '100%',
39         overflow: 'auto'
40     },
41     adminRoot: {
42         // ...theme.mixins.gutters()
43     },
44     gridItem: {
45         height: 45,
46         marginBottom: 20
47     },
48     label: {
49         fontSize: '0.675rem'
50     },
51     title: {
52         fontSize: '1.1rem',
53     },
54     description: {
55         color: theme.palette.grey["600"]
56     },
57     actions: {
58         display: 'flex',
59         justifyContent: 'flex-end'
60     },
61     content: {
62         // reserve space for the tab bar
63         height: `calc(100% - ${theme.spacing.unit * 7}px)`,
64     }
65 });
66
67 export interface UserProfilePanelRootActionProps {
68     openSetupShellAccount: (uuid: string) => void;
69     loginAs: (uuid: string) => void;
70     openDeactivateDialog: (uuid: string) => void;
71 }
72
73 export interface UserProfilePanelRootDataProps {
74     isAdmin: boolean;
75     isSelf: boolean;
76     isPristine: boolean;
77     isValid: boolean;
78     initialValues?: User;
79     localCluster: string;
80 }
81
82 const RoleTypes = [
83     { key: 'Bio-informatician', value: 'Bio-informatician' },
84     { key: 'Data Scientist', value: 'Data Scientist' },
85     { key: 'Analyst', value: 'Analyst' },
86     { key: 'Researcher', value: 'Researcher' },
87     { key: 'Software Developer', value: 'Software Developer' },
88     { key: 'System Administrator', value: 'System Administrator' },
89     { key: 'Other', value: 'Other' }
90 ];
91
92 type UserProfilePanelRootProps = InjectedFormProps<{}> & UserProfilePanelRootActionProps & UserProfilePanelRootDataProps & WithStyles<CssRules>;
93
94 export enum UserProfileGroupsColumnNames {
95     NAME = "Name",
96     PERMISSION = "Permission",
97     VISIBLE = "Visible to other members",
98     UUID = "UUID",
99     REMOVE = "Remove",
100 }
101
102 export const userProfileGroupsColumns: DataColumns<string> = [
103     {
104         name: UserProfileGroupsColumnNames.NAME,
105         selected: true,
106         configurable: true,
107         filters: createTree(),
108         render: uuid => <ResourceLinkHead uuid={uuid} />
109     },
110     {
111         name: UserProfileGroupsColumnNames.PERMISSION,
112         selected: true,
113         configurable: true,
114         filters: createTree(),
115         render: uuid => <ResourceLinkHeadPermissionLevel uuid={uuid} />
116     },
117     {
118         name: UserProfileGroupsColumnNames.VISIBLE,
119         selected: true,
120         configurable: true,
121         filters: createTree(),
122         render: uuid => <ResourceLinkTailIsVisible uuid={uuid} />
123     },
124     {
125         name: UserProfileGroupsColumnNames.UUID,
126         selected: true,
127         configurable: true,
128         filters: createTree(),
129         render: uuid => <ResourceLinkHeadUuid uuid={uuid} />
130     },
131     {
132         name: UserProfileGroupsColumnNames.REMOVE,
133         selected: true,
134         configurable: true,
135         filters: createTree(),
136         render: uuid => <ResourceLinkDelete uuid={uuid} />
137     },
138 ];
139
140 export const UserProfilePanelRoot = withStyles(styles)(
141     class extends React.Component<UserProfilePanelRootProps> {
142         state = {
143             value: 0,
144         };
145
146         componentDidMount() {
147             this.setState({ value: 0 });
148         }
149
150         render() {
151             return <Paper className={this.props.classes.root}>
152                 <Tabs value={this.state.value} onChange={this.handleChange} variant={"fullWidth"}>
153                     <Tab label="PROFILE" />
154                     <Tab label="GROUPS" />
155                     <Tab label="ADMIN" disabled={!this.props.isAdmin} />
156                 </Tabs>
157                 {this.state.value === 0 &&
158                     <CardContent>
159                         <form onSubmit={this.props.handleSubmit}>
160                             <Grid container spacing={24}>
161                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
162                                     <Field
163                                         label="First name"
164                                         name="firstName"
165                                         component={TextField as any}
166                                         disabled
167                                     />
168                                 </Grid>
169                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
170                                     <Field
171                                         label="Last name"
172                                         name="lastName"
173                                         component={TextField as any}
174                                         disabled
175                                     />
176                                 </Grid>
177                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
178                                     <Field
179                                         label="E-mail"
180                                         name="email"
181                                         component={TextField as any}
182                                         disabled
183                                     />
184                                 </Grid>
185                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
186                                     <Field
187                                         label="Username"
188                                         name="username"
189                                         component={TextField as any}
190                                         disabled
191                                     />
192                                 </Grid>
193                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
194                                     <Field
195                                         label="Organization"
196                                         name="prefs.profile.organization"
197                                         component={TextField as any}
198                                         validate={MY_ACCOUNT_VALIDATION}
199                                         required
200                                         disabled={!this.props.isAdmin && !this.props.isSelf}
201                                     />
202                                 </Grid>
203                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
204                                     <Field
205                                         label="E-mail at Organization"
206                                         name="prefs.profile.organization_email"
207                                         component={TextField as any}
208                                         validate={MY_ACCOUNT_VALIDATION}
209                                         required
210                                         disabled={!this.props.isAdmin && !this.props.isSelf}
211                                     />
212                                 </Grid>
213                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
214                                     <InputLabel className={this.props.classes.label} htmlFor="prefs.profile.role">Role</InputLabel>
215                                     <Field
216                                         id="prefs.profile.role"
217                                         name="prefs.profile.role"
218                                         component={NativeSelectField as any}
219                                         items={RoleTypes}
220                                         disabled={!this.props.isAdmin && !this.props.isSelf}
221                                     />
222                                 </Grid>
223                                 <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
224                                     <Field
225                                         label="Website"
226                                         name="prefs.profile.website_url"
227                                         component={TextField as any}
228                                         disabled={!this.props.isAdmin && !this.props.isSelf}
229                                     />
230                                 </Grid>
231                                 <Grid item sm={12}>
232                                     <Grid container direction="row" justify="flex-end">
233                                         <Button color="primary" onClick={this.props.reset} disabled={this.props.isPristine}>Discard changes</Button>
234                                         <Button
235                                             color="primary"
236                                             variant="contained"
237                                             type="submit"
238                                             disabled={this.props.isPristine || this.props.invalid || this.props.submitting}>
239                                             Save changes
240                                         </Button>
241                                     </Grid>
242                                 </Grid>
243                             </Grid>
244                         </form >
245                     </CardContent>
246                 }
247                 {this.state.value === 1 &&
248                     <div className={this.props.classes.content}>
249                         <DataExplorer
250                                 id={USER_PROFILE_PANEL_ID}
251                                 onRowClick={noop}
252                                 onRowDoubleClick={noop}
253                                 contextMenuColumn={false}
254                                 hideColumnSelector
255                                 hideSearchInput
256                                 paperProps={{
257                                     elevation: 0,
258                                 }}
259                                 dataTableDefaultView={
260                                     <DataTableDefaultView
261                                         icon={GroupsIcon}
262                                         messages={['Group list is empty.']} />
263                                 } />
264                     </div>}
265                 {this.state.value === 2 &&
266                     <Paper elevation={0} className={this.props.classes.adminRoot}>
267                         <Card elevation={0}>
268                             <CardContent>
269                                 <Grid container
270                                     direction="row"
271                                     justify={'flex-end'}
272                                     alignItems={'center'}>
273                                     <Grid item xs>
274                                         <Typography variant="h6" className={this.props.classes.title}>
275                                             Setup Account
276                                         </Typography>
277                                         <Typography variant="body1" className={this.props.classes.description}>
278                                             This button sets up a user. After setup, they will be able use Arvados. This dialog box also allows you to optionally set up a shell account for this user. The login name is automatically generated from the user's e-mail address.
279                                         </Typography>
280                                     </Grid>
281                                     <Grid item sm={'auto'} xs={12}>
282                                         <Button variant="contained"
283                                             color="primary"
284                                             onClick={() => {this.props.openSetupShellAccount(this.props.initialValues.uuid)}}
285                                             disabled={false}>
286                                             Setup Account
287                                         </Button>
288                                     </Grid>
289                                 </Grid>
290                             </CardContent>
291                         </Card>
292                         <Card elevation={0}>
293                             <CardContent>
294                                 <Grid container
295                                     direction="row"
296                                     justify={'flex-end'}
297                                     alignItems={'center'}>
298                                     <Grid item xs>
299                                         <Typography variant="h6" className={this.props.classes.title}>
300                                             Deactivate
301                                         </Typography>
302                                         <Typography variant="body1" className={this.props.classes.description}>
303                                             As an admin, you can deactivate and reset this user. This will remove all repository/VM permissions for the user. If you "setup" the user again, the user will have to sign the user agreement again. You may also want to reassign data ownership.
304                                         </Typography>
305                                     </Grid>
306                                     <Grid item sm={'auto'} xs={12}>
307                                         <Button variant="contained"
308                                             color="primary"
309                                             onClick={() => {this.props.openDeactivateDialog(this.props.initialValues.uuid)}}
310                                             disabled={false}>
311                                             Deactivate
312                                         </Button>
313                                     </Grid>
314                                 </Grid>
315                             </CardContent>
316                         </Card>
317                         <Card elevation={0}>
318                             <CardContent>
319                                 <Grid container
320                                     direction="row"
321                                     justify={'flex-end'}
322                                     alignItems={'center'}>
323                                     <Grid item xs>
324                                         <Typography variant="h6" className={this.props.classes.title}>
325                                             Log In
326                                         </Typography>
327                                         <Typography variant="body1" className={this.props.classes.description}>
328                                             As an admin, you can log in as this user. When you’ve finished, you will need to log out and log in again with your own account.
329                                         </Typography>
330                                     </Grid>
331                                     <Grid item sm={'auto'} xs={12}>
332                                         <Button variant="contained"
333                                             color="primary"
334                                             onClick={() => {this.props.loginAs(this.props.initialValues.uuid)}}
335                                             disabled={false}>
336                                             Log In
337                                         </Button>
338                                     </Grid>
339                                 </Grid>
340                             </CardContent>
341                         </Card>
342                     </Paper>}
343             </Paper >;
344         }
345
346         handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
347             this.setState({ value });
348         }
349
350     }
351 );