Skip to content

Commit 11d0080

Browse files
authored
Move change user password modal (#806)
1 parent 8bea9ab commit 11d0080

File tree

5 files changed

+232
-22
lines changed

5 files changed

+232
-22
lines changed

portal-ui/src/screens/Console/Account/ChangePasswordModal.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ const ChangePassword = ({
6666
return;
6767
}
6868

69+
if (newPassword.length < 8) {
70+
setModalErrorSnackMessage("Passwords must be at least 8 characters long");
71+
return;
72+
}
73+
6974
if (loading) {
7075
return;
7176
}

portal-ui/src/screens/Console/Account/ChangeUserPasswordModal.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ const styles = (theme: Theme) =>
4444
interface IChangeUserPasswordProps {
4545
classes: any;
4646
open: boolean;
47-
selectedUser: User | null;
47+
userName: string;
4848
closeModal: () => void;
4949
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
5050
}
5151

5252
const ChangeUserPassword = ({
5353
classes,
5454
open,
55-
selectedUser,
55+
userName,
5656
closeModal,
5757
setModalErrorSnackMessage,
5858
}: IChangeUserPasswordProps) => {
@@ -67,9 +67,15 @@ const ChangeUserPassword = ({
6767
return;
6868
}
6969
setLoading(true);
70+
71+
if (newPassword.length < 8) {
72+
setModalErrorSnackMessage("Passwords must be at least 8 characters long");
73+
setLoading(false);
74+
return;
75+
}
7076

7177
let request: ChangeUserPasswordRequest = {
72-
selectedUser: String(selectedUser?.accessKey),
78+
selectedUser: userName,
7379
newSecretKey: newPassword,
7480
};
7581

@@ -110,7 +116,7 @@ const ChangeUserPassword = ({
110116
>
111117
<Grid container>
112118
<Grid item xs={12} className={classes.formScrollable}>
113-
<h3>Change password for {selectedUser?.accessKey}</h3>
119+
<h3>Change password for {userName}</h3>
114120
<Grid item xs={12}>
115121
<InputBoxWrapper
116122
id="new-password"
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2021 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import React, { useState } from "react";
18+
import { connect } from "react-redux";
19+
import {
20+
Button,
21+
Dialog,
22+
DialogActions,
23+
DialogContent,
24+
DialogContentText,
25+
DialogTitle,
26+
LinearProgress,
27+
} from "@material-ui/core";
28+
import api from "../../../common/api";
29+
import { User, UsersList } from "./types";
30+
import { setErrorSnackMessage } from "../../../actions";
31+
import history from "../../../history";
32+
33+
interface IDeleteUserProps {
34+
closeDeleteModalAndRefresh: (refresh: boolean) => void;
35+
deleteOpen: boolean;
36+
userName: string;
37+
setErrorSnackMessage: typeof setErrorSnackMessage;
38+
}
39+
40+
const DeleteUserString = ({
41+
closeDeleteModalAndRefresh,
42+
deleteOpen,
43+
userName,
44+
setErrorSnackMessage,
45+
}: IDeleteUserProps) => {
46+
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
47+
48+
const removeRecord = () => {
49+
if (deleteLoading) {
50+
return;
51+
}
52+
if (userName == null) {
53+
return;
54+
}
55+
setDeleteLoading(true);
56+
api
57+
.invoke("DELETE", `/api/v1/users/${userName}`, {
58+
id: userName,
59+
})
60+
.then((res: UsersList) => {
61+
setDeleteLoading(false);
62+
closeDeleteModalAndRefresh(true);
63+
})
64+
.catch((err) => {
65+
setDeleteLoading(false);
66+
setErrorSnackMessage(err);
67+
});
68+
};
69+
70+
if (userName === null) {
71+
return <div />;
72+
}
73+
74+
return (
75+
<Dialog
76+
open={deleteOpen}
77+
onClose={() => {
78+
closeDeleteModalAndRefresh(false);
79+
}}
80+
aria-labelledby="alert-dialog-title"
81+
aria-describedby="alert-dialog-description"
82+
>
83+
<DialogTitle id="alert-dialog-title">Delete User</DialogTitle>
84+
<DialogContent>
85+
{deleteLoading && <LinearProgress />}
86+
<DialogContentText id="alert-dialog-description">
87+
Are you sure you want to delete user <b>{userName}</b>?
88+
</DialogContentText>
89+
</DialogContent>
90+
<DialogActions>
91+
<Button
92+
onClick={() => {
93+
closeDeleteModalAndRefresh(false);
94+
}}
95+
color="primary"
96+
disabled={deleteLoading}
97+
>
98+
Cancel
99+
</Button>
100+
<Button
101+
onClick={() => {
102+
removeRecord();
103+
closeDeleteModalAndRefresh(true);
104+
history.push(`/users/`);
105+
}}
106+
color="secondary"
107+
autoFocus
108+
>
109+
Delete
110+
</Button>
111+
</DialogActions>
112+
</Dialog>
113+
);
114+
};
115+
116+
const mapDispatchToProps = {
117+
setErrorSnackMessage,
118+
};
119+
120+
const connector = connect(null, mapDispatchToProps);
121+
122+
export default connector(DeleteUserString);

portal-ui/src/screens/Console/Users/ListUsers.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import TableWrapper from "../Common/TableWrapper/TableWrapper";
3737
import SetPolicy from "../Policies/SetPolicy";
3838
import PageHeader from "../Common/PageHeader/PageHeader";
3939
import history from "../../../history";
40-
import ChangeUserPasswordModal from "../Account/ChangeUserPasswordModal";
4140

4241
const styles = (theme: Theme) =>
4342
createStyles({
@@ -91,8 +90,6 @@ const ListUsers = ({ classes, setErrorSnackMessage }: IUsersProps) => {
9190
const [filter, setFilter] = useState<string>("");
9291
const [checkedUsers, setCheckedUsers] = useState<string[]>([]);
9392
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
94-
const [ChangeUserPasswordModalOpen, setChangeUserPasswordModalOpen] =
95-
useState<boolean>(false);
9693

9794
const fetchRecords = useCallback(() => {
9895
setLoading(true);
@@ -168,14 +165,8 @@ const ListUsers = ({ classes, setErrorSnackMessage }: IUsersProps) => {
168165

169166
const userLoggedIn = atob(localStorage.getItem("userLoggedIn") || "");
170167

171-
const setNewPW = (selectionElement: any): void => {
172-
setChangeUserPasswordModalOpen(true);
173-
setSelectedUser(selectionElement);
174-
};
175-
176168
const tableActions = [
177169
{ type: "view", onClick: viewAction },
178-
{ type: "edit", onClick: setNewPW },
179170
{
180171
type: "delete",
181172
onClick: deleteAction,
@@ -223,13 +214,6 @@ const ListUsers = ({ classes, setErrorSnackMessage }: IUsersProps) => {
223214
}}
224215
/>
225216
)}
226-
{ChangeUserPasswordModalOpen && (
227-
<ChangeUserPasswordModal
228-
open={ChangeUserPasswordModalOpen}
229-
closeModal={() => setChangeUserPasswordModalOpen(false)}
230-
selectedUser={selectedUser}
231-
/>
232-
)}
233217
<PageHeader label={"Users"} />
234218
<Grid container>
235219
<Grid item xs={12} className={classes.container}>

portal-ui/src/screens/Console/Users/UserDetails.tsx

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import React, { Fragment, useCallback, useEffect, useState } from "react";
1818
import { connect } from "react-redux";
1919
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
20-
import { Button, Grid } from "@material-ui/core";
20+
import { Button, Grid, IconButton, Menu, MenuItem } from "@material-ui/core";
2121
import PageHeader from "../Common/PageHeader/PageHeader";
2222
import { CreateIcon } from "../../../icons";
2323
import {
@@ -29,7 +29,7 @@ import {
2929
containerForHeader,
3030
searchField,
3131
} from "../Common/FormComponents/common/styleLibrary";
32-
import { IPolicyItem } from "./types";
32+
import { IPolicyItem, User, UsersList } from "./types";
3333
import Tabs from "@material-ui/core/Tabs";
3434
import Tab from "@material-ui/core/Tab";
3535
import { TabPanel } from "../../shared/tabs";
@@ -42,6 +42,10 @@ import SetUserPolicies from "./SetUserPolicies";
4242
import { Bookmark } from "@material-ui/icons";
4343
import history from "../../../history";
4444
import UserServiceAccountsPanel from "./UserServiceAccountsPanel";
45+
import MoreVertIcon from "@material-ui/icons/MoreVert";
46+
import ChangeUserPasswordModal from "../Account/ChangeUserPasswordModal";
47+
import DeleteUserString from "./DeleteUserString";
48+
import { usersSort } from "../../../utils/sortFunctions";
4549

4650
const styles = (theme: Theme) =>
4751
createStyles({
@@ -134,14 +138,29 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
134138
const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
135139
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
136140
const [addLoading, setAddLoading] = useState<boolean>(false);
141+
const [records, setRecords] = useState<User[]>([]);
137142

138143
const [enabled, setEnabled] = useState<boolean>(false);
139144
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
140145
const [currentGroups, setCurrentGroups] = useState<IGroupItem[]>([]);
141146
const [currentPolicies, setCurrentPolicies] = useState<IPolicyItem[]>([]);
147+
const [changeUserPasswordModalOpen, setChangeUserPasswordModalOpen] =
148+
useState<boolean>(false);
149+
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
150+
const [selectedUser, setSelectedUser] = useState<User | null>(null);
142151

143152
const userName = match.params["userName"];
144153

154+
const changeUserPassword = () => {
155+
setAnchorEl(null);
156+
setChangeUserPasswordModalOpen(true);
157+
};
158+
159+
const deleteUser = () => {
160+
setAnchorEl(null);
161+
setDeleteOpen(true);
162+
};
163+
145164
const getUserInformation = useCallback(() => {
146165
if (userName === "") {
147166
return null;
@@ -168,6 +187,7 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
168187
}
169188
setCurrentPolicies(currentPolicies);
170189
setEnabled(res.status === "enabled");
190+
setSelectedUser(res.user);
171191
setLoading(false);
172192
})
173193
.catch((err) => {
@@ -196,12 +216,41 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
196216
});
197217
};
198218

219+
const fetchRecords = useCallback(() => {
220+
setLoading(true);
221+
api
222+
.invoke("GET", `/api/v1/users`)
223+
.then((res: UsersList) => {
224+
const users = res.users === null ? [] : res.users;
225+
226+
setLoading(false);
227+
setRecords(users.sort(usersSort));
228+
})
229+
.catch((err) => {
230+
setLoading(false);
231+
setErrorSnackMessage(err);
232+
});
233+
}, [setLoading, setRecords, setErrorSnackMessage]);
234+
235+
const [anchorEl, setAnchorEl] = React.useState(null);
236+
237+
const handleUserMenu = (event: any) => {
238+
setAnchorEl(event.currentTarget);
239+
};
240+
199241
useEffect(() => {
200242
getUserInformation();
201243
}, [getUserInformation]);
202244

203245
const userLoggedIn = atob(localStorage.getItem("userLoggedIn") || "");
204246

247+
const closeDeleteModalAndRefresh = (refresh: boolean) => {
248+
setDeleteOpen(false);
249+
if (refresh) {
250+
fetchRecords();
251+
}
252+
};
253+
205254
return (
206255
<React.Fragment>
207256
<PageHeader label={`User: ${userName}`} />
@@ -226,6 +275,23 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
226275
}}
227276
/>
228277
)}
278+
{deleteOpen && (
279+
<DeleteUserString
280+
deleteOpen={deleteOpen}
281+
userName={userName}
282+
closeDeleteModalAndRefresh={(refresh: boolean) => {
283+
closeDeleteModalAndRefresh(refresh);
284+
}}
285+
/>
286+
)}
287+
{changeUserPasswordModalOpen && (
288+
<ChangeUserPasswordModal
289+
open={changeUserPasswordModalOpen}
290+
userName={userName}
291+
closeModal={() => setChangeUserPasswordModalOpen(false)}
292+
/>
293+
)}
294+
229295
<Grid container>
230296
<Grid item xs={12} className={classes.container}>
231297
<Grid item xs={12}>
@@ -249,10 +315,37 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
249315
/>
250316
</div>
251317
</div>
318+
<Fragment>
319+
<IconButton
320+
aria-label="more"
321+
aria-controls="long-menu"
322+
aria-haspopup="true"
323+
onClick={handleUserMenu}
324+
>
325+
<MoreVertIcon />
326+
</IconButton>
327+
<Menu
328+
id="long-menu"
329+
anchorEl={anchorEl}
330+
keepMounted
331+
open={Boolean(anchorEl)}
332+
>
333+
<MenuItem
334+
key="changeUserPassword"
335+
onClick={changeUserPassword}
336+
>
337+
Change User Password
338+
</MenuItem>
339+
<MenuItem key="deleteUser" onClick={deleteUser}>
340+
Delete User
341+
</MenuItem>
342+
</Menu>
343+
</Fragment>
252344
</Paper>
253345
</Grid>
254346
</Grid>
255347
</Grid>
348+
<h1>{selectedUser != null && selectedUser.id}</h1>
256349
<Grid item xs={12}>
257350
<br />
258351
</Grid>

0 commit comments

Comments
 (0)