Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions portal-ui/src/screens/Console/Account/ChangePasswordModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ const ChangePassword = ({
return;
}

if (newPassword.length < 8) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between this component and ChangeUserPasswordModal?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChangePasswordModal is for the current user, which asks for current password and the other one is to change some other user's password

Copy link
Collaborator

@bexsoft bexsoft Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think it would be better to merge these two components into a single one, and just use a flag to show / hide fields, We can do this in a new PR

setModalErrorSnackMessage("Passwords must be at least 8 characters long");
return;
}

if (loading) {
return;
}
Expand Down
14 changes: 10 additions & 4 deletions portal-ui/src/screens/Console/Account/ChangeUserPasswordModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ const styles = (theme: Theme) =>
interface IChangeUserPasswordProps {
classes: any;
open: boolean;
selectedUser: User | null;
userName: string;
closeModal: () => void;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
}

const ChangeUserPassword = ({
classes,
open,
selectedUser,
userName,
closeModal,
setModalErrorSnackMessage,
}: IChangeUserPasswordProps) => {
Expand All @@ -67,9 +67,15 @@ const ChangeUserPassword = ({
return;
}
setLoading(true);

if (newPassword.length < 8) {
setModalErrorSnackMessage("Passwords must be at least 8 characters long");
setLoading(false);
return;
}

let request: ChangeUserPasswordRequest = {
selectedUser: String(selectedUser?.accessKey),
selectedUser: userName,
newSecretKey: newPassword,
};

Expand Down Expand Up @@ -110,7 +116,7 @@ const ChangeUserPassword = ({
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<h3>Change password for {selectedUser?.accessKey}</h3>
<h3>Change password for {userName}</h3>
<Grid item xs={12}>
<InputBoxWrapper
id="new-password"
Expand Down
122 changes: 122 additions & 0 deletions portal-ui/src/screens/Console/Users/DeleteUserString.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import React, { useState } from "react";
import { connect } from "react-redux";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
LinearProgress,
} from "@material-ui/core";
import api from "../../../common/api";
import { User, UsersList } from "./types";
import { setErrorSnackMessage } from "../../../actions";
import history from "../../../history";

interface IDeleteUserProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void;
deleteOpen: boolean;
userName: string;
setErrorSnackMessage: typeof setErrorSnackMessage;
}

const DeleteUserString = ({
closeDeleteModalAndRefresh,
deleteOpen,
userName,
setErrorSnackMessage,
}: IDeleteUserProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);

const removeRecord = () => {
if (deleteLoading) {
return;
}
if (userName == null) {
return;
}
setDeleteLoading(true);
api
.invoke("DELETE", `/api/v1/users/${userName}`, {
id: userName,
})
.then((res: UsersList) => {
setDeleteLoading(false);
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setErrorSnackMessage(err);
});
};

if (userName === null) {
return <div />;
}

return (
<Dialog
open={deleteOpen}
onClose={() => {
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete User</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete user <b>{userName}</b>?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
closeDeleteModalAndRefresh(true);
history.push(`/users/`);
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};

const mapDispatchToProps = {
setErrorSnackMessage,
};

const connector = connect(null, mapDispatchToProps);

export default connector(DeleteUserString);
16 changes: 0 additions & 16 deletions portal-ui/src/screens/Console/Users/ListUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import TableWrapper from "../Common/TableWrapper/TableWrapper";
import SetPolicy from "../Policies/SetPolicy";
import PageHeader from "../Common/PageHeader/PageHeader";
import history from "../../../history";
import ChangeUserPasswordModal from "../Account/ChangeUserPasswordModal";

const styles = (theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -91,8 +90,6 @@ const ListUsers = ({ classes, setErrorSnackMessage }: IUsersProps) => {
const [filter, setFilter] = useState<string>("");
const [checkedUsers, setCheckedUsers] = useState<string[]>([]);
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
const [ChangeUserPasswordModalOpen, setChangeUserPasswordModalOpen] =
useState<boolean>(false);

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

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

const setNewPW = (selectionElement: any): void => {
setChangeUserPasswordModalOpen(true);
setSelectedUser(selectionElement);
};

const tableActions = [
{ type: "view", onClick: viewAction },
{ type: "edit", onClick: setNewPW },
{
type: "delete",
onClick: deleteAction,
Expand Down Expand Up @@ -223,13 +214,6 @@ const ListUsers = ({ classes, setErrorSnackMessage }: IUsersProps) => {
}}
/>
)}
{ChangeUserPasswordModalOpen && (
<ChangeUserPasswordModal
open={ChangeUserPasswordModalOpen}
closeModal={() => setChangeUserPasswordModalOpen(false)}
selectedUser={selectedUser}
/>
)}
<PageHeader label={"Users"} />
<Grid container>
<Grid item xs={12} className={classes.container}>
Expand Down
97 changes: 95 additions & 2 deletions portal-ui/src/screens/Console/Users/UserDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, Grid } from "@material-ui/core";
import { Button, Grid, IconButton, Menu, MenuItem } from "@material-ui/core";
import PageHeader from "../Common/PageHeader/PageHeader";
import { CreateIcon } from "../../../icons";
import {
Expand All @@ -29,7 +29,7 @@ import {
containerForHeader,
searchField,
} from "../Common/FormComponents/common/styleLibrary";
import { IPolicyItem } from "./types";
import { IPolicyItem, User, UsersList } from "./types";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import { TabPanel } from "../../shared/tabs";
Expand All @@ -42,6 +42,10 @@ import SetUserPolicies from "./SetUserPolicies";
import { Bookmark } from "@material-ui/icons";
import history from "../../../history";
import UserServiceAccountsPanel from "./UserServiceAccountsPanel";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import ChangeUserPasswordModal from "../Account/ChangeUserPasswordModal";
import DeleteUserString from "./DeleteUserString";
import { usersSort } from "../../../utils/sortFunctions";

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

const [enabled, setEnabled] = useState<boolean>(false);
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
const [currentGroups, setCurrentGroups] = useState<IGroupItem[]>([]);
const [currentPolicies, setCurrentPolicies] = useState<IPolicyItem[]>([]);
const [changeUserPasswordModalOpen, setChangeUserPasswordModalOpen] =
useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);

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

const changeUserPassword = () => {
setAnchorEl(null);
setChangeUserPasswordModalOpen(true);
};

const deleteUser = () => {
setAnchorEl(null);
setDeleteOpen(true);
};

const getUserInformation = useCallback(() => {
if (userName === "") {
return null;
Expand All @@ -168,6 +187,7 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
}
setCurrentPolicies(currentPolicies);
setEnabled(res.status === "enabled");
setSelectedUser(res.user);
setLoading(false);
})
.catch((err) => {
Expand Down Expand Up @@ -196,12 +216,41 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
});
};

const fetchRecords = useCallback(() => {
setLoading(true);
api
.invoke("GET", `/api/v1/users`)
.then((res: UsersList) => {
const users = res.users === null ? [] : res.users;

setLoading(false);
setRecords(users.sort(usersSort));
})
.catch((err) => {
setLoading(false);
setErrorSnackMessage(err);
});
}, [setLoading, setRecords, setErrorSnackMessage]);

const [anchorEl, setAnchorEl] = React.useState(null);

const handleUserMenu = (event: any) => {
setAnchorEl(event.currentTarget);
};

useEffect(() => {
getUserInformation();
}, [getUserInformation]);

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

const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
fetchRecords();
}
};

return (
<React.Fragment>
<PageHeader label={`User: ${userName}`} />
Expand All @@ -226,6 +275,23 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
}}
/>
)}
{deleteOpen && (
<DeleteUserString
deleteOpen={deleteOpen}
userName={userName}
closeDeleteModalAndRefresh={(refresh: boolean) => {
closeDeleteModalAndRefresh(refresh);
}}
/>
)}
{changeUserPasswordModalOpen && (
<ChangeUserPasswordModal
open={changeUserPasswordModalOpen}
userName={userName}
closeModal={() => setChangeUserPasswordModalOpen(false)}
/>
)}

<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12}>
Expand All @@ -249,10 +315,37 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
/>
</div>
</div>
<Fragment>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleUserMenu}
>
<MoreVertIcon />
</IconButton>
<Menu
id="long-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
>
<MenuItem
key="changeUserPassword"
onClick={changeUserPassword}
>
Change User Password
</MenuItem>
<MenuItem key="deleteUser" onClick={deleteUser}>
Delete User
</MenuItem>
</Menu>
</Fragment>
</Paper>
</Grid>
</Grid>
</Grid>
<h1>{selectedUser != null && selectedUser.id}</h1>
<Grid item xs={12}>
<br />
</Grid>
Expand Down