Skip to content

Commit fda090f

Browse files
authored
Created Add Policy screen (#1896)
1 parent 74d4c4a commit fda090f

File tree

5 files changed

+358
-21
lines changed

5 files changed

+358
-21
lines changed

portal-ui/src/common/SecureComponent/permissions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export const IAM_PAGES = {
127127
ACCOUNT_ADD: "/identity/new-account",
128128
/* Access */
129129
POLICIES: "/access/policies",
130+
POLICY_ADD: "/access/add-policy",
130131
POLICIES_VIEW: "/access/policies/*",
131132
/* Monitoring */
132133
TOOLS_LOGS: "/tools/logs",
@@ -329,6 +330,9 @@ export const IAM_PAGES_PERMISSIONS = {
329330
IAM_SCOPES.ADMIN_LIST_USER_POLICIES, // displays policies
330331
IAM_SCOPES.ADMIN_CREATE_POLICY, // displays create policy button
331332
],
333+
[IAM_PAGES.POLICY_ADD]: [
334+
IAM_SCOPES.ADMIN_CREATE_POLICY, // displays create policy button
335+
],
332336
[IAM_PAGES.SETTINGS]: [
333337
IAM_SCOPES.ADMIN_CONFIG_UPDATE, // displays configuration list
334338
],

portal-ui/src/screens/Console/Console.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ const ObjectManager = React.lazy(
102102

103103
const Buckets = React.lazy(() => import("./Buckets/Buckets"));
104104
const Policies = React.lazy(() => import("./Policies/Policies"));
105+
106+
const AddPolicy = React.lazy(() => import("./Policies/AddPolicyScreen"));
105107
const Dashboard = React.lazy(() => import("./Dashboard/Dashboard"));
106108

107109
const Account = React.lazy(() => import("./Account/Account"));
@@ -304,6 +306,10 @@ const Console = ({
304306
component: Policies,
305307
path: IAM_PAGES.POLICIES_VIEW,
306308
},
309+
{
310+
component: AddPolicy,
311+
path: IAM_PAGES.POLICY_ADD,
312+
},
307313
{
308314
component: Policies,
309315
path: IAM_PAGES.POLICIES,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
import React from "react";
17+
import { Box } from "@mui/material";
18+
import {
19+
HelpIconFilled,
20+
IAMPoliciesIcon,
21+
} from "../../../icons";
22+
23+
const FeatureItem = ({
24+
icon,
25+
description,
26+
}: {
27+
icon: any;
28+
description: string;
29+
}) => {
30+
return (
31+
<Box
32+
sx={{
33+
display: "flex",
34+
"& .min-icon": {
35+
marginRight: "10px",
36+
height: "23px",
37+
width: "23px",
38+
marginBottom: "10px",
39+
},
40+
}}
41+
>
42+
{icon}{" "}
43+
<div style={{ fontSize: "14px", fontStyle: "italic", color: "#5E5E5E" }}>
44+
{description}
45+
</div>
46+
</Box>
47+
);
48+
};
49+
const AddPolicyHelpBox = ({ hasMargin = true }: { hasMargin?: boolean }) => {
50+
return (
51+
<Box
52+
sx={{
53+
flex: 1,
54+
border: "1px solid #eaeaea",
55+
borderRadius: "2px",
56+
display: "flex",
57+
flexFlow: "column",
58+
padding: "20px",
59+
marginLeft: {
60+
xs: "0px",
61+
sm: "0px",
62+
md: hasMargin ? "30px" : "",
63+
},
64+
marginTop: {
65+
xs: "0px",
66+
},
67+
}}
68+
>
69+
<Box
70+
sx={{
71+
fontSize: "16px",
72+
fontWeight: 600,
73+
display: "flex",
74+
alignItems: "center",
75+
marginBottom: "16px",
76+
paddingBottom: "20px",
77+
78+
"& .min-icon": {
79+
height: "21px",
80+
width: "21px",
81+
marginRight: "15px",
82+
},
83+
}}
84+
>
85+
<HelpIconFilled />
86+
<div>Learn more about Policies</div>
87+
</Box>
88+
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
89+
<Box sx={{paddingBottom: "20px"}}>
90+
<FeatureItem icon={<IAMPoliciesIcon />} description={`Create Policies`} />
91+
<Box sx={{ paddingTop: "20px"}}>
92+
MinIO uses Policy-Based Access Control (PBAC) to define the authorized actions and resources to which an authenticated user has access. Each policy describes one or more actions and conditions that outline the permissions of a user or group of users. </Box>
93+
</Box>
94+
<Box sx={{paddingBottom: "20px"}}>
95+
MinIO PBAC is built for compatibility with AWS IAM policy syntax, structure, and behavior.
96+
</Box>
97+
<Box sx={{paddingBottom: "20px"}}>
98+
Each user can access only those resources and operations which are explicitly granted by the built-in role. MinIO denies access to any other resource or action by default.
99+
</Box>
100+
</Box>
101+
</Box>
102+
);
103+
};
104+
105+
export default AddPolicyHelpBox;
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import React, { Fragment, useState } from "react";
18+
import { Theme } from "@mui/material/styles";
19+
import createStyles from "@mui/styles/createStyles";
20+
import withStyles from "@mui/styles/withStyles";
21+
import {
22+
formFieldStyles,
23+
modalStyleUtils,
24+
} from "../Common/FormComponents/common/styleLibrary";
25+
import Grid from "@mui/material/Grid";
26+
import { Button, Box} from "@mui/material";
27+
import PageHeader from "../Common/PageHeader/PageHeader";
28+
import history from "../../../../src/history";
29+
import PageLayout from "../Common/Layout/PageLayout";
30+
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
31+
import AddPolicyHelpBox from "./AddPolicyHelpBox";
32+
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
33+
import BackLink from "../../../common/BackLink";
34+
import { connect } from "react-redux";
35+
import { AddAccessRuleIcon } from "../../../icons";
36+
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
37+
import { ErrorResponseHandler } from "../../../../src/common/types";
38+
import api from "../../../../src/common/api";
39+
import { setErrorSnackMessage } from "../../../../src/actions";
40+
41+
interface IAddPolicyProps {
42+
classes: any;
43+
setErrorSnackMessage: typeof setErrorSnackMessage;
44+
}
45+
46+
const styles = (theme: Theme) =>
47+
createStyles({
48+
buttonContainer: {
49+
textAlign: "right",
50+
},
51+
bottomContainer: {
52+
display: "flex",
53+
flexGrow: 1,
54+
alignItems: "center",
55+
margin: "auto",
56+
justifyContent: "center",
57+
"& div": {
58+
width: 150,
59+
"@media (max-width: 900px)": {
60+
flexFlow: "column",
61+
},
62+
},
63+
},
64+
factorElements: {
65+
display: "flex",
66+
justifyContent: "flex-start",
67+
marginLeft: 30,
68+
},
69+
sizeNumber: {
70+
fontSize: 35,
71+
fontWeight: 700,
72+
textAlign: "center",
73+
},
74+
sizeDescription: {
75+
fontSize: 14,
76+
color: "#777",
77+
textAlign: "center",
78+
},
79+
pageBox: {
80+
border: "1px solid #EAEAEA",
81+
borderTop: 0,
82+
},
83+
addPoolTitle: {
84+
border: "1px solid #EAEAEA",
85+
borderBottom: 0,
86+
},
87+
headTitle: {
88+
fontWeight: "bold",
89+
fontSize: 20,
90+
paddingLeft: 20,
91+
paddingBottom: 40,
92+
paddingTop: 8,
93+
textAlign: "end",
94+
},
95+
headIcon: {
96+
fontWeight: "bold",
97+
size: "50",
98+
},
99+
...formFieldStyles,
100+
...modalStyleUtils,
101+
});
102+
103+
const AddPolicyScreen = ({
104+
classes,
105+
setErrorSnackMessage,
106+
}: IAddPolicyProps) => {
107+
const [addLoading, setAddLoading] = useState<boolean>(false);
108+
const [policyName, setPolicyName] = useState<string>("");
109+
const [policyDefinition, setPolicyDefinition] = useState<string>("");
110+
111+
const addRecord = (event: React.FormEvent) => {
112+
event.preventDefault();
113+
if (addLoading) {
114+
return;
115+
}
116+
setAddLoading(true);
117+
api
118+
.invoke("POST", "/api/v1/policies", {
119+
name: policyName,
120+
policy: policyDefinition,
121+
})
122+
.then((res) => {
123+
setAddLoading(false);
124+
history.push(`${IAM_PAGES.POLICIES}`);
125+
})
126+
.catch((err: ErrorResponseHandler) => {
127+
setAddLoading(false);
128+
setErrorSnackMessage(err);
129+
});
130+
};
131+
132+
133+
const resetForm = () => {
134+
setPolicyName("");
135+
setPolicyDefinition("");
136+
};
137+
138+
const validSave = policyName.trim() !== "";
139+
140+
141+
142+
return (
143+
<Fragment>
144+
<Grid item xs={12}>
145+
<PageHeader
146+
label={<BackLink to={IAM_PAGES.POLICIES} label={"Policies"} />}
147+
/>
148+
<PageLayout>
149+
<Grid
150+
item
151+
xs={12}
152+
container
153+
className={classes.title}
154+
align-items="stretch"
155+
>
156+
<Grid item className={classes.headIcon}>
157+
<AddAccessRuleIcon />
158+
</Grid>
159+
<Grid item className={classes.headTitle}>
160+
Create Policy
161+
</Grid>
162+
</Grid>
163+
164+
<Grid container align-items="center">
165+
<Grid item xs={8}>
166+
<Box>
167+
<form noValidate
168+
autoComplete="off"
169+
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
170+
addRecord(e);
171+
}}>
172+
<Grid container item spacing = "20">
173+
174+
<Grid item xs={12} >
175+
<Grid container>
176+
<Grid item xs={12} className={classes.formFieldRow}>
177+
<InputBoxWrapper
178+
id="policy-name"
179+
name="policy-name"
180+
label="Policy Name"
181+
autoFocus={true}
182+
value={policyName}
183+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
184+
setPolicyName(e.target.value);
185+
}}
186+
/>
187+
</Grid>
188+
<Grid item xs={12} className={classes.userSelector}>
189+
<CodeMirrorWrapper
190+
label={"Write Policy"}
191+
value={policyDefinition}
192+
onBeforeChange={(editor, data, value) => {
193+
setPolicyDefinition(value);
194+
}}
195+
editorHeight={"350px"}
196+
/>
197+
</Grid>
198+
</Grid>
199+
<Grid item xs={12} className={classes.modalButtonBar}>
200+
<Button
201+
type="button"
202+
variant="outlined"
203+
color="primary"
204+
className={classes.spacerRight}
205+
onClick={resetForm}
206+
>
207+
Clear
208+
</Button>
209+
210+
<Button
211+
type="submit"
212+
variant="contained"
213+
color="primary"
214+
disabled={addLoading || !validSave}
215+
>
216+
Save
217+
</Button>
218+
</Grid>
219+
</Grid>
220+
</Grid>
221+
</form>
222+
</Box>
223+
</Grid>
224+
<Grid item xs={4}>
225+
<Box>
226+
<AddPolicyHelpBox />
227+
</Box>
228+
</Grid>
229+
</Grid>
230+
</PageLayout>
231+
</Grid>
232+
</Fragment>
233+
);
234+
};
235+
236+
const mapDispatchToProps = {
237+
setErrorSnackMessage,
238+
};
239+
240+
const connector = connect(null, mapDispatchToProps);
241+
242+
export default withStyles(styles)(connector(AddPolicyScreen));

0 commit comments

Comments
 (0)