Create Add Group screen (#1890)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
119
portal-ui/src/screens/Console/Groups/AddGroupHelpBox.tsx
Normal file
119
portal-ui/src/screens/Console/Groups/AddGroupHelpBox.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,
|
||||
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. Client’s 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;
|
||||
258
portal-ui/src/screens/Console/Groups/AddGroupScreen.tsx
Normal file
258
portal-ui/src/screens/Console/Groups/AddGroupScreen.tsx
Normal 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));
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user