Group bucket users and policies under access tab. Rename ViewBuckets to BucketDetails (#766)

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2021-05-24 11:11:39 -07:00
committed by GitHub
parent d8350625f3
commit 003eaaea76
15 changed files with 201 additions and 122 deletions

View File

@@ -0,0 +1,161 @@
// 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, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { AppState } from "../../../../store";
import { setErrorSnackMessage } from "../../../../actions";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import { Paper } from "@material-ui/core";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import { TabPanel } from "../../../shared/tabs";
import { Policy } from "../../Policies/types";
import { ISessionResponse } from "../../types";
import { User } from "../../Users/types";
import api from "../../../../common/api";
import history from "../../../../history";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
const styles = (theme: Theme) => createStyles({});
const mapState = (state: AppState) => ({
session: state.console.session,
});
const connector = connect(mapState, { setErrorSnackMessage });
function a11yProps(index: any) {
return {
id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`,
};
}
interface IAccessDetailsProps {
bucketName: string;
session: ISessionResponse;
setErrorSnackMessage: typeof setErrorSnackMessage;
classes: any;
}
const AccessDetails = ({
bucketName,
classes,
setErrorSnackMessage,
session,
}: IAccessDetailsProps) => {
const [curTab, setCurTab] = useState<number>(0);
const [loadingPolicies, setLoadingPolicies] = useState<boolean>(true);
const [bucketPolicy, setBucketPolicy] = useState<Policy[]>([]);
const [loadingUsers, setLoadingUsers] = useState<boolean>(true);
const [bucketUsers, setBucketUsers] = useState<User[]>([]);
const usersEnabled = session.pages?.indexOf("/users") > -1;
const PolicyActions = [
{
type: "view",
onClick: (policy: any) => {
history.push(`/policies/${policy.name}`);
},
},
];
const userTableActions = [
{
type: "view",
onClick: (user: any) => {
history.push(`/users/${user}`);
},
},
];
useEffect(() => {
if (loadingUsers && usersEnabled) {
api
.invoke("GET", `/api/v1/bucket-users/${bucketName}`)
.then((res: any) => {
setBucketUsers(res);
setLoadingUsers(false);
})
.catch((err: any) => {
setErrorSnackMessage(err);
setLoadingUsers(false);
});
}
}, [loadingUsers, setErrorSnackMessage, bucketName, usersEnabled]);
useEffect(() => {
if (loadingPolicies) {
api
.invoke("GET", `/api/v1/bucket-policy/${bucketName}`)
.then((res: any) => {
setBucketPolicy(res.policies);
setLoadingPolicies(false);
})
.catch((err: any) => {
setErrorSnackMessage(err);
setLoadingPolicies(false);
});
}
}, [loadingPolicies, setErrorSnackMessage, bucketName]);
return (
<Paper>
<Tabs
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
setCurTab(newValue);
}}
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Policies" {...a11yProps(0)} />
{usersEnabled && <Tab label="Users" {...a11yProps(1)} />}
</Tabs>
<TabPanel index={0} value={curTab}>
<TableWrapper
noBackground={true}
itemActions={PolicyActions}
columns={[{ label: "Name", elementKey: "name" }]}
isLoading={loadingPolicies}
records={bucketPolicy}
entityName="Policies"
idField="name"
/>
</TabPanel>
{usersEnabled && (
<TabPanel index={1} value={curTab}>
<TableWrapper
noBackground={true}
itemActions={userTableActions}
columns={[{ label: "User", elementKey: "accessKey" }]}
isLoading={loadingUsers}
records={bucketUsers}
entityName="Users"
idField="accessKey"
/>
</TabPanel>
)}
</Paper>
);
};
export default withStyles(styles)(connector(AccessDetails));

View File

@@ -52,8 +52,6 @@ import {
searchField,
} from "../../Common/FormComponents/common/styleLibrary";
import { setErrorSnackMessage } from "../../../../actions";
import { Policy } from "../../Policies/types";
import { User } from "../../Users/types";
import { AppState } from "../../../../store";
import { ISessionResponse } from "../../types";
import SetAccessPolicy from "./SetAccessPolicy";
@@ -66,13 +64,12 @@ import PageHeader from "../../Common/PageHeader/PageHeader";
import EnableBucketEncryption from "./EnableBucketEncryption";
import EnableVersioningModal from "./EnableVersioningModal";
import UsageIcon from "../../../../icons/UsageIcon";
import AddPolicy from "../../Policies/AddPolicy";
import DeleteReplicationRule from "../ViewBucket/DeleteReplicationRule";
import DeleteReplicationRule from ".//DeleteReplicationRule";
import EditLifecycleConfiguration from "./EditLifecycleConfiguration";
import AddLifecycleModal from "./AddLifecycleModal";
import history from "../../../../history";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import AccessDetails from "./AccessDetails";
const styles = (theme: Theme) =>
createStyles({
@@ -178,6 +175,9 @@ const styles = (theme: Theme) =>
reportedUsage: {
padding: "15px",
},
titleCol: {
fontWeight: "bold",
},
...searchField,
...actionsTray,
actionsTray: {
@@ -189,7 +189,7 @@ const styles = (theme: Theme) =>
...containerForHeader(theme.spacing(4)),
});
interface IViewBucketProps {
interface IBucketDetailsProps {
classes: any;
match: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
@@ -226,21 +226,18 @@ function a11yProps(index: any) {
};
}
const ViewBucket = ({
const BucketDetails = ({
classes,
match,
setErrorSnackMessage,
session,
}: IViewBucketProps) => {
}: IBucketDetailsProps) => {
const [info, setInfo] = useState<BucketInfo | null>(null);
const [records, setRecords] = useState<BucketEvent[]>([]);
const [replicationRules, setReplicationRules] = useState<
BucketReplicationRule[]
>([]);
const [bucketPolicy, setBucketPolicy] = useState<Policy[]>([]);
const [loadingPolicy, setLoadingPolicy] = useState<boolean>(true);
const [bucketUsers, setBucketUsers] = useState<User[]>([]);
const [loadingUsers, setLoadingUsers] = useState<boolean>(true);
const [loadingBucket, setLoadingBucket] = useState<boolean>(true);
const [loadingEvents, setLoadingEvents] = useState<boolean>(true);
const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
@@ -252,7 +249,6 @@ const ViewBucket = ({
useState<boolean>(false);
const [curTab, setCurTab] = useState<number>(0);
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
const [policyScreenOpen, setPolicyScreenOpen] = useState<boolean>(false);
const [enableEncryptionScreenOpen, setEnableEncryptionScreenOpen] =
useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
@@ -267,7 +263,6 @@ const ViewBucket = ({
useState<BucketEncryptionInfo | null>(null);
const [retentionConfigOpen, setRetentionConfigOpen] =
useState<boolean>(false);
const [policyEdit, setPolicyEdit] = useState<any>(null);
const [enableVersioningOpen, setEnableVersioningOpen] =
useState<boolean>(false);
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
@@ -281,7 +276,6 @@ const ViewBucket = ({
const [addLifecycleOpen, setAddLifecycleOpen] = useState<boolean>(false);
const bucketName = match.params["bucketName"];
const usersEnabled = session.pages?.indexOf("/users") > -1;
// check the permissions for creating bucket
useEffect(() => {
@@ -403,36 +397,6 @@ const ViewBucket = ({
}
}, [loadingReplication, setErrorSnackMessage, bucketName]);
useEffect(() => {
if (loadingPolicy) {
api
.invoke("GET", `/api/v1/bucket-policy/${bucketName}`)
.then((res: any) => {
setBucketPolicy(res.policies);
setLoadingPolicy(false);
})
.catch((err: any) => {
setErrorSnackMessage(err);
setLoadingPolicy(false);
});
}
}, [loadingPolicy, setErrorSnackMessage, bucketName]);
useEffect(() => {
if (loadingUsers && usersEnabled) {
api
.invoke("GET", `/api/v1/bucket-users/${bucketName}`)
.then((res: any) => {
setBucketUsers(res);
setLoadingUsers(false);
})
.catch((err: any) => {
setErrorSnackMessage(err);
setLoadingUsers(false);
});
}
}, [loadingUsers, setErrorSnackMessage, bucketName, usersEnabled]);
useEffect(() => {
if (loadingSize) {
api
@@ -522,18 +486,6 @@ const ViewBucket = ({
setLoadingEncryption(true);
};
const closeAddModalAndRefresh = (refresh: boolean) => {
setPolicyScreenOpen(false);
if (refresh) {
fetchPolicies();
}
};
const fetchPolicies = () => {
setLoadingPolicy(true);
};
const closeAddEventAndRefresh = () => {
setAddScreenOpen(false);
loadAllBucketData();
@@ -628,12 +580,7 @@ const ViewBucket = ({
};
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
const viewAction = (row: any) => {
setPolicyScreenOpen(true);
setPolicyEdit(row);
};
const PolicyActions = [{ type: "view", onClick: viewAction }];
const replicationTableActions = [
{
type: "delete",
@@ -700,12 +647,6 @@ const ViewBucket = ({
},
];
const userViewAction = (user: any) => {
history.push(`/users/${user}`);
};
const userTableActions = [{ type: "view", onClick: userViewAction }];
return (
<Fragment>
{addScreenOpen && (
@@ -732,13 +673,6 @@ const ViewBucket = ({
closeModalAndRefresh={closeSetAccessPolicy}
/>
)}
{policyScreenOpen && (
<AddPolicy
open={policyScreenOpen}
closeModalAndRefresh={closeAddModalAndRefresh}
policyEdit={policyEdit}
/>
)}
{retentionConfigOpen && (
<SetRetentionConfig
bucketName={bucketName}
@@ -813,8 +747,7 @@ const ViewBucket = ({
<Tab label="Events" {...a11yProps(1)} />
{canGetReplication && <Tab label="Replication" {...a11yProps(2)} />}
<Tab label="Lifecycle" {...a11yProps(3)} />
<Tab label="Policies" {...a11yProps(4)} />
{usersEnabled && <Tab label="Users" {...a11yProps(5)} />}
<Tab label="Access" {...a11yProps(4)} />
</Tabs>
</Grid>
<Grid item xs={12}>
@@ -827,7 +760,7 @@ const ViewBucket = ({
<hr className={classes.hrClass} />
<table width={"100%"}>
<tr>
<td>Access Policy:</td>
<td className={classes.titleCol}>Access Policy:</td>
<td className={classes.capitalizeFirst}>
<Button
color="primary"
@@ -847,7 +780,7 @@ const ViewBucket = ({
)}
</Button>
</td>
<td>Encryption:</td>
<td className={classes.titleCol}>Encryption:</td>
<td>
{loadingEncryption ? (
<CircularProgress
@@ -869,7 +802,7 @@ const ViewBucket = ({
</td>
</tr>
<tr>
<td>Replication:</td>
<td className={classes.titleCol}>Replication:</td>
<td className={classes.doubleElement}>
<span>
{replicationRules.length ? "Enabled" : "Disabled"}
@@ -877,7 +810,7 @@ const ViewBucket = ({
</td>
{!hasObjectLocking ? (
<React.Fragment>
<td>Object Locking:</td>
<td className={classes.titleCol}>Object Locking:</td>
<td>Disabled</td>
</React.Fragment>
) : (
@@ -915,7 +848,7 @@ const ViewBucket = ({
<hr className={classes.hrClass} />
<table>
<tr>
<td>Versioning:</td>
<td className={classes.titleCol}>Versioning:</td>
<td>
{loadingVersioning ? (
<CircularProgress
@@ -951,7 +884,7 @@ const ViewBucket = ({
<table>
<tr>
<td className={classes.gridContainer}>
<td>Retention:</td>
<td className={classes.titleCol}>Retention:</td>
<td>
{loadingVersioning ? (
<CircularProgress
@@ -1155,28 +1088,8 @@ const ViewBucket = ({
</TabPanel>
<TabPanel index={4} value={curTab}>
<br />
<TableWrapper
itemActions={PolicyActions}
columns={[{ label: "Name", elementKey: "name" }]}
isLoading={loadingEvents}
records={bucketPolicy}
entityName="Policies"
idField="name"
/>
<AccessDetails bucketName={bucketName} />
</TabPanel>
{usersEnabled && (
<TabPanel index={5} value={curTab}>
<br />
<TableWrapper
itemActions={userTableActions}
columns={[{ label: "User", elementKey: "accessKey" }]}
isLoading={loadingUsers}
records={bucketUsers}
entityName="Users"
idField="accessKey"
/>
</TabPanel>
)}
</Grid>
</Grid>
</Fragment>
@@ -1191,4 +1104,4 @@ const connector = connect(mapState, {
setErrorSnackMessage,
});
export default withStyles(styles)(connector(ViewBucket));
export default withStyles(styles)(connector(BucketDetails));

View File

@@ -22,7 +22,7 @@ import { AppState } from "../../../store";
import { setMenuOpen } from "../../../actions";
import NotFoundPage from "../../NotFoundPage";
import ListBuckets from "./ListBuckets/ListBuckets";
import ViewBucket from "./ViewBucket/ViewBucket";
import BucketDetails from "./BucketDetails/BucketDetails";
const mapState = (state: AppState) => ({
open: state.system.sidebarOpen,
@@ -34,7 +34,7 @@ const Buckets = () => {
return (
<Router history={history}>
<Switch>
<Route path="/buckets/:bucketName" component={ViewBucket} />
<Route path="/buckets/:bucketName" component={BucketDetails} />
<Route path="/" component={ListBuckets} />
<Route component={NotFoundPage} />
</Switch>

View File

@@ -323,7 +323,7 @@ const PolicyDetails = ({
columns={[{ label: "Name", elementKey: "name" }]}
isLoading={false}
records={userList}
entityName="Servers"
entityName="Users"
idField="name"
/>
)}

View File

@@ -126,6 +126,9 @@ const styles = (theme: Theme) =>
poolLabel: {
color: "#666666",
},
titleCol: {
fontWeight: "bold",
},
breadcrumLink: {
textDecoration: "none",
color: "black",
@@ -445,9 +448,9 @@ const TenantDetails = ({
</td>
</tr>
<tr>
<td>Capacity:</td>
<td className={classes.titleCol}>Capacity:</td>
<td>{niceBytes(capacity.toString(10))}</td>
<td>MinIO:</td>
<td className={classes.titleCol}>MinIO:</td>
<td>
<Button
color="primary"
@@ -461,9 +464,9 @@ const TenantDetails = ({
</td>
</tr>
<tr>
<td>Clusters:</td>
<td className={classes.titleCol}>Clusters:</td>
<td>{poolCount}</td>
<td>Console:</td>
<td className={classes.titleCol}>Console:</td>
<td>
<Button
color="primary"
@@ -477,14 +480,14 @@ const TenantDetails = ({
</td>
</tr>
<tr>
<td>Instances:</td>
<td className={classes.titleCol}>Instances:</td>
<td>{instances}</td>
<td>Volumes:</td>
<td className={classes.titleCol}>Volumes:</td>
<td>{volumes}</td>
</tr>
{tenant?.endpoints && (
<tr>
<td>Endpoint:</td>
<td className={classes.titleCol}>Endpoint:</td>
<td>
<a
href={tenant?.endpoints.minio}
@@ -494,7 +497,7 @@ const TenantDetails = ({
{tenant?.endpoints.minio}
</a>
</td>
<td>Console:</td>
<td className={classes.titleCol}>Console:</td>
<td>
<a
href={tenant?.endpoints.console}
@@ -507,7 +510,7 @@ const TenantDetails = ({
</tr>
)}
<tr>
<td>State:</td>
<td className={classes.titleCol}>State:</td>
<td colSpan={3}>{tenant?.currentState}</td>
</tr>
</table>
@@ -537,7 +540,7 @@ const TenantDetails = ({
</td>
</tr>
<tr>
<td>Logs:</td>
<td className={classes.titleCol}>Logs:</td>
<td>
<Button
color="primary"
@@ -546,7 +549,7 @@ const TenantDetails = ({
{logEnabled ? "Enabled" : "Disabled"}
</Button>
</td>
<td>Monitoring:</td>
<td className={classes.titleCol}>Monitoring:</td>
<td>
<Button
color="primary"
@@ -557,7 +560,7 @@ const TenantDetails = ({
</td>
</tr>
<tr>
<td>Encryption:</td>
<td className={classes.titleCol}>Encryption:</td>
<td>
<Button
color="primary"
@@ -573,7 +576,9 @@ const TenantDetails = ({
{adEnabled ||
(!adEnabled && !oicEnabled && (
<React.Fragment>
<td>Active Directory:</td>
<td className={classes.titleCol}>
Active Directory:
</td>
<td>
<Button
color="primary"
@@ -587,7 +592,7 @@ const TenantDetails = ({
{oicEnabled ||
(!oicEnabled && !adEnabled && (
<React.Fragment>
<td>OpenID:</td>
<td className={classes.titleCol}>OpenID:</td>
<td>
<Button
color="primary"