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

mendersoftware / gui / 1308594387

28 May 2024 12:32PM UTC coverage: 83.391% (-16.6%) from 99.964%
1308594387

Pull #4424

gitlab-ci

mzedel
feat: restructured account menu & added option to switch tenant in supporting setups

Ticket: MEN-6906
Changelog: Title
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #4424: MEN-6906 - tenant switching - wip

4466 of 6371 branches covered (70.1%)

27 of 31 new or added lines in 3 files covered. (87.1%)

1670 existing lines in 164 files now uncovered.

8480 of 10169 relevant lines covered (83.39%)

140.77 hits per line

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

90.48
/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
  FormHelperText,
25
  InputLabel,
26
  ListItemText,
27
  MenuItem,
28
  Select,
29
  Tooltip
30
} from '@mui/material';
31

32
import pluralize from 'pluralize';
33

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

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

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

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

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

121
const PasswordLabel = () => (
6✔
122
  <div className="flexbox center-aligned">
7✔
123
    Optional
124
    <Tooltip
125
      title={
126
        <>
127
          <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>
128
          <p>Organizations using Single Sign-On or other means of authorization may want to create users with no password.</p>
129
        </>
130
      }
131
    >
132
      <InfoOutlined fontSize="small" className="margin-left-small" />
133
    </Tooltip>
134
  </div>
135
);
136

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

141
  const onSelect = (newlySelectedRoles, hadRoleChanges) => {
7✔
UNCOV
142
    setSelectedRoles(newlySelectedRoles);
×
UNCOV
143
    setHadRoleChanges(hadRoleChanges);
×
144
  };
145

146
  const onSubmit = data => {
7✔
147
    const { password, ...remainder } = data;
2✔
148
    const roleData = hadRoleChanges ? { roles: selectedRoles } : {};
2!
149
    return submit({ ...remainder, ...roleData, password }, 'create');
2✔
150
  };
151

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

179
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