Create Add Group screen (#1890)

This commit is contained in:
jinapurapu
2022-04-22 20:50:31 -07:00
committed by GitHub
parent c6798a69d9
commit 74d4c4a3e6
5 changed files with 393 additions and 5 deletions

View File

@@ -121,6 +121,7 @@ export const IAM_PAGES = {
USERS: "/identity/users",
USERS_VIEW: "/identity/users/:userName+",
GROUPS: "/identity/groups",
GROUPS_ADD: "/identity/create-group",
GROUPS_VIEW: "/identity/groups/:groupName+",
ACCOUNT: "/identity/account",
ACCOUNT_ADD: "/identity/new-account",
@@ -296,6 +297,10 @@ export const IAM_PAGES_PERMISSIONS = {
IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP, // display "edit members" button in groups detail page
IAM_SCOPES.ADMIN_ATTACH_USER_OR_GROUP_POLICY, // display "set policy" button in groups details page
],
[IAM_PAGES.GROUPS_ADD]: [
IAM_SCOPES.ADMIN_LIST_USERS, // displays users
IAM_SCOPES.ADMIN_CREATE_USER, // displays create user button
],
[IAM_PAGES.USERS]: [
IAM_SCOPES.ADMIN_LIST_USERS, // displays users
IAM_SCOPES.ADMIN_CREATE_USER, // displays create user button

View File

@@ -120,6 +120,9 @@ const ConfigurationOptions = React.lazy(
const AddPool = React.lazy(
() => import("./Tenants/TenantDetails/Pools/AddPool/AddPool")
);
const AddGroupScreen = React.lazy(
() => import("./Groups/AddGroupScreen")
);
const SiteReplication = React.lazy(
() => import("./Configurations/SiteReplication/SiteReplication")
);
@@ -289,6 +292,10 @@ const Console = ({
path: IAM_PAGES.GROUPS,
fsHidden: ldapIsEnabled,
},
{
component: AddGroupScreen,
path: IAM_PAGES.GROUPS_ADD,
},
{
component: GroupsDetails,
path: IAM_PAGES.GROUPS_VIEW,
@@ -394,7 +401,7 @@ const Console = ({
{
component: Account,
path: IAM_PAGES.ACCOUNT,
forceDisplay: true, // user has implicit access to service-accounts
// user has implicit access to service-accounts
},
{
component: AccountCreate,

View 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,
GroupsIcon,
IAMPoliciesIcon,
} 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 AddGroupHelpBox = ({ 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 Groups</div>
</Box>
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
Adding groups lets you assign IAM policies to multiple users at once.
<Box sx={{ paddingTop: "20px", paddingBottom: "10px" }}>
Users inherit access permissions to data and resources through the groups they belong to.
</Box>
<Box sx={{ paddingTop: "10px", paddingBottom: "10px" }}>
A user can be a member of multiple groups.
</Box>
<Box sx={{ paddingTop: "10px", paddingBottom: "10px" }}>
Groups provide a simplified method for managing shared permissions among users with common access patterns and workloads. Clients cannot authenticate to a MinIO deployment using a group as an identity.
</Box>
</Box>
<Box
sx={{
display: "flex",
flexFlow: "column",
}}
>
<FeatureItem icon={<GroupsIcon />} description={`Add Users to Group`} />
<FeatureItem icon={<IAMPoliciesIcon />} description={`Assign Custom IAM Policies for Group`} />
</Box>
</Box>
);
};
export default AddGroupHelpBox;

View File

@@ -0,0 +1,258 @@
// 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, useEffect } 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, Box, LinearProgress } from "@mui/material";
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 AddGroupHelpBox from "./AddGroupHelpBox";
import UsersSelectors from "./UsersSelectors";
import BackLink from "../../../common/BackLink";
import { connect } from "react-redux";
import { CreateGroupIcon } from "../../../icons";
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 IAddGroupProps {
classes: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
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: 20,
paddingLeft: 20,
paddingBottom: 40,
paddingTop: 10,
textAlign: "end",
},
headIcon: {
fontWeight: "bold",
size: "50",
},
...formFieldStyles,
...modalStyleUtils,
});
const AddGroupScreen = ({
classes,
setErrorSnackMessage,
}: IAddGroupProps) => {
const [groupName, setGroupName] = useState<string>("");
const [saving, isSaving] = useState<boolean>(false);
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
const [validGroup, setValidGroup] = useState<boolean>(false);
useEffect(() => {
setValidGroup(groupName.trim() !== "");
}, [groupName, selectedUsers]);
useEffect(() => {
if (saving) {
const saveRecord = () => {
api
.invoke("POST", "/api/v1/groups", {
group: groupName,
members: selectedUsers,
})
.then((res) => {
isSaving(false);
history.push(`${IAM_PAGES.GROUPS}`);
})
.catch((err: ErrorResponseHandler) => {
isSaving(false);
setErrorSnackMessage(err);
});
}
saveRecord();
}
}, [
saving,
groupName,
selectedUsers,
setErrorSnackMessage,
]);
//Fetch Actions
const setSaving = (event: React.FormEvent) => {
event.preventDefault();
isSaving(true);
};
const resetForm = () => {
setGroupName("");
setSelectedUsers([]);
};
return (
<Fragment>
<Grid item xs={12}>
<PageHeader
label={<BackLink to={IAM_PAGES.GROUPS} label={"Groups"} />}
/>
<PageLayout>
<Grid
item
xs={12}
container
className={classes.title}
align-items="stretch"
>
<Grid item className={classes.headIcon}>
<CreateGroupIcon />
</Grid>
<Grid item className={classes.headTitle}>
Create Group
</Grid>
</Grid>
<Grid container align-items="center">
<Grid item xs={8}>
<Box>
<form noValidate autoComplete="off" onSubmit={setSaving}>
<Grid container item spacing = "20">
<Grid item xs={12} >
<Grid container>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="group-name"
name="group-name"
label="Group Name"
autoFocus={true}
value={groupName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setGroupName(e.target.value);
}}
/>
</Grid>
<Grid item xs={12} className={classes.userSelector}>
<UsersSelectors
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
editMode={true}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.modalButtonBar}>
<Button
type="button"
variant="outlined"
color="primary"
className={classes.spacerRight}
onClick={resetForm}
>
Clear
</Button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving || !validGroup}
>
Save
</Button>
</Grid>
</Grid>
{saving && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</Box>
</Grid>
<Grid item xs={4}>
<Box>
<AddGroupHelpBox />
</Box>
</Grid>
</Grid>
</PageLayout>
</Grid>
</Fragment>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default withStyles(styles)(connector(AddGroupScreen));

View File

@@ -36,7 +36,6 @@ import api from "../../../common/api";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import PageHeader from "../Common/PageHeader/PageHeader";
import HelpBox from "../../../common/HelpBox";
import history from "../../../history";
import AButton from "../Common/AButton/AButton";
import PageLayout from "../Common/Layout/PageLayout";
import SearchBox from "../Common/SearchBox";
@@ -63,6 +62,7 @@ interface IGroupsProps {
classes: any;
openGroupModal: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
history: any;
}
const styles = (theme: Theme) =>
@@ -79,7 +79,7 @@ const styles = (theme: Theme) =>
...containerForHeader(theme.spacing(4)),
});
const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
const [addGroupOpen, setGroupOpen] = useState<boolean>(false);
const [selectedGroup, setSelectedGroup] = useState<any>(null);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
@@ -232,8 +232,7 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
color="primary"
icon={<AddIcon />}
onClick={() => {
setSelectedGroup(null);
setGroupOpen(true);
history.push(`${IAM_PAGES.GROUPS_ADD}`);
}}
/>
</SecureComponent>