• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 1113439055

19 Dec 2023 09:01PM UTC coverage: 82.752% (-17.2%) from 99.964%
1113439055

Pull #4258

gitlab-ci

mender-test-bot
chore: Types update

Signed-off-by: Mender Test Bot <mender@northern.tech>
Pull Request #4258: chore: Types update

4326 of 6319 branches covered (0.0%)

8348 of 10088 relevant lines covered (82.75%)

189.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.0
/src/js/components/settings/user-management/userdefinition.js
1
// Copyright 2021 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useEffect, useMemo, useState } from 'react';
15

16
// material ui
17
import { Close as CloseIcon } from '@mui/icons-material';
18
import { Button, Checkbox, Divider, Drawer, FormControl, FormControlLabel, FormHelperText, IconButton, InputLabel, TextField } from '@mui/material';
19
import { makeStyles } from 'tss-react/mui';
20

21
import validator from 'validator';
22

23
import { mapUserRolesToUiPermissions } from '../../../actions/userActions';
24
import { uiPermissionsByArea, uiPermissionsById } from '../../../constants/userConstants';
25
import { toggle } from '../../../helpers';
26
import { TwoColumnData } from '../../common/configurationobject';
27
import { OAuth2Providers, genericProvider } from '../../login/oauth2providers';
28
import { UserRolesSelect } from './userform';
29

30
const useStyles = makeStyles()(theme => ({
5✔
31
  actionButtons: { justifyContent: 'flex-end' },
32
  divider: { marginTop: theme.spacing(4) },
33
  leftButton: { marginRight: theme.spacing(2) },
34
  oauthIcon: { fontSize: 36, marginRight: 10 },
35
  widthLimit: { maxWidth: 400 }
36
}));
37

38
export const getUserSSOState = user => {
5✔
39
  const { sso = [] } = user;
52✔
40
  const isOAuth2 = !!sso.length;
52✔
41
  const provider = isOAuth2 ? OAuth2Providers.find(provider => sso.some(({ kind }) => kind.includes(provider.id))) ?? genericProvider : null;
52!
42
  return { isOAuth2, provider };
52✔
43
};
44

45
const mapPermissions = permissions => permissions.map(permission => uiPermissionsById[permission].title).join(', ');
57✔
46

47
const scopedPermissionAreas = ['groups', 'releases'];
5✔
48

49
export const UserDefinition = ({ currentUser, isEnterprise, onCancel, onSubmit, onRemove, roles, selectedUser }) => {
5✔
50
  const { email = '', id } = selectedUser;
42✔
51

52
  const { classes } = useStyles();
42✔
53

54
  const [nameError, setNameError] = useState(false);
42✔
55
  const [hadRoleChanges, setHadRoleChanges] = useState(false);
42✔
56
  const [selectedRoles, setSelectedRoles] = useState([]);
42✔
57
  const [shouldResetPassword, setShouldResetPassword] = useState(false);
42✔
58
  const [currentEmail, setCurrentEmail] = useState('');
42✔
59

60
  useEffect(() => {
42✔
61
    setCurrentEmail(email);
8✔
62
  }, [email]);
63

64
  useEffect(() => {
42✔
65
    setSelectedRoles(selectedUser.roles || []);
8✔
66
  }, [selectedUser.roles]);
67

68
  const validateNameChange = ({ target: { value } }) => {
42✔
69
    setNameError(!validator.isEmail(value) || validator.isEmpty(value));
14✔
70
    setCurrentEmail(value);
14✔
71
  };
72

73
  const onRemoveClick = () => {
42✔
74
    onRemove(selectedUser);
1✔
75
  };
76

77
  const onRolesSelect = (newlySelectedRoles, hadRoleChanges) => {
42✔
78
    setSelectedRoles(newlySelectedRoles);
4✔
79
    setHadRoleChanges(hadRoleChanges);
4✔
80
  };
81

82
  const onSubmitClick = () => {
42✔
83
    if (id && !hadRoleChanges && email === currentEmail) {
1!
84
      return onSubmit(null, 'edit', id, shouldResetPassword ? email : null);
×
85
    }
86
    const changedRoles = hadRoleChanges ? { roles: selectedRoles } : {};
1!
87
    const submissionData = { ...selectedUser, ...changedRoles, email: currentEmail };
1✔
88
    return onSubmit(submissionData, 'edit', id, shouldResetPassword ? currentEmail : null);
1!
89
  };
90

91
  const togglePasswordReset = () => setShouldResetPassword(toggle);
42✔
92

93
  const { areas, groups } = useMemo(() => {
42✔
94
    const emptySelection = { areas: {}, groups: {}, releases: {} };
16✔
95
    if (!(selectedRoles && roles)) {
16!
96
      return emptySelection;
×
97
    }
98

99
    return Object.entries(mapUserRolesToUiPermissions(selectedRoles, roles)).reduce((accu, [key, values]) => {
16✔
100
      if (scopedPermissionAreas.includes(key)) {
80✔
101
        accu[key] = Object.entries(values).reduce((groupsAccu, [name, uiPermissions]) => {
32✔
102
          groupsAccu[name] = mapPermissions(uiPermissions);
9✔
103
          return groupsAccu;
9✔
104
        }, {});
105
      } else {
106
        accu.areas[uiPermissionsByArea[key].title] = mapPermissions(values);
48✔
107
      }
108
      return accu;
80✔
109
    }, emptySelection);
110
  }, [selectedRoles, roles]);
111

112
  const isSubmitDisabled = !selectedRoles.length;
42✔
113

114
  const { isOAuth2, provider } = getUserSSOState(selectedUser);
42✔
115
  const rolesClasses = isEnterprise ? '' : 'muted';
42!
116
  return (
42✔
117
    <Drawer anchor="right" open={!!id} PaperProps={{ style: { minWidth: 600, width: '50vw' } }}>
118
      <div className="flexbox margin-bottom-small space-between">
119
        <h3>Edit user</h3>
120
        <div className="flexbox center-aligned">
121
          {currentUser.id !== id && (
84✔
122
            <Button className={`flexbox center-aligned ${classes.leftButton}`} color="secondary" onClick={onRemoveClick}>
123
              delete user
124
            </Button>
125
          )}
126
          <IconButton onClick={onCancel} aria-label="close">
127
            <CloseIcon />
128
          </IconButton>
129
        </div>
130
      </div>
131
      <Divider />
132
      <FormControl className={classes.widthLimit}>
133
        <TextField label="Email" id="email" value={currentEmail} disabled={isOAuth2 || currentUser.id === id} error={nameError} onChange={validateNameChange} />
84✔
134
        {nameError && <FormHelperText className="warning">Please enter a valid email address</FormHelperText>}
54✔
135
      </FormControl>
136
      {isOAuth2 ? (
42!
137
        <div className="flexbox margin-top-small margin-bottom">
138
          <div className={classes.oauthIcon}>{provider.icon}</div>
139
          <div className="info">
140
            This user logs in using his <strong>{provider.name}</strong> account.
141
            <br />
142
            He can connect to {provider.name} to update his login settings.
143
          </div>
144
        </div>
145
      ) : (
146
        <FormControlLabel
147
          control={<Checkbox checked={shouldResetPassword} onChange={togglePasswordReset} />}
148
          label="Send an email to the user containing a link to reset the password"
149
        />
150
      )}
151
      <UserRolesSelect disabled={!isEnterprise} currentUser={currentUser} onSelect={onRolesSelect} roles={roles} user={selectedUser} />
152
      {!!(Object.keys(groups).length || Object.keys(areas).length) && (
146✔
153
        <InputLabel className="margin-top" shrink>
154
          Role permissions
155
        </InputLabel>
156
      )}
157
      <TwoColumnData className={rolesClasses} config={areas} />
158
      {!!Object.keys(groups).length && (
64✔
159
        <>
160
          <InputLabel className="margin-top-small" shrink>
161
            Device groups
162
          </InputLabel>
163
          <TwoColumnData className={rolesClasses} config={groups} />
164
        </>
165
      )}
166
      <Divider className={classes.divider} light />
167
      <div className={`flexbox centered margin-top ${classes.actionButtons}`}>
168
        <Button className={classes.leftButton} onClick={onCancel}>
169
          Cancel
170
        </Button>
171
        <Button color="secondary" variant="contained" disabled={isSubmitDisabled} target="_blank" onClick={onSubmitClick}>
172
          Save
173
        </Button>
174
      </div>
175
    </Drawer>
176
  );
177
};
178

179
export default UserDefinition;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc