Moved AddUser from modal to screen (#1869)
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
@@ -120,6 +120,7 @@ export const IAM_PAGES = {
|
||||
IDENTITY: "/identity",
|
||||
USERS: "/identity/users",
|
||||
USERS_VIEW: "/identity/users/:userName+",
|
||||
USER_ADD: "/identity/add-user",
|
||||
GROUPS: "/identity/groups",
|
||||
GROUPS_ADD: "/identity/create-group",
|
||||
GROUPS_VIEW: "/identity/groups/:groupName+",
|
||||
@@ -313,6 +314,8 @@ export const IAM_PAGES_PERMISSIONS = {
|
||||
IAM_SCOPES.ADMIN_DISABLE_USER,
|
||||
IAM_SCOPES.ADMIN_DELETE_USER,
|
||||
],
|
||||
[IAM_PAGES.USER_ADD]: [
|
||||
IAM_SCOPES.ADMIN_CREATE_USER,], // displays create user button
|
||||
[IAM_PAGES.ACCOUNT_ADD]: [
|
||||
IAM_SCOPES.ADMIN_CREATE_SERVICEACCOUNT,
|
||||
],
|
||||
|
||||
@@ -281,6 +281,10 @@ const Console = ({
|
||||
component: Users,
|
||||
path: IAM_PAGES.USERS_VIEW,
|
||||
},
|
||||
{
|
||||
component: Users,
|
||||
path: IAM_PAGES.USER_ADD,
|
||||
},
|
||||
{
|
||||
component: Users,
|
||||
path: IAM_PAGES.USERS,
|
||||
|
||||
@@ -145,7 +145,7 @@ const PolicySelectors = ({
|
||||
<span className={classes.fieldLabel}>Assign Policies</span>
|
||||
<div className={classes.searchBox}>
|
||||
<SearchBox
|
||||
placeholder="Filter Policy"
|
||||
placeholder="Start typing to search for a Policy"
|
||||
onChange={(value) => {
|
||||
setFilter(value);
|
||||
}}
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
// 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, { useCallback, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Button, LinearProgress, Tab, Tabs } from "@mui/material";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import {
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { User } from "./types";
|
||||
import { setModalErrorSnackMessage } from "../../../actions";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import api from "../../../common/api";
|
||||
import GroupsSelectors from "./GroupsSelectors";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import PredefinedList from "../Common/FormComponents/PredefinedList/PredefinedList";
|
||||
import PolicySelectors from "../Policies/PolicySelectors";
|
||||
import { TabPanel } from "../../shared/tabs";
|
||||
import { CreateUserIcon } from "../../../icons";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
tabsHeader: {
|
||||
marginBottom: "1rem",
|
||||
},
|
||||
...modalStyleUtils,
|
||||
...formFieldStyles,
|
||||
...spacingUtils,
|
||||
});
|
||||
|
||||
interface IAddUserContentProps {
|
||||
classes: any;
|
||||
closeModalAndRefresh: () => void;
|
||||
selectedUser: User | null;
|
||||
open: boolean;
|
||||
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
|
||||
}
|
||||
|
||||
const AddUser = ({
|
||||
classes,
|
||||
closeModalAndRefresh,
|
||||
selectedUser,
|
||||
open,
|
||||
setModalErrorSnackMessage,
|
||||
}: IAddUserContentProps) => {
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
|
||||
const [selectedPolicies, setSelectedPolicies] = useState<string[]>([]);
|
||||
const [currentGroups, setCurrentGroups] = useState<string[]>([]);
|
||||
const [currenTab, setCurrenTab] = useState<number>(0);
|
||||
|
||||
const getUserInformation = useCallback(() => {
|
||||
if (!selectedUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/user?name=${encodeURI(selectedUser.accessKey)}`)
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
setAccessKey(res.accessKey);
|
||||
setSelectedGroups(res.memberOf || []);
|
||||
setCurrentGroups(res.memberOf || []);
|
||||
setEnabled(res.status === "enabled");
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}, [selectedUser, setModalErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedUser === null) {
|
||||
setAccessKey("");
|
||||
setSecretKey("");
|
||||
setSelectedGroups([]);
|
||||
} else {
|
||||
getUserInformation();
|
||||
}
|
||||
}, [selectedUser, getUserInformation]);
|
||||
|
||||
const saveRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (secretKey.length < 8) {
|
||||
setModalErrorSnackMessage({
|
||||
errorMessage: "Passwords must be at least 8 characters long",
|
||||
detailedError: "",
|
||||
});
|
||||
setAddLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
setAddLoading(true);
|
||||
if (selectedUser !== null) {
|
||||
api
|
||||
.invoke(
|
||||
"PUT",
|
||||
`/api/v1/user?name=${encodeURI(selectedUser.accessKey)}`,
|
||||
{
|
||||
status: enabled ? "enabled" : "disabled",
|
||||
groups: selectedGroups,
|
||||
policies: selectedPolicies,
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
} else {
|
||||
api
|
||||
.invoke("POST", "/api/v1/users", {
|
||||
accessKey,
|
||||
secretKey,
|
||||
groups: selectedGroups,
|
||||
policies: selectedPolicies,
|
||||
})
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
if (selectedUser !== null) {
|
||||
setSelectedGroups([]);
|
||||
return;
|
||||
}
|
||||
setAccessKey("");
|
||||
setSecretKey("");
|
||||
setSelectedGroups([]);
|
||||
};
|
||||
|
||||
const sendEnabled =
|
||||
accessKey.trim() !== "" &&
|
||||
((secretKey.trim() !== "" && selectedUser === null) ||
|
||||
selectedUser !== null);
|
||||
return (
|
||||
<ModalWrapper
|
||||
onClose={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
modalOpen={open}
|
||||
title={selectedUser !== null ? "Edit User" : "Create User"}
|
||||
titleIcon={<CreateUserIcon />}
|
||||
>
|
||||
{selectedUser !== null && (
|
||||
<div className={classes.floatingEnabled}>
|
||||
<FormSwitchWrapper
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={enabled}
|
||||
value={"user_enabled"}
|
||||
id="user-status"
|
||||
name="user-status"
|
||||
onChange={(e) => {
|
||||
setEnabled(e.target.checked);
|
||||
}}
|
||||
switchOnly
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<React.Fragment>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
id="accesskey-input"
|
||||
name="accesskey-input"
|
||||
label="Access Key"
|
||||
value={accessKey}
|
||||
autoFocus={true}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(e.target.value);
|
||||
}}
|
||||
disabled={selectedUser !== null}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{selectedUser !== null ? (
|
||||
<PredefinedList
|
||||
label={"Current Groups"}
|
||||
content={currentGroups.join(", ")}
|
||||
/>
|
||||
) : (
|
||||
<div className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
id="standard-multiline-static"
|
||||
name="standard-multiline-static"
|
||||
label="Secret Key"
|
||||
type="password"
|
||||
value={secretKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(e.target.value);
|
||||
}}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Grid item xs={12} className={classes.tabsHeader}>
|
||||
<Tabs
|
||||
value={currenTab}
|
||||
onChange={(e, nv) => {
|
||||
setCurrenTab(nv);
|
||||
}}
|
||||
>
|
||||
<Tab label="Policies" />
|
||||
<Tab label="Groups" />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<TabPanel value={currenTab} index={0}>
|
||||
<Grid item xs={12}>
|
||||
<PolicySelectors
|
||||
selectedPolicy={selectedPolicies}
|
||||
setSelectedPolicy={setSelectedPolicies}
|
||||
/>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
<TabPanel value={currenTab} index={1}>
|
||||
<Grid item xs={12}>
|
||||
<GroupsSelectors
|
||||
selectedGroups={selectedGroups}
|
||||
setSelectedGroups={(elements: string[]) => {
|
||||
setSelectedGroups(elements);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !sendEnabled}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</React.Fragment>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setModalErrorSnackMessage,
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(AddUser));
|
||||
119
portal-ui/src/screens/Console/Users/AddUserHelpBox.tsx
Normal file
119
portal-ui/src/screens/Console/Users/AddUserHelpBox.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import {
|
||||
HelpIconFilled,
|
||||
UsersIcon,
|
||||
ChangeAccessPolicyIcon,
|
||||
GroupsIcon,
|
||||
} from "../../../icons";
|
||||
|
||||
const FeatureItem = ({
|
||||
icon,
|
||||
description,
|
||||
}: {
|
||||
icon: any;
|
||||
description: string;
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
"& .min-icon": {
|
||||
marginRight: "10px",
|
||||
height: "23px",
|
||||
width: "23px",
|
||||
marginBottom: "10px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{icon}{" "}
|
||||
<div style={{ fontSize: "14px", fontStyle: "italic", color: "#5E5E5E" }}>
|
||||
{description}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const AddUserHelpBox = ({ hasMargin = true }: { hasMargin?: boolean }) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
border: "1px solid #eaeaea",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
padding: "20px",
|
||||
marginLeft: {
|
||||
xs: "0px",
|
||||
sm: "0px",
|
||||
md: hasMargin ? "30px" : "",
|
||||
},
|
||||
marginTop: {
|
||||
xs: "0px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 600,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: "16px",
|
||||
|
||||
"& .min-icon": {
|
||||
height: "21px",
|
||||
width: "21px",
|
||||
marginRight: "15px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<HelpIconFilled />
|
||||
<div>Learn more about the Users feature</div>
|
||||
</Box>
|
||||
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must authenticate their
|
||||
identity by specifying both a valid access key (username) and the
|
||||
corresponding secret key (password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that explicitly list
|
||||
the actions and resources to which that user has access. Users can also
|
||||
inherit policies from the groups in which they have membership.
|
||||
<br />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
}}
|
||||
>
|
||||
<FeatureItem icon={<UsersIcon />} description={`Create Users`} />
|
||||
<FeatureItem icon={<GroupsIcon />} description={`Manage Groups`} />
|
||||
<FeatureItem
|
||||
icon={<ChangeAccessPolicyIcon />}
|
||||
description={`Assign Policies`}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUserHelpBox;
|
||||
304
portal-ui/src/screens/Console/Users/AddUserScreen.tsx
Normal file
304
portal-ui/src/screens/Console/Users/AddUserScreen.tsx
Normal file
@@ -0,0 +1,304 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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, { Fragment, useState } from "react";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import {
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Button, LinearProgress, Box } from "@mui/material";
|
||||
import { CreateUserIcon } from "../../../icons";
|
||||
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import history from "../../../../src/history";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
import AddUserHelpBox from "./AddUserHelpBox";
|
||||
import PolicySelectors from "../Policies/PolicySelectors";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
import GroupsSelectors from "./GroupsSelectors";
|
||||
import { connect } from "react-redux";
|
||||
import { User } from "./types";
|
||||
|
||||
import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import { ErrorResponseHandler } from "../../../../src/common/types";
|
||||
import api from "../../../../src/common/api";
|
||||
|
||||
import { setErrorSnackMessage } from "../../../../src/actions";
|
||||
|
||||
interface IAddUserProps {
|
||||
classes: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
selectedUser: User | null;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
bottomContainer: {
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
margin: "auto",
|
||||
justifyContent: "center",
|
||||
"& div": {
|
||||
width: 150,
|
||||
"@media (max-width: 900px)": {
|
||||
flexFlow: "column",
|
||||
},
|
||||
},
|
||||
},
|
||||
factorElements: {
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
marginLeft: 30,
|
||||
},
|
||||
sizeNumber: {
|
||||
fontSize: 35,
|
||||
fontWeight: 700,
|
||||
textAlign: "center",
|
||||
},
|
||||
sizeDescription: {
|
||||
fontSize: 14,
|
||||
color: "#777",
|
||||
textAlign: "center",
|
||||
},
|
||||
pageBox: {
|
||||
border: "1px solid #EAEAEA",
|
||||
borderTop: 0,
|
||||
},
|
||||
addPoolTitle: {
|
||||
border: "1px solid #EAEAEA",
|
||||
borderBottom: 0,
|
||||
},
|
||||
headTitle: {
|
||||
fontWeight: "bold",
|
||||
fontSize: 16,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
...formFieldStyles,
|
||||
...modalStyleUtils,
|
||||
});
|
||||
|
||||
const AddUser = ({
|
||||
classes,
|
||||
setErrorSnackMessage,
|
||||
}: IAddUserProps) => {
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
|
||||
const [selectedPolicies, setSelectedPolicies] = useState<string[]>([]);
|
||||
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||
|
||||
const sendEnabled = accessKey.trim() !== "";
|
||||
|
||||
const saveRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (secretKey.length < 8) {
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "Passwords must be at least 8 characters long",
|
||||
detailedError: "",
|
||||
});
|
||||
setAddLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
setAddLoading(true);
|
||||
api
|
||||
.invoke("POST", "/api/v1/users", {
|
||||
accessKey,
|
||||
secretKey,
|
||||
groups: selectedGroups,
|
||||
policies: selectedPolicies,
|
||||
})
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
history.push(`${IAM_PAGES.USERS}`);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setSelectedGroups([]);
|
||||
setAccessKey("");
|
||||
setSecretKey("");
|
||||
setSelectedPolicies([]);
|
||||
setShowPassword(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<PageHeader label={<BackLink to={IAM_PAGES.USERS} label={"Users"} />} />
|
||||
<PageLayout>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
container
|
||||
className={classes.title}
|
||||
align-items="baseline"
|
||||
>
|
||||
<Grid item xs={"auto"}>
|
||||
<CreateUserIcon />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={"auto"}
|
||||
align-self="end"
|
||||
className={classes.headTitle}
|
||||
>
|
||||
Create User
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container align-items="center">
|
||||
<Grid item xs={8}>
|
||||
<Box>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
className={classes.spacerBottom}
|
||||
classes={{
|
||||
inputLabel: classes.sizedLabel,
|
||||
}}
|
||||
id="accesskey-input"
|
||||
name="accesskey-input"
|
||||
label="User Name"
|
||||
value={accessKey}
|
||||
autoFocus={true}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setAccessKey(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
className={classes.spacerBottom}
|
||||
classes={{
|
||||
inputLabel: classes.sizedLabel,
|
||||
}}
|
||||
id="standard-multiline-static"
|
||||
name="standard-multiline-static"
|
||||
label="Password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={secretKey}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setSecretKey(e.target.value);
|
||||
}}
|
||||
autoComplete="current-password"
|
||||
overlayIcon={
|
||||
showPassword ? (
|
||||
<VisibilityOffIcon />
|
||||
) : (
|
||||
<RemoveRedEyeIcon />
|
||||
)
|
||||
}
|
||||
overlayAction={() => setShowPassword(!showPassword)}
|
||||
/>
|
||||
</div>
|
||||
<Grid container item spacing="20">
|
||||
<Grid item xs={12}>
|
||||
<PolicySelectors
|
||||
selectedPolicy={selectedPolicies}
|
||||
setSelectedPolicy={setSelectedPolicies}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<GroupsSelectors
|
||||
selectedGroups={selectedGroups}
|
||||
setSelectedGroups={(elements: string[]) => {
|
||||
setSelectedGroups(elements);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !sendEnabled}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Box>
|
||||
<AddUserHelpBox />
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage,
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(AddUser));
|
||||
@@ -145,7 +145,7 @@ const GroupsSelectors = ({
|
||||
|
||||
<div className={classes.searchBox}>
|
||||
<SearchBox
|
||||
placeholder="Filter Groups"
|
||||
placeholder="Start typing to search for Groups"
|
||||
adornmentPosition="end"
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
|
||||
@@ -54,7 +54,6 @@ import {
|
||||
SecureComponent,
|
||||
} from "../../../common/SecureComponent";
|
||||
|
||||
const AddUser = withSuspense(React.lazy(() => import("./AddUser")));
|
||||
const SetPolicy = withSuspense(
|
||||
React.lazy(() => import("../Policies/SetPolicy"))
|
||||
);
|
||||
@@ -83,8 +82,6 @@ interface IUsersProps {
|
||||
const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
const [records, setRecords] = useState<User[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
|
||||
@@ -108,12 +105,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP,
|
||||
]);
|
||||
|
||||
const closeAddModalAndRefresh = () => {
|
||||
setAddScreenOpen(false);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
if (refresh) {
|
||||
setLoading(true);
|
||||
@@ -201,15 +193,6 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddUser
|
||||
open={addScreenOpen}
|
||||
selectedUser={selectedUser}
|
||||
closeModalAndRefresh={() => {
|
||||
closeAddModalAndRefresh();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{policyOpen && (
|
||||
<SetPolicy
|
||||
open={policyOpen}
|
||||
@@ -283,8 +266,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
icon={<AddIcon />}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setSelectedUser(null);
|
||||
history.push(`${IAM_PAGES.USER_ADD}`);
|
||||
}}
|
||||
variant={"contained"}
|
||||
/>
|
||||
@@ -389,8 +371,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
To get started,{" "}
|
||||
<AButton
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setSelectedUser(null);
|
||||
history.push(`${IAM_PAGES.USER_ADD}`);
|
||||
}}
|
||||
>
|
||||
Create a User
|
||||
|
||||
@@ -25,6 +25,7 @@ import NotFoundPage from "../../NotFoundPage";
|
||||
import ListUsers from "./ListUsers";
|
||||
import UserDetails from "./UserDetails";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import AddUserScreen from "./AddUserScreen";
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
open: state.system.sidebarOpen,
|
||||
@@ -38,6 +39,7 @@ const Users = () => {
|
||||
<Switch>
|
||||
<Route path={IAM_PAGES.USERS_VIEW} component={UserDetails} />
|
||||
<Route path={IAM_PAGES.USERS} component={ListUsers} />
|
||||
<Route path={IAM_PAGES.USER_ADD} component={AddUserScreen} />
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Router>
|
||||
|
||||
Reference in New Issue
Block a user