github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/AccessRulePanel.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 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, useEffect, useState } from "react"; 18 import { AddIcon, Button, DataTable, SectionTitle, HelpTip } from "mds"; 19 import { useSelector } from "react-redux"; 20 import { useParams } from "react-router-dom"; 21 import { api } from "api"; 22 import { AccessRule as IAccessRule } from "api/consoleApi"; 23 import { errorToHandler } from "api/errors"; 24 import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions"; 25 import { 26 hasPermission, 27 SecureComponent, 28 } from "../../../../common/SecureComponent"; 29 import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice"; 30 import { selBucketDetailsLoading } from "./bucketDetailsSlice"; 31 import { useAppDispatch } from "../../../../store"; 32 import withSuspense from "../../Common/Components/withSuspense"; 33 import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; 34 35 const AddAccessRuleModal = withSuspense( 36 React.lazy(() => import("./AddAccessRule")), 37 ); 38 const DeleteAccessRuleModal = withSuspense( 39 React.lazy(() => import("./DeleteAccessRule")), 40 ); 41 const EditAccessRuleModal = withSuspense( 42 React.lazy(() => import("./EditAccessRule")), 43 ); 44 45 const AccessRule = () => { 46 const dispatch = useAppDispatch(); 47 const params = useParams(); 48 49 const loadingBucket = useSelector(selBucketDetailsLoading); 50 51 const [loadingAccessRules, setLoadingAccessRules] = useState<boolean>(true); 52 const [accessRules, setAccessRules] = useState<IAccessRule[] | undefined>([]); 53 const [addAccessRuleOpen, setAddAccessRuleOpen] = useState<boolean>(false); 54 const [deleteAccessRuleOpen, setDeleteAccessRuleOpen] = 55 useState<boolean>(false); 56 const [accessRuleToDelete, setAccessRuleToDelete] = useState<string>(""); 57 const [editAccessRuleOpen, setEditAccessRuleOpen] = useState<boolean>(false); 58 const [accessRuleToEdit, setAccessRuleToEdit] = useState<string>(""); 59 const [initialAccess, setInitialAccess] = useState<string>(""); 60 61 const bucketName = params.bucketName || ""; 62 63 const displayAccessRules = hasPermission(bucketName, [ 64 IAM_SCOPES.S3_GET_BUCKET_POLICY, 65 IAM_SCOPES.S3_GET_ACTIONS, 66 ]); 67 68 const deleteAccessRules = hasPermission(bucketName, [ 69 IAM_SCOPES.S3_DELETE_BUCKET_POLICY, 70 ]); 71 72 const editAccessRules = hasPermission(bucketName, [ 73 IAM_SCOPES.S3_PUT_BUCKET_POLICY, 74 IAM_SCOPES.S3_PUT_ACTIONS, 75 ]); 76 77 useEffect(() => { 78 if (loadingBucket) { 79 setLoadingAccessRules(true); 80 } 81 }, [loadingBucket, setLoadingAccessRules]); 82 83 const AccessRuleActions = [ 84 { 85 type: "delete", 86 disableButtonFunction: () => !deleteAccessRules, 87 onClick: (accessRule: any) => { 88 setDeleteAccessRuleOpen(true); 89 setAccessRuleToDelete(accessRule.prefix); 90 }, 91 }, 92 { 93 type: "view", 94 disableButtonFunction: () => !editAccessRules, 95 onClick: (accessRule: any) => { 96 setAccessRuleToEdit(accessRule.prefix); 97 setInitialAccess(accessRule.access); 98 setEditAccessRuleOpen(true); 99 }, 100 }, 101 ]; 102 103 useEffect(() => { 104 dispatch(setHelpName("bucket_detail_prefix")); 105 // eslint-disable-next-line react-hooks/exhaustive-deps 106 }, []); 107 108 useEffect(() => { 109 if (loadingAccessRules) { 110 if (displayAccessRules) { 111 api.bucket 112 .listAccessRulesWithBucket(bucketName) 113 .then((res) => { 114 setAccessRules(res.data.accessRules); 115 setLoadingAccessRules(false); 116 }) 117 .catch((err) => { 118 dispatch(setErrorSnackMessage(errorToHandler(err))); 119 setLoadingAccessRules(false); 120 }); 121 } else { 122 setLoadingAccessRules(false); 123 } 124 } 125 }, [loadingAccessRules, dispatch, displayAccessRules, bucketName]); 126 127 const closeAddAccessRuleModal = () => { 128 setAddAccessRuleOpen(false); 129 setLoadingAccessRules(true); 130 }; 131 132 const closeDeleteAccessRuleModal = () => { 133 setDeleteAccessRuleOpen(false); 134 setLoadingAccessRules(true); 135 }; 136 137 const closeEditAccessRuleModal = () => { 138 setEditAccessRuleOpen(false); 139 setLoadingAccessRules(true); 140 }; 141 142 return ( 143 <Fragment> 144 {addAccessRuleOpen && ( 145 <AddAccessRuleModal 146 modalOpen={addAccessRuleOpen} 147 onClose={closeAddAccessRuleModal} 148 bucket={bucketName} 149 /> 150 )} 151 {deleteAccessRuleOpen && ( 152 <DeleteAccessRuleModal 153 modalOpen={deleteAccessRuleOpen} 154 onClose={closeDeleteAccessRuleModal} 155 bucket={bucketName} 156 toDelete={accessRuleToDelete} 157 /> 158 )} 159 {editAccessRuleOpen && ( 160 <EditAccessRuleModal 161 modalOpen={editAccessRuleOpen} 162 onClose={closeEditAccessRuleModal} 163 bucket={bucketName} 164 toEdit={accessRuleToEdit} 165 initial={initialAccess} 166 /> 167 )} 168 <SectionTitle 169 separator 170 sx={{ marginBottom: 15 }} 171 actions={ 172 <SecureComponent 173 scopes={[ 174 IAM_SCOPES.S3_GET_BUCKET_POLICY, 175 IAM_SCOPES.S3_PUT_BUCKET_POLICY, 176 IAM_SCOPES.S3_GET_ACTIONS, 177 IAM_SCOPES.S3_PUT_ACTIONS, 178 ]} 179 resource={bucketName} 180 matchAll 181 errorProps={{ disabled: true }} 182 > 183 <TooltipWrapper tooltip={"Add Access Rule"}> 184 <Button 185 id={"add-bucket-access-rule"} 186 onClick={() => { 187 setAddAccessRuleOpen(true); 188 }} 189 label={"Add Access Rule"} 190 icon={<AddIcon />} 191 variant={"callAction"} 192 /> 193 </TooltipWrapper> 194 </SecureComponent> 195 } 196 > 197 <HelpTip 198 content={ 199 <Fragment> 200 Setting an{" "} 201 <a 202 href="https://min.io/docs/minio/linux/reference/minio-mc/mc-anonymous-set.html" 203 target="blank" 204 > 205 Anonymous 206 </a>{" "} 207 policy allows clients to access the Bucket or prefix contents and 208 perform actions consistent with the specified policy without 209 authentication. 210 </Fragment> 211 } 212 placement="right" 213 > 214 Anonymous Access 215 </HelpTip> 216 </SectionTitle> 217 <SecureComponent 218 scopes={[IAM_SCOPES.S3_GET_BUCKET_POLICY, IAM_SCOPES.S3_GET_ACTIONS]} 219 resource={bucketName} 220 errorProps={{ disabled: true }} 221 > 222 <DataTable 223 itemActions={AccessRuleActions} 224 columns={[ 225 { 226 label: "Prefix", 227 elementKey: "prefix", 228 renderFunction: (prefix: string) => { 229 return prefix || "/"; 230 }, 231 }, 232 { label: "Access", elementKey: "access" }, 233 ]} 234 isLoading={loadingAccessRules} 235 records={accessRules || []} 236 entityName="Access Rules" 237 idField="prefix" 238 /> 239 </SecureComponent> 240 </Fragment> 241 ); 242 }; 243 244 export default AccessRule;