• 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

90.91
/src/js/components/settings/user-management/userform.js
1
// Copyright 2017 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, { useMemo, useState } from 'react';
15

16
import { InfoOutlined } from '@mui/icons-material';
17
import {
18
  Checkbox,
19
  Dialog,
20
  DialogActions,
21
  DialogContent,
22
  DialogTitle,
23
  FormControl,
24
  FormControlLabel,
25
  FormHelperText,
26
  InputLabel,
27
  ListItemText,
28
  MenuItem,
29
  Select,
30
  Tooltip
31
} from '@mui/material';
32

33
import pluralize from 'pluralize';
34

35
import { BENEFITS } from '../../../constants/appConstants';
36
import { rolesById, rolesByName, uiPermissionsById } from '../../../constants/userConstants';
37
import { toggle } from '../../../helpers';
38
import EnterpriseNotification from '../../common/enterpriseNotification';
39
import Form from '../../common/forms/form';
40
import PasswordInput from '../../common/forms/passwordinput';
41
import TextInput from '../../common/forms/textinput';
42

43
export const UserRolesSelect = ({ currentUser, disabled, onSelect, roles, user }) => {
6✔
44
  const [selectedRoleIds, setSelectedRoleIds] = useState(
33✔
45
    (user.roles || [rolesByName.admin]).reduce((accu, roleId) => {
41✔
46
      const foundRole = roles[roleId];
33✔
47
      if (foundRole) {
33✔
48
        accu.push(roleId);
32✔
49
      }
50
      return accu;
33✔
51
    }, [])
52
  );
53

54
  const onInputChange = ({ target: { value } }) => {
33✔
55
    const { roles = [] } = user;
4!
56
    let newlySelectedRoles = value;
4✔
57
    if (value.includes('')) {
4!
58
      newlySelectedRoles = [];
×
59
    }
60
    const hadRoleChanges =
61
      roles.length !== newlySelectedRoles.length || roles.some(currentRoleId => !newlySelectedRoles.some(roleId => currentRoleId === roleId));
4✔
62
    setSelectedRoleIds(newlySelectedRoles);
4✔
63
    onSelect(newlySelectedRoles, hadRoleChanges);
4✔
64
  };
65

66
  const { editableRoles, showRoleUsageNotification } = useMemo(() => {
33✔
67
    const editableRoles = Object.entries(roles).map(([id, role]) => {
8✔
68
      const enabled = selectedRoleIds.some(roleId => id === roleId);
42✔
69
      return { enabled, id, ...role };
42✔
70
    });
71
    const showRoleUsageNotification = selectedRoleIds.reduce((accu, roleId) => {
8✔
72
      const { permissions, uiPermissions } = roles[roleId];
5✔
73
      const hasUiApiAccess = [rolesByName.ci].includes(roleId)
5✔
74
        ? false
75
        : roleId === rolesByName.admin ||
6✔
76
          permissions.some(permission => ![rolesByName.deploymentCreation.action].includes(permission.action)) ||
×
77
          uiPermissions.userManagement.includes(uiPermissionsById.read.value);
78
      if (hasUiApiAccess) {
5✔
79
        return false;
4✔
80
      }
81
      return typeof accu !== 'undefined' ? accu : true;
1!
82
    }, undefined);
83
    return { editableRoles, showRoleUsageNotification };
8✔
84
    // eslint-disable-next-line react-hooks/exhaustive-deps
85
  }, [JSON.stringify(roles), selectedRoleIds]);
86

87
  return (
33✔
88
    <div className="flexbox" style={{ alignItems: 'flex-end' }}>
89
      <FormControl id="roles-form" style={{ maxWidth: 400 }}>
90
        <InputLabel id="roles-selection-label">Roles</InputLabel>
91
        <Select
92
          labelId="roles-selection-label"
93
          id={`roles-selector-${selectedRoleIds.length}`}
94
          disabled={disabled}
95
          multiple
96
          value={selectedRoleIds}
97
          required
98
          onChange={onInputChange}
99
          renderValue={selected => selected.map(role => roles[role].name).join(', ')}
40✔
100
        >
101
          {editableRoles.map(role => (
102
            <MenuItem id={role.id} key={role.id} value={role.id}>
192✔
103
              <Checkbox id={`${role.id}-checkbox`} checked={role.enabled} />
104
              <ListItemText id={`${role.id}-text`} primary={role.name} />
105
            </MenuItem>
106
          ))}
107
        </Select>
108
        {showRoleUsageNotification && (
34✔
109
          <FormHelperText className="info">
110
            The selected {pluralize('role', selectedRoleIds.length)} may prevent {currentUser.email === user.email ? 'you' : <i>{user.email}</i>} from using the
1!
111
            Mender UI.
112
            <br />
113
            Consider adding the <i>{rolesById[rolesByName.readOnly].name}</i> role as well.
114
          </FormHelperText>
115
        )}
116
      </FormControl>
117
      <EnterpriseNotification className="margin-left-small" id={BENEFITS.rbac.id} />
118
    </div>
119
  );
120
};
121

122
const PasswordLabel = () => (
6✔
123
  <div className="flexbox center-aligned">
6✔
124
    Optional
125
    <Tooltip
126
      title={
127
        <>
128
          <p>You can skip setting a password for now - you can opt to send the new user an email containing a password reset link by checking the box below.</p>
129
          <p>Organizations using Single Sign-On or other means of authorization may want to create users with no password.</p>
130
        </>
131
      }
132
    >
133
      <InfoOutlined fontSize="small" className="margin-left-small" />
134
    </Tooltip>
135
  </div>
136
);
137

138
export const UserForm = ({ closeDialog, currentUser, canManageUsers, isEnterprise, roles, submit }) => {
6✔
139
  const [hadRoleChanges, setHadRoleChanges] = useState(false);
6✔
140
  const [selectedRoles, setSelectedRoles] = useState();
6✔
141
  const [shouldResetPassword, setShouldResetPassword] = useState(false);
6✔
142

143
  const onSelect = (newlySelectedRoles, hadRoleChanges) => {
6✔
144
    setSelectedRoles(newlySelectedRoles);
×
145
    setHadRoleChanges(hadRoleChanges);
×
146
  };
147

148
  const onSubmit = data => {
6✔
149
    const { password, ...remainder } = data;
1✔
150
    const roleData = hadRoleChanges ? { roles: selectedRoles } : {};
1!
151
    return submit({ ...remainder, ...roleData, password }, 'create');
1✔
152
  };
153

154
  const togglePasswordReset = () => setShouldResetPassword(toggle);
6✔
155

156
  return (
6✔
157
    <Dialog open={true} fullWidth={true} maxWidth="sm">
158
      <DialogTitle>Create new user</DialogTitle>
159
      <DialogContent style={{ overflowY: 'initial' }}>
160
        <Form onSubmit={onSubmit} handleCancel={closeDialog} submitLabel="Create user" showButtons={true} autocomplete="off">
161
          <TextInput hint="Email" label="Email" id="email" validations="isLength:1,isEmail,trim" required autocomplete="off" />
162
          <PasswordInput
163
            id="password"
164
            className="edit-pass"
165
            autocomplete="off"
166
            create
167
            edit={false}
168
            generate
169
            InputLabelProps={{ shrink: true }}
170
            label={<PasswordLabel />}
171
            placeholder="Password"
172
            validations="isLength:8"
173
          />
174
          <FormControlLabel
175
            control={<Checkbox checked={shouldResetPassword} onChange={togglePasswordReset} />}
176
            label="Send an email to the user containing a link to reset the password"
177
          />
178
          <UserRolesSelect currentUser={currentUser} disabled={!(canManageUsers && isEnterprise)} onSelect={onSelect} roles={roles} user={{}} />
11✔
179
        </Form>
180
      </DialogContent>
181
      <DialogActions />
182
    </Dialog>
183
  );
184
};
185

186
export default UserForm;
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