User policies (#744)
* adding policy users and new UI page * Prettier + aligment Co-authored-by: Adam Stafford <adam@minio.io>
This commit is contained in:
@@ -258,6 +258,10 @@ const Console = ({
|
||||
component: Groups,
|
||||
path: "/groups",
|
||||
},
|
||||
{
|
||||
component: Policies,
|
||||
path: "/policies/:policyName",
|
||||
},
|
||||
{
|
||||
component: Policies,
|
||||
path: "/policies",
|
||||
|
||||
232
portal-ui/src/screens/Console/Policies/ListPolicies.tsx
Normal file
232
portal-ui/src/screens/Console/Policies/ListPolicies.tsx
Normal file
@@ -0,0 +1,232 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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, useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { Policy, PolicyList } from "./types";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import AddPolicy from "./AddPolicy";
|
||||
import DeletePolicy from "./DeletePolicy";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import api from "../../../common/api";
|
||||
import history from "../../../history";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IPoliciesProps {
|
||||
classes: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
const [records, setRecords] = useState<Policy[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedPolicy, setSelectedPolicy] = useState<string>("");
|
||||
const [filterPolicies, setFilterPolicies] = useState<string>("");
|
||||
const [policyEdit, setPolicyEdit] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = get(res, "policies", []);
|
||||
|
||||
policies.sort((pa, pb) => {
|
||||
if (pa.name > pb.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pa.name < pb.name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
setRecords(policies);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loading, setLoading, setRecords, setErrorSnackMessage]);
|
||||
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const closeAddModalAndRefresh = (refresh: boolean) => {
|
||||
setAddScreenOpen(false);
|
||||
|
||||
if (refresh) {
|
||||
fetchRecords();
|
||||
}
|
||||
};
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
|
||||
if (refresh) {
|
||||
fetchRecords();
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeletePolicy = (policy: string) => {
|
||||
setDeleteOpen(true);
|
||||
setSelectedPolicy(policy);
|
||||
};
|
||||
|
||||
const viewAction = (policy: any) => {
|
||||
history.push(`/policies/${policy.name}`);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: viewAction },
|
||||
{ type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true },
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((elementItem) =>
|
||||
elementItem.name.includes(filterPolicies)
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddPolicy
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||
policyEdit={policyEdit}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeletePolicy
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPolicy={selectedPolicy}
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="IAM Policies" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Policies"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterPolicies(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage,
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(ListPolicies));
|
||||
@@ -1,232 +1,33 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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, useEffect } from "react";
|
||||
import React from "react";
|
||||
import history from "../../../history";
|
||||
import { Route, Router, Switch, withRouter } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { Policy, PolicyList } from "./types";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import AddPolicy from "./AddPolicy";
|
||||
import DeletePolicy from "./DeletePolicy";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import api from "../../../common/api";
|
||||
import { AppState } from "../../../store";
|
||||
import { setMenuOpen } from "../../../actions";
|
||||
import NotFoundPage from "../../NotFoundPage";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
import ListPolicies from "./ListPolicies";
|
||||
import PolicyDetails from "./PolicyDetails";
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
const mapState = (state: AppState) => ({
|
||||
open: state.system.sidebarOpen,
|
||||
});
|
||||
|
||||
interface IPoliciesProps {
|
||||
classes: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const Policies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
const [records, setRecords] = useState<Policy[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedPolicy, setSelectedPolicy] = useState<string>("");
|
||||
const [filterPolicies, setFilterPolicies] = useState<string>("");
|
||||
const [policyEdit, setPolicyEdit] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = get(res, "policies", []);
|
||||
|
||||
policies.sort((pa, pb) => {
|
||||
if (pa.name > pb.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pa.name < pb.name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
setRecords(policies);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loading, setLoading, setRecords, setErrorSnackMessage]);
|
||||
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const closeAddModalAndRefresh = (refresh: boolean) => {
|
||||
setAddScreenOpen(false);
|
||||
|
||||
if (refresh) {
|
||||
fetchRecords();
|
||||
}
|
||||
};
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
|
||||
if (refresh) {
|
||||
fetchRecords();
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeletePolicy = (policy: string) => {
|
||||
setDeleteOpen(true);
|
||||
setSelectedPolicy(policy);
|
||||
};
|
||||
|
||||
const viewAction = (row: any) => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(row);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: viewAction },
|
||||
{ type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true },
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((elementItem) =>
|
||||
elementItem.name.includes(filterPolicies)
|
||||
);
|
||||
const connector = connect(mapState, { setMenuOpen });
|
||||
|
||||
const Users = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddPolicy
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||
policyEdit={policyEdit}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeletePolicy
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPolicy={selectedPolicy}
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="IAM Policies" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Policies"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterPolicies(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path="/policies/:policyName" component={PolicyDetails} />
|
||||
<Route path="/" component={ListPolicies} />
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage,
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(Policies));
|
||||
export default withRouter(connector(Users));
|
||||
|
||||
351
portal-ui/src/screens/Console/Policies/PolicyDetails.tsx
Normal file
351
portal-ui/src/screens/Console/Policies/PolicyDetails.tsx
Normal file
@@ -0,0 +1,351 @@
|
||||
// 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 { Policy } from "./types";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
containerForHeader,
|
||||
modalBasic,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import { User } from "../Users/types";
|
||||
import api from "../../../common/api";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setModalErrorSnackMessage,
|
||||
} from "../../../actions";
|
||||
import { Fragment } from "react";
|
||||
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
|
||||
import history from "../../../history";
|
||||
|
||||
interface IPolicyDetailsProps {
|
||||
classes: any;
|
||||
match: any;
|
||||
closeModalAndRefresh: (refresh: boolean) => void;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
multiContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center" as const,
|
||||
justifyContent: "flex-start" as const,
|
||||
},
|
||||
sizeFactorContainer: {
|
||||
marginLeft: 8,
|
||||
},
|
||||
containerHeader: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
paperContainer: {
|
||||
padding: "15px 15px 15px 50px",
|
||||
},
|
||||
infoGrid: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "auto auto auto auto",
|
||||
gridGap: 8,
|
||||
"& div": {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
"& div:nth-child(odd)": {
|
||||
justifyContent: "flex-end",
|
||||
fontWeight: 700,
|
||||
},
|
||||
"& div:nth-child(2n)": {
|
||||
paddingRight: 35,
|
||||
},
|
||||
},
|
||||
masterActions: {
|
||||
width: "25%",
|
||||
minWidth: "120px",
|
||||
"& div": {
|
||||
margin: "5px 0px",
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
},
|
||||
updateButton: {
|
||||
backgroundColor: "transparent",
|
||||
border: 0,
|
||||
padding: "0 6px",
|
||||
cursor: "pointer",
|
||||
"&:focus, &:active": {
|
||||
outline: "none",
|
||||
},
|
||||
"& svg": {
|
||||
height: 12,
|
||||
},
|
||||
},
|
||||
noUnderLine: {
|
||||
textDecoration: "none",
|
||||
},
|
||||
poolLabel: {
|
||||
color: "#666666",
|
||||
},
|
||||
licenseContainer: {
|
||||
position: "relative",
|
||||
padding: "20px 52px 0px 28px",
|
||||
background: "#032F51",
|
||||
boxShadow: "0px 3px 7px #00000014",
|
||||
"& h2": {
|
||||
color: "#FFF",
|
||||
marginBottom: 67,
|
||||
},
|
||||
"& a": {
|
||||
textDecoration: "none",
|
||||
},
|
||||
"& h3": {
|
||||
color: "#FFFFFF",
|
||||
marginBottom: "30px",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
"& h6": {
|
||||
color: "#FFFFFF !important",
|
||||
},
|
||||
},
|
||||
licenseInfo: { color: "#FFFFFF", position: "relative" },
|
||||
licenseInfoTitle: {
|
||||
textTransform: "none",
|
||||
color: "#BFBFBF",
|
||||
fontSize: 11,
|
||||
},
|
||||
licenseInfoValue: {
|
||||
textTransform: "none",
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
verifiedIcon: {
|
||||
width: 96,
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
bottom: 29,
|
||||
},
|
||||
breadcrumLink: {
|
||||
textDecoration: "none",
|
||||
color: "black",
|
||||
},
|
||||
...modalBasic,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const PolicyDetails = ({
|
||||
classes,
|
||||
match,
|
||||
closeModalAndRefresh,
|
||||
setErrorSnackMessage,
|
||||
}: IPolicyDetailsProps) => {
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
const [policy, setPolicy] = useState<Policy | null>(null);
|
||||
const [userList, setUserList] = useState<User[]>([]);
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [policyName, setPolicyName] = useState<string>(
|
||||
match.params["policyName"]
|
||||
);
|
||||
const [policyDefinition, setPolicyDefinition] = useState<string>("");
|
||||
const [loadingPolicy, setLoadingPolicy] = useState<boolean>(true);
|
||||
const [loadingUsers, setLoadingUsers] = useState<boolean>(true);
|
||||
|
||||
const loadUsersForPolicy = () => {
|
||||
if (loadingUsers) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies/${policyName}/users`)
|
||||
.then((result: any) => {
|
||||
setUserList(result);
|
||||
setLoadingUsers(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Error in loading users");
|
||||
});
|
||||
}
|
||||
};
|
||||
const loadPolicyDetails = () => {
|
||||
if (loadingPolicy) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policy?name=${policyName}`)
|
||||
.then((result: any) => {
|
||||
setPolicy(result);
|
||||
setLoadingPolicy(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Error in loading policy");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
setAddLoading(true);
|
||||
api
|
||||
.invoke("POST", "/api/v1/policies", {
|
||||
name: policyName,
|
||||
policy: policyDefinition,
|
||||
})
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
closeModalAndRefresh(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingPolicy) {
|
||||
loadUsersForPolicy();
|
||||
loadPolicyDetails();
|
||||
if (policy) {
|
||||
setPolicyName(policy.name);
|
||||
setPolicyDefinition(
|
||||
policy ? JSON.stringify(JSON.parse(policy.policy), null, 4) : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [policy, loadingPolicy]);
|
||||
|
||||
const resetForm = () => {
|
||||
setPolicyName("");
|
||||
setPolicyDefinition("");
|
||||
};
|
||||
|
||||
const validSave = policyName.trim() !== "";
|
||||
|
||||
const userViewAction = (user: any) => {
|
||||
history.push(`/users/${user}`);
|
||||
};
|
||||
const userTableActions = [{ type: "view", onClick: userViewAction }];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader
|
||||
label={
|
||||
<Fragment>
|
||||
<Link to={"/policies"} className={classes.breadcrumLink}>
|
||||
Policy
|
||||
</Link>
|
||||
{` > ${match.params["policyName"]}`}
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12}>
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
onChange={(_, newValue: number) => {
|
||||
setSelectedTab(newValue);
|
||||
}}
|
||||
aria-label="policy-tabs"
|
||||
>
|
||||
<Tab label="Details" />
|
||||
<Tab label="Users" />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{selectedTab === 0 && (
|
||||
<Paper className={classes.paperContainer}>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<CodeMirrorWrapper
|
||||
label={`${policy ? "Edit" : "Write"} Policy`}
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
{!policy && (
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !validSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
)}
|
||||
{selectedTab === 1 && (
|
||||
<TableWrapper
|
||||
itemActions={userTableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={false}
|
||||
records={userList}
|
||||
entityName="Servers"
|
||||
idField="name"
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
setErrorSnackMessage,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(PolicyDetails));
|
||||
Reference in New Issue
Block a user