From 006b3c7da8d62f4fe4748b73e271b8fd15430946 Mon Sep 17 00:00:00 2001 From: Prakash Senthil Vel <23444145+prakashsvmx@users.noreply.github.com> Date: Tue, 22 Feb 2022 18:05:31 +0000 Subject: [PATCH] UI for Inspect (#1583) --- .../src/common/SecureComponent/permissions.ts | 2 + portal-ui/src/common/utils.ts | 17 + .../FormSwitchWrapper/FormSwitchWrapper.tsx | 5 +- .../FormComponents/common/styleLibrary.ts | 4 +- portal-ui/src/screens/Console/Console.tsx | 4 + .../src/screens/Console/Tools/Inspect.tsx | 501 ++++++++++++++++++ portal-ui/src/screens/Console/Tools/Tools.tsx | 7 +- portal-ui/src/screens/Console/valid-routes.ts | 8 + portal-ui/tests/permissions/inspect.ts | 201 +++++++ portal-ui/tests/policies/inspect-allowed.json | 22 + .../tests/policies/inspect-not-allowed.json | 22 + portal-ui/tests/scripts/cleanup-env.sh | 4 + portal-ui/tests/scripts/common.sh | 6 + portal-ui/tests/scripts/permissions.sh | 4 + 14 files changed, 803 insertions(+), 4 deletions(-) create mode 100644 portal-ui/src/screens/Console/Tools/Inspect.tsx create mode 100644 portal-ui/tests/permissions/inspect.ts create mode 100644 portal-ui/tests/policies/inspect-allowed.json create mode 100644 portal-ui/tests/policies/inspect-not-allowed.json diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts index 63986fcf9..56fb46e90 100644 --- a/portal-ui/src/common/SecureComponent/permissions.ts +++ b/portal-ui/src/common/SecureComponent/permissions.ts @@ -132,6 +132,7 @@ export const IAM_PAGES = { DASHBOARD: "/tools/metrics", TOOLS_HEAL: "/tools/heal", TOOLS_WATCH: "/tools/watch", + TOOLS_INSPECT: "/tools/inspect", /* Health */ HEALTH: "/health", @@ -366,6 +367,7 @@ export const IAM_PAGES_PERMISSIONS = { [IAM_PAGES.CALL_HOME]: [IAM_SCOPES.ADMIN_HEALTH_INFO], [IAM_PAGES.PROFILE]: [IAM_SCOPES.ADMIN_HEALTH_INFO], [IAM_PAGES.HEALTH]: [IAM_SCOPES.ADMIN_HEALTH_INFO], + [IAM_PAGES.TOOLS_INSPECT]: [IAM_SCOPES.ADMIN_HEALTH_INFO], [IAM_PAGES.LICENSE]: [ IAM_SCOPES.ADMIN_SERVER_INFO, IAM_SCOPES.ADMIN_CONFIG_UPDATE, diff --git a/portal-ui/src/common/utils.ts b/portal-ui/src/common/utils.ts index 46de1d97f..170fe52dd 100644 --- a/portal-ui/src/common/utils.ts +++ b/portal-ui/src/common/utils.ts @@ -609,3 +609,20 @@ export const decodeFileName = (text: string) => { return text; } }; + +export const performDownload = (blob: Blob, fileName: string) => { + const link = document.createElement("a"); + link.href = window.URL.createObjectURL(blob); + link.download = fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}; + +export const getCookieValue = (cookieName: string) => { + return ( + document.cookie + .match("(^|;)\\s*" + cookieName + "\\s*=\\s*([^;]+)") + ?.pop() || "" + ); +}; diff --git a/portal-ui/src/screens/Console/Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper.tsx b/portal-ui/src/screens/Console/Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper.tsx index 8c92d9c78..6472b97ed 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper.tsx +++ b/portal-ui/src/screens/Console/Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper.tsx @@ -23,6 +23,7 @@ import Grid from "@mui/material/Grid"; import { actionsTray, fieldBasic } from "../common/styleLibrary"; import HelpIcon from "../../../../../icons/HelpIcon"; import clsx from "clsx"; +import { InputProps as StandardInputProps } from "@mui/material/Input/Input"; interface IFormSwitch { label?: string; @@ -38,6 +39,7 @@ interface IFormSwitch { checked: boolean; switchOnly?: boolean; indicatorLabels?: string[]; + extraInputProps?: StandardInputProps["inputProps"]; } const styles = (theme: Theme) => @@ -125,6 +127,7 @@ const FormSwitchWrapper = ({ description = "", classes, indicatorLabels, + extraInputProps = {}, }: IFormSwitch) => { const switchComponent = ( @@ -144,7 +147,7 @@ const FormSwitchWrapper = ({ onChange={onChange} color="primary" name={name} - inputProps={{ "aria-label": "primary checkbox" }} + inputProps={{ "aria-label": "primary checkbox", ...extraInputProps }} disabled={disabled} disableRipple disableFocusRipple diff --git a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts index 39dfca2ba..a5f8112e4 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts +++ b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts @@ -414,7 +414,7 @@ export const selectorsCommon = { }, }; -export const settingsCommon = { +export const settingsCommon:any = { customTitle: { fontSize: 18, color: "#000", @@ -1152,7 +1152,7 @@ export const spacingUtils: any = { }, }; -export const formFieldStyles = { +export const formFieldStyles:any = { formFieldRow: { marginBottom: ".8rem", "& .MuiInputLabel-root": { diff --git a/portal-ui/src/screens/Console/Console.tsx b/portal-ui/src/screens/Console/Console.tsx index 4dcd35e83..99a1a859f 100644 --- a/portal-ui/src/screens/Console/Console.tsx +++ b/portal-ui/src/screens/Console/Console.tsx @@ -326,6 +326,10 @@ const Console = ({ component: Tools, path: IAM_PAGES.PROFILE, }, + { + component: Tools, + path: IAM_PAGES.TOOLS_INSPECT, + }, { component: ConfigurationOptions, path: IAM_PAGES.SETTINGS, diff --git a/portal-ui/src/screens/Console/Tools/Inspect.tsx b/portal-ui/src/screens/Console/Tools/Inspect.tsx new file mode 100644 index 000000000..f43697ef5 --- /dev/null +++ b/portal-ui/src/screens/Console/Tools/Inspect.tsx @@ -0,0 +1,501 @@ +import React, { Fragment, useEffect, useState } from "react"; +import { Box, Button, DialogContentText } from "@mui/material"; +import PageHeader from "../Common/PageHeader/PageHeader"; +import PageLayout from "../Common/Layout/PageLayout"; +import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; +import { CopyIcon, FileBookIcon, PasswordKeyIcon } from "../../../icons"; +import ModalWrapper from "../Common/ModalWrapper/ModalWrapper"; +import { Theme } from "@mui/material/styles"; +import createStyles from "@mui/styles/createStyles"; +import { + deleteDialogStyles, + modalStyleUtils, +} from "../Common/FormComponents/common/styleLibrary"; +import withStyles from "@mui/styles/withStyles"; +import { setErrorSnackMessage } from "../../../actions"; +import { connect } from "react-redux"; +import HelpBox from "../../../common/HelpBox"; +import { + deleteCookie, + getCookieValue, + performDownload, +} from "../../../common/utils"; +import DistributedOnly from "../Common/DistributedOnly/DistributedOnly"; +import { AppState } from "../../../store"; +import { InspectMenuIcon } from "../../../icons/SidebarMenus"; + +const styles = (theme: Theme) => + createStyles({ + switchLabel: { + fontWeight: "normal", + }, + ...deleteDialogStyles, + ...modalStyleUtils, + }); + +const KeyRevealer = ({ value }: { value: string }) => { + const [shown, setShown] = React.useState(false); + + return ( + + {}} + value={value} + overlayIcon={} + extraInputProps={{ + readOnly: true, + }} + overlayAction={() => navigator.clipboard.writeText(value)} + /> + + + + ); +}; + +const mapState = (state: AppState) => ({ + distributedSetup: state.system.distributedSetup, +}); + +const mapDispatchToProps = { + setErrorSnackMessage, +}; +const connector = connect(mapState, mapDispatchToProps); + +const Inspect = ({ + classes, + setErrorSnackMessage, + distributedSetup, +}: { + classes: any; + setErrorSnackMessage: any; + distributedSetup: boolean; +}) => { + const [volumeName, setVolumeName] = useState(""); + const [inspectPath, setInspectPath] = useState(""); + const [isEncrypt, setIsEncrypt] = useState(true); + + const [decryptionKey, setDecryptionKey] = useState(""); + + const [insFileName, setInsFileName] = useState(""); + + const [isFormValid, setIsFormValid] = useState(false); + const [volumeError, setVolumeError] = useState(""); + const [pathError, setPathError] = useState(""); + + /** + * Validation Effect + */ + useEffect(() => { + let isVolValid; + let isPathValid; + + isVolValid = volumeName.trim().length > 0; + if (!isVolValid) { + setVolumeError("This field is required"); + } else if (volumeName.slice(0, 1) === "/") { + isVolValid = false; + setVolumeError("Volume/Bucket name cannot start with /"); + } + isPathValid = inspectPath.trim().length > 0; + if (!inspectPath) { + setPathError("This field is required"); + } else if (inspectPath.slice(0, 1) === "/") { + isPathValid = false; + setPathError("Path cannot start with /"); + } + const isValid = isVolValid && isPathValid; + + if (isVolValid) { + setVolumeError(""); + } + if (isPathValid) { + setPathError(""); + } + + setIsFormValid(isValid); + }, [volumeName, inspectPath]); + + const makeRequest = async (url: string) => { + return await fetch(url, { method: "GET" }); + }; + + const performInspect = async () => { + const file = encodeURIComponent(inspectPath); + const volume = encodeURIComponent(volumeName); + + const urlOfInspectApi = `/api/v1/admin/inspect?volume=${volume}&file=${file}&encrypt=${isEncrypt}`; + + makeRequest(urlOfInspectApi) + .then(async (res) => { + if (!res.ok) { + const resErr: any = await res.json(); + + setErrorSnackMessage({ + errorMessage: resErr.message, + detailedError: resErr.code, + }); + } + const blob: Blob = await res.blob(); + + //@ts-ignore + const filename = res.headers.get("content-disposition").split('"')[1]; + const decryptKey = getCookieValue(filename) || ""; + + performDownload(blob, filename); + setInsFileName(filename); + setDecryptionKey(decryptKey); + }) + .catch((err) => { + setErrorSnackMessage(err); + }); + }; + + const resetForm = () => { + setVolumeName(""); + setInspectPath(""); + setIsEncrypt(true); + }; + + const onCloseDecKeyModal = () => { + deleteCookie(insFileName); + setDecryptionKey(""); + resetForm(); + }; + + return ( + + + + {!distributedSetup ? ( + } + entity={"Inspect"} + /> + ) : ( + + +
) => { + e.preventDefault(); + performInspect(); + }} + > + + ) => { + setVolumeName(e.target.value); + }} + label="Volume or Bucket Name" + value={volumeName} + error={volumeError} + required + placeholder={"test-bucket"} + /> + + + ) => { + setInspectPath(e.target.value); + }} + label="File or Path to inspect" + value={inspectPath} + required + placeholder={"test*/xl.meta"} + /> + + + { + setIsEncrypt(!isEncrypt); + }} + /> + + + + + +
+
+ + } + help={ + + + Inspect files on MinIO server + + + + Examples + + + + + +
+ To Download 'xl.meta' for a specific object from all + the drives in a zip file: +
+
+ + + + {" "} + test-bucket +
+ + test*/xl.meta + + + + + + +
+ To Download all constituent parts for a specific + object, and optionally encrypt the downloaded zip: +
+
+ + + + + test-bucket +
+ test*/*/part.* + + + + + +
+ To Download recursively all objects at a prefix. +
+ NOTE: This can be an expensive operation use it with + caution. +
+
+ + + + + test-bucket +
+ test/** + + + + + + + You can learn more at our{" "} + + documentation + + . + + + } + /> + + + )} + {decryptionKey ? ( + } + > + + + This will be displayed only once. It cannot be recovered. +
+ Use secure medium to share this key. +
+ + + +
+
+ ) : null} + + + ); +}; + +export default withStyles(styles)(connector(Inspect)); diff --git a/portal-ui/src/screens/Console/Tools/Tools.tsx b/portal-ui/src/screens/Console/Tools/Tools.tsx index 4a4fb5831..834c14d7a 100644 --- a/portal-ui/src/screens/Console/Tools/Tools.tsx +++ b/portal-ui/src/screens/Console/Tools/Tools.tsx @@ -19,11 +19,15 @@ import { Route, Router, Switch } from "react-router-dom"; import history from "../../../history"; import NotFoundPage from "../../NotFoundPage"; import ToolsList from "./ToolsPanel/ToolsList"; -import Register from "../Support/Register"; import { IAM_PAGES } from "../../../common/SecureComponent/permissions"; import FeatureNotAvailablePage from "../Common/Components/FeatureNotAvailablePage"; import { SupportMenuIcon } from "../../../icons/SidebarMenus"; +import withSuspense from "../Common/Components/withSuspense"; + +const Inspect = withSuspense(React.lazy(() => import("./Inspect"))); +const Register = withSuspense(React.lazy(() => import("../Support/Register"))); + const Tools = () => { return ( @@ -72,6 +76,7 @@ const Tools = () => { ); }} /> + diff --git a/portal-ui/src/screens/Console/valid-routes.ts b/portal-ui/src/screens/Console/valid-routes.ts index c107088c1..4321fe632 100644 --- a/portal-ui/src/screens/Console/valid-routes.ts +++ b/portal-ui/src/screens/Console/valid-routes.ts @@ -39,6 +39,7 @@ import { SupportMenuIcon, TraceMenuIcon, UsersMenuIcon, + InspectMenuIcon, } from "../../icons/SidebarMenus"; import { hasPermission } from "../../common/SecureComponent"; import WatchIcon from "../../icons/WatchIcon"; @@ -159,6 +160,13 @@ export const validRoutes = ( icon: DrivesMenuIcon, component: NavLink, }, + { + name: "Inspect", + id: "inspectObjects", + to: IAM_PAGES.TOOLS_INSPECT, + icon: InspectMenuIcon, + component: NavLink, + }, ], }, { diff --git a/portal-ui/tests/permissions/inspect.ts b/portal-ui/tests/permissions/inspect.ts new file mode 100644 index 000000000..870ddedd7 --- /dev/null +++ b/portal-ui/tests/permissions/inspect.ts @@ -0,0 +1,201 @@ +// 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 . + +import { Role, Selector } from "testcafe"; +import { readFileSync } from "fs"; + +const data = readFileSync(__dirname + "/../constants/timestamp.txt", "utf-8"); +const $TIMESTAMP = data.trim(); + +let testDomainUrl = "http://localhost:9090"; + +let insAllowedAccKey = `inspect-allowed-${$TIMESTAMP}`; +let insAllowedSeckey = "insallowed1234"; +let insNotAllowedAccKey = `inspect-not-allowed-${$TIMESTAMP}`; +let insNotAllowedSeckey = "insnotallowed1234"; + +/* Begin Local Testing config block */ + +// For local Testing Create users and assign policies then update here. +// Command to invoke the test locally: testcafe chrome tests/permissions/inspect.ts +/* +testDomainUrl = "http://localhost:5005"; +insAllowedAccKey = `all-actions`; +insAllowedSeckey = "minio123"; +insNotAllowedAccKey = `deny-admin`; +insNotAllowedSeckey = "minio123"; +*/ + +/* End Local Testing config block */ + +const loginUrl = `${testDomainUrl}/login`; +const inspectScreenUrl = `${testDomainUrl}/tools/inspect`; + +const loginSubmitBtn = Selector("form button"); + +export const supportSidebarEl = Selector(".MuiPaper-root") + .find("ul") + .child("#tools"); + +export const supportChildren = Selector("#tools-children"); +export const inspectEl = supportChildren + .find("a") + .withAttribute("href", "/tools/inspect"); + +export const inspect_volume_input = Selector('[data-test-id="inspect_volume"]'); +export const inspect_path_input = Selector('[data-test-id="inspect_path"]'); + +export const inspect_volume_input_err = Selector("#inspect_volume-helper-text"); +export const inspect_path_input_err = Selector("#inspect_path-helper-text"); + +export const inspect_encrypt_input = Selector( + '[data-test-id="inspect_encrypt"]' +); +export const inspect_form_clear_btn = Selector( + '[data-test-id="inspect-clear-button"]' +); +export const inspect_form_submit_btn = Selector( + '[data-test-id="inspect-submit-button"]' +); +/** Begin Allowed Policy Test **/ + +export const inspectAllowedRole = Role( + loginUrl, + async (t) => { + await t + .typeText("#accessKey", insAllowedAccKey) + .typeText("#secretKey", insAllowedSeckey) + .click(loginSubmitBtn); + }, + { preserveUrl: true } +); + +fixture("For user with Inspect permissions") + .page(testDomainUrl) + .beforeEach(async (t) => { + await t.useRole(inspectAllowedRole); + }); + +test("Inspect page can be opened", async (t) => { + await t.navigateTo(inspectScreenUrl); +}); +test("Inspect sidebar item exists", async (t) => { + await t.expect(supportSidebarEl.exists).ok(); +}); + +test("Inspect link exists in Menu list", async (t) => { + await t + .expect(supportSidebarEl.exists) + .ok() + .click(supportSidebarEl) + .expect(inspectEl.exists) + .ok(); +}); + +test("Form Input states verification", async (t) => { + const volumeValue = "test"; + const pathValue = "test.txt/xl.meta"; + + await t.navigateTo(inspectScreenUrl); + + //Initial state verification + await t.expect(inspect_volume_input.value).eql(""); + await t.expect(inspect_path_input.value).eql(""); + await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).ok(); + await t.expect(inspect_encrypt_input.hasAttribute("checked")).ok(); + await t + .expect(inspect_volume_input_err.innerText) + .eql("This field is required"); + await t + .expect(inspect_path_input_err.innerText) + .eql("This field is required"); + + //Enter form values + await t.typeText(inspect_volume_input, "/").typeText(inspect_path_input, "/"); + + //verify post state of Invalid values + await t.expect(inspect_volume_input.value).eql("/"); + await t.expect(inspect_path_input.value).eql("/"); + await t + .expect(inspect_volume_input_err.innerText) + .eql("Volume/Bucket name cannot start with /"); + await t + .expect(inspect_path_input_err.innerText) + .eql("Path cannot start with /"); + await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).eql(true); + await t.expect(inspect_form_clear_btn.hasAttribute("disabled")).eql(false); + + //Important. Testcafe's way to clear input values. + await t.selectText(inspect_volume_input).pressKey("delete"); + await t.selectText(inspect_path_input).pressKey("delete"); + + //Enter Valid form values + await t + .typeText(inspect_volume_input, volumeValue) + .typeText(inspect_path_input, pathValue); + + //verify post state of valid values + await t.expect(inspect_volume_input.value).eql(volumeValue); + await t.expect(inspect_path_input.value).eql(pathValue); + await t.expect(inspect_volume_input_err.exists).notOk(); + await t.expect(inspect_path_input_err.exists).notOk(); + + await t.click(inspect_form_clear_btn); + //reset state verification + await t.expect(inspect_volume_input.value).eql(""); + await t.expect(inspect_path_input.value).eql(""); + await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).eql(true); +}); +/** End Allowed Policy Test **/ + +/** Begin Not Allowed Policy Test **/ + +export const inspectNotAllowedRole = Role( + loginUrl, + async (t) => { + await t + .typeText("#accessKey", insNotAllowedAccKey) + .typeText("#secretKey", insNotAllowedSeckey) + .click(loginSubmitBtn); + }, + { preserveUrl: true } +); + +fixture("For user with Denied Inspect permissions") + .page(testDomainUrl) + .beforeEach(async (t) => { + await t.useRole(inspectNotAllowedRole); + }); + +test("Inspect page can NOT be opened", async (t) => { + try { + await t.navigateTo(inspectScreenUrl); + } catch (e) { + await t.expect(e).ok(); + } +}); + +test("Inspect link should NOT exists in Menu list", async (t) => { + await t + .expect(supportSidebarEl.exists) + .ok() + .click(supportSidebarEl) + .expect(inspectEl.exists) + .notOk( + "Inspect Link should not exist in the menu list as per inspect not allowed policy" + ); +}); +/** End Not Allowed Policy Test **/ diff --git a/portal-ui/tests/policies/inspect-allowed.json b/portal-ui/tests/policies/inspect-allowed.json new file mode 100644 index 000000000..f605b82b8 --- /dev/null +++ b/portal-ui/tests/policies/inspect-allowed.json @@ -0,0 +1,22 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "admin:*" + ], + "Effect": "Allow", + "Sid": "Allow_Admin_Actions" + }, + { + "Action": [ + "s3:*" + ], + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::*" + ], + "Sid": "Allow_S3_Actions" + } + ] +} \ No newline at end of file diff --git a/portal-ui/tests/policies/inspect-not-allowed.json b/portal-ui/tests/policies/inspect-not-allowed.json new file mode 100644 index 000000000..86c32cd87 --- /dev/null +++ b/portal-ui/tests/policies/inspect-not-allowed.json @@ -0,0 +1,22 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "admin:*" + ], + "Effect": "Deny", + "Sid": "Deny_Admin_Actions" + }, + { + "Action": [ + "s3:*" + ], + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::*" + ], + "Sid": "Allow_S3_Actions" + } + ] +} \ No newline at end of file diff --git a/portal-ui/tests/scripts/cleanup-env.sh b/portal-ui/tests/scripts/cleanup-env.sh index 19bfbd41f..7d348b1fb 100644 --- a/portal-ui/tests/scripts/cleanup-env.sh +++ b/portal-ui/tests/scripts/cleanup-env.sh @@ -23,6 +23,8 @@ remove_users() { mc admin user remove minio trace-$TIMESTAMP mc admin user remove minio users-$TIMESTAMP mc admin user remove minio watch-$TIMESTAMP + mc admin user remove minio inspect-allowed-$TIMESTAMP + mc admin user remove minio inspect-not-allowed-$TIMESTAMP } remove_policies() { @@ -41,6 +43,8 @@ remove_policies() { mc admin policy remove minio trace-$TIMESTAMP mc admin policy remove minio users-$TIMESTAMP mc admin policy remove minio watch-$TIMESTAMP + mc admin policy remove minio inspect-allowed-$TIMESTAMP + mc admin policy remove minio inspect-not-allowed-$TIMESTAMP } __init__() { diff --git a/portal-ui/tests/scripts/common.sh b/portal-ui/tests/scripts/common.sh index 0cbde43f4..8f44b2dcf 100644 --- a/portal-ui/tests/scripts/common.sh +++ b/portal-ui/tests/scripts/common.sh @@ -40,6 +40,8 @@ create_policies() { mc admin policy add minio users-$TIMESTAMP portal-ui/tests/policies/users.json mc admin policy add minio watch-$TIMESTAMP portal-ui/tests/policies/watch.json mc admin policy add minio bucketwriteprefixonlypolicy-$TIMESTAMP portal-ui/tests/policies/bucketWritePrefixOnlyPolicy.json + mc admin policy add minio inspect-allowed-$TIMESTAMP portal-ui/tests/policies/inspect-allowed.json + mc admin policy add minio inspect-not-allowed-$TIMESTAMP portal-ui/tests/policies/inspect-not-allowed.json } create_users() { @@ -60,6 +62,8 @@ create_users() { mc admin user add minio users-$TIMESTAMP users1234 mc admin user add minio watch-$TIMESTAMP watch1234 mc admin user add minio bucketwriteprefixonlypolicy-$TIMESTAMP bucketwriteprefixonlypolicy + mc admin user add minio inspect-allowed-$TIMESTAMP insallowed1234 + mc admin user add minio inspect-not-allowed-$TIMESTAMP insnotallowed1234 } create_buckets() { @@ -84,4 +88,6 @@ assign_policies() { mc admin policy set minio users-$TIMESTAMP user=users-$TIMESTAMP mc admin policy set minio watch-$TIMESTAMP user=watch-$TIMESTAMP mc admin policy set minio bucketwriteprefixonlypolicy-$TIMESTAMP user=bucketwriteprefixonlypolicy-$TIMESTAMP + mc admin policy set minio inspect-allowed-$TIMESTAMP user=inspect-allowed-$TIMESTAMP + mc admin policy set minio inspect-not-allowed-$TIMESTAMP user=inspect-not-allowed-$TIMESTAMP } \ No newline at end of file diff --git a/portal-ui/tests/scripts/permissions.sh b/portal-ui/tests/scripts/permissions.sh index 82e094ae9..2dac135d7 100644 --- a/portal-ui/tests/scripts/permissions.sh +++ b/portal-ui/tests/scripts/permissions.sh @@ -32,6 +32,8 @@ remove_users() { mc admin user remove minio users-$TIMESTAMP mc admin user remove minio watch-$TIMESTAMP mc admin user remove minio bucketwriteprefixonlypolicy-$TIMESTAMP + mc admin user remove minio inspect-allowed-$TIMESTAMP + mc admin user remove minio inspect-not-allowed-$TIMESTAMP } remove_policies() { @@ -51,6 +53,8 @@ remove_policies() { mc admin policy remove minio users-$TIMESTAMP mc admin policy remove minio watch-$TIMESTAMP mc admin policy remove minio bucketwriteprefixonlypolicy-$TIMESTAMP + mc admin policy remove minio inspect-allowed-$TIMESTAMP + mc admin policy remove minio inspect-not-allowed-$TIMESTAMP } remove_buckets() {