From 60d70e36689b71997c1b57dc6ce905d0ce75a016 Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Thu, 23 Mar 2023 12:05:16 -0600 Subject: [PATCH] Added anonymous access modal in folder selection (#2736) Signed-off-by: Benjamin Perez --- portal-ui/package.json | 2 +- .../Buckets/BucketDetails/AddAccessRule.tsx | 18 ++++++-- .../BucketDetails/DeleteAccessRule.tsx | 2 +- .../Buckets/BucketDetails/EditAccessRule.tsx | 2 +- .../Objects/ListObjects/ListObjects.tsx | 41 +++++++++++++++++++ .../ObjectBrowser/objectBrowserSlice.ts | 5 +++ .../ObjectBrowser/objectBrowserThunks.ts | 15 +++++++ .../screens/Console/ObjectBrowser/types.ts | 1 + portal-ui/yarn.lock | 20 ++++----- 9 files changed, 90 insertions(+), 16 deletions(-) diff --git a/portal-ui/package.json b/portal-ui/package.json index c429e8b2a..27ff043bf 100644 --- a/portal-ui/package.json +++ b/portal-ui/package.json @@ -18,7 +18,7 @@ "local-storage-fallback": "^4.1.1", "lodash": "^4.17.21", "luxon": "^3.3.0", - "mds": "https://github.com/minio/mds.git#v0.3.1", + "mds": "https://github.com/minio/mds.git#v0.3.2", "minio": "^7.0.32", "react": "^18.1.0", "react-component-export-image": "^1.0.6", diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddAccessRule.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddAccessRule.tsx index b8e780790..2d8649cb5 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddAccessRule.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddAccessRule.tsx @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; import { Grid } from "@mui/material"; import { AddAccessRuleIcon, Button } from "mds"; @@ -30,7 +30,10 @@ import { import api from "../../../../common/api"; import { ErrorResponseHandler } from "../../../../common/types"; import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper"; -import { setErrorSnackMessage } from "../../../../systemSlice"; +import { + setErrorSnackMessage, + setSnackBarMessage, +} from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; interface IAddAccessRule { @@ -38,6 +41,7 @@ interface IAddAccessRule { modalOpen: boolean; onClose: () => any; bucket: string; + prefilledRoute?: string; } const styles = (theme: Theme) => @@ -51,12 +55,19 @@ const AddAccessRule = ({ onClose, classes, bucket, + prefilledRoute, }: IAddAccessRule) => { const dispatch = useAppDispatch(); const [prefix, setPrefix] = useState(""); const [selectedAccess, setSelectedAccess] = useState("readonly"); + useEffect(() => { + if (prefilledRoute) { + setPrefix(prefilledRoute); + } + }, [prefilledRoute]); + const accessOptions = [ { label: "readonly", value: "readonly" }, { label: "writeonly", value: "writeonly" }, @@ -75,6 +86,7 @@ const AddAccessRule = ({ access: selectedAccess, }) .then((res: any) => { + dispatch(setSnackBarMessage("Access Rule added successfully")); onClose(); }) .catch((err: ErrorResponseHandler) => { @@ -86,7 +98,7 @@ const AddAccessRule = ({ return ( } > diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/DeleteAccessRule.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/DeleteAccessRule.tsx index 9f82971c3..b27261078 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/DeleteAccessRule.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/DeleteAccessRule.tsx @@ -61,7 +61,7 @@ const DeleteAccessRule = ({ return ( } > diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx index ec098490b..f691e1ce9 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx @@ -28,6 +28,7 @@ import { useDropzone } from "react-dropzone"; import { Theme } from "@mui/material/styles"; import { CSSObject } from "styled-components"; import { + AccessRuleIcon, BucketsIcon, Button, DeleteIcon, @@ -103,6 +104,7 @@ import { openList, resetMessages, resetRewind, + setAnonymousAccessOpen, setDownloadRenameModal, setLoadingObjects, setLoadingRecords, @@ -133,11 +135,13 @@ import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper"; import ListObjectsTable from "./ListObjectsTable"; import { downloadSelected, + openAnonymousAccess, openPreview, openShare, } from "../../../../ObjectBrowser/objectBrowserThunks"; import FilterObjectsSB from "../../../../ObjectBrowser/FilterObjectsSB"; +import AddAccessRule from "../../../BucketDetails/AddAccessRule"; const DeleteMultipleObjects = withSuspense( React.lazy(() => import("./DeleteMultipleObjects")) @@ -280,6 +284,9 @@ const ListObjects = () => { const colorVariants = useSelector( (state: AppState) => state.system.overrideStyles ); + const anonymousAccessOpen = useSelector( + (state: AppState) => state.objectBrowser.anonymousAccessOpen + ); const loadingBucket = useSelector(selBucketDetailsLoading); const bucketInfo = useSelector(selBucketDetailsInfo); @@ -323,6 +330,13 @@ const ListObjects = () => { const displayDeleteObject = hasPermission(bucketName, [ IAM_SCOPES.S3_DELETE_OBJECT, ]); + const canSetAnonymousAccess = hasPermission(bucketName, [ + IAM_SCOPES.S3_GET_BUCKET_POLICY, + IAM_SCOPES.S3_PUT_BUCKET_POLICY, + IAM_SCOPES.S3_GET_ACTIONS, + IAM_SCOPES.S3_PUT_ACTIONS, + ]); + const selectedObjects = useSelector( (state: AppState) => state.objectBrowser.selectedObjects ); @@ -782,6 +796,10 @@ const ListObjects = () => { dispatch(setDownloadRenameModal(null)); }; + const closeAddAccessRule = () => { + dispatch(setAnonymousAccessOpen(false)); + }; + let createdTime = DateTime.now(); if (bucketInfo?.creation_date) { @@ -855,6 +873,21 @@ const ListObjects = () => { icon: , tooltip: canPreviewFile ? "Preview Selected File" : "Preview unavailable", }, + { + action: () => { + dispatch(openAnonymousAccess()); + }, + label: "Anonymous Access", + disabled: + selectedObjects.length !== 1 || + !selectedObjects[0].endsWith("/") || + !canSetAnonymousAccess, + icon: , + tooltip: + selectedObjects.length === 1 && selectedObjects[0].endsWith("/") + ? "Set Anonymous Access to this Folder" + : "Anonymous Access unavailable", + }, { action: () => { setDeleteMultipleOpen(true); @@ -925,6 +958,14 @@ const ListObjects = () => { }} /> )} + {anonymousAccessOpen && ( + + )} {anonymousMode && ( diff --git a/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserSlice.ts b/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserSlice.ts index ae7e97e9a..9851ea82a 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserSlice.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserSlice.ts @@ -67,6 +67,7 @@ const initialState: ObjectBrowserState = { previewOpen: false, shareFileModalOpen: false, isOpeningObjectDetail: false, + anonymousAccessOpen: false, retentionConfig: { mode: "", unit: "", @@ -361,6 +362,9 @@ export const objectBrowserSlice = createSlice({ setLongFileOpen: (state, action: PayloadAction) => { state.longFileOpen = action.payload; }, + setAnonymousAccessOpen: (state, action: PayloadAction) => { + state.anonymousAccessOpen = action.payload; + }, }, }); export const { @@ -407,6 +411,7 @@ export const { setRetentionConfig, setSelectedBucket, setLongFileOpen, + setAnonymousAccessOpen, } = objectBrowserSlice.actions; export default objectBrowserSlice.reducer; diff --git a/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserThunks.ts b/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserThunks.ts index b5367cc5d..ea7458cde 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserThunks.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/objectBrowserThunks.ts @@ -24,6 +24,7 @@ import { cancelObjectInList, completeObject, failObject, + setAnonymousAccessOpen, setDownloadRenameModal, setNewObject, setPreviewOpen, @@ -155,3 +156,17 @@ export const openShare = createAsyncThunk( } } ); + +export const openAnonymousAccess = createAsyncThunk( + "objectBrowser/openAnonymousAccess", + async (_, { getState, dispatch }) => { + const state = getState() as AppState; + + if ( + state.objectBrowser.selectedObjects.length === 1 && + state.objectBrowser.selectedObjects[0].endsWith("/") + ) { + dispatch(setAnonymousAccessOpen(true)); + } + } +); diff --git a/portal-ui/src/screens/Console/ObjectBrowser/types.ts b/portal-ui/src/screens/Console/ObjectBrowser/types.ts index b1ebf82aa..7f11f23f1 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/types.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/types.ts @@ -95,6 +95,7 @@ export interface ObjectBrowserState { isOpeningObjectDetail: boolean; retentionConfig: IRetentionConfig | null; longFileOpen: boolean; + anonymousAccessOpen: boolean; } export interface ObjectManager { diff --git a/portal-ui/yarn.lock b/portal-ui/yarn.lock index 491cc5ac6..5317145cb 100644 --- a/portal-ui/yarn.lock +++ b/portal-ui/yarn.lock @@ -4877,10 +4877,10 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-gpu@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.15.tgz#d375f89b246f21167859efbbd89eff60ccbcfd4d" - integrity sha512-ImImgPRhTvo/bmtotR1KG534ZE+L6yIV9sMe4y3zOUzeInnYk/9KOBqxz9M5hKkHXhGKKjY04pMrLMb+rp3FWw== +detect-gpu@^5.0.16: + version "5.0.16" + resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.16.tgz#a42054724f4a75d667add68ad1073c80d29ef733" + integrity sha512-6+o6Sy+FzgQJG7Ray0fN7B4kRGGPuyaM5FHXJ4N3sLcQhsUO9+NEw9emM7vxN7DroZGG16ZydCX6kpgDmNXyKQ== dependencies: webgl-constants "^1.1.1" @@ -8316,14 +8316,14 @@ mdn-data@2.0.4: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== -"mds@https://github.com/minio/mds.git#v0.3.1": - version "0.3.1" - resolved "https://github.com/minio/mds.git#8beec5b36aef3ba4c389b170a0108f301a5f29e3" +"mds@https://github.com/minio/mds.git#v0.3.2": + version "0.3.2" + resolved "https://github.com/minio/mds.git#85654388adaec4d2624b61bee439c7d9cd5801f7" dependencies: "@types/styled-components" "^5.1.25" - detect-gpu "^5.0.15" + detect-gpu "^5.0.16" react-virtualized "^9.22.3" - styled-components "^5.3.8" + styled-components "^5.3.9" media-typer@0.3.0: version "0.3.0" @@ -11212,7 +11212,7 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899" integrity sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw== -styled-components@^5.3.8, styled-components@^5.3.9: +styled-components@^5.3.9: version "5.3.9" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.9.tgz#641af2a8bb89904de708c71b439caa9633e8f0ba" integrity sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==