github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/AccessDetailsPanel.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 { useSelector } from "react-redux"; 19 import { useNavigate, useParams } from "react-router-dom"; 20 import { DataTable, SectionTitle, Tabs, HelpTip } from "mds"; 21 import { api } from "api"; 22 import { errorToHandler } from "api/errors"; 23 import { 24 CONSOLE_UI_RESOURCE, 25 IAM_PAGES, 26 IAM_SCOPES, 27 } from "../../../../common/SecureComponent/permissions"; 28 import { 29 hasPermission, 30 SecureComponent, 31 } from "../../../../common/SecureComponent"; 32 import { encodeURLString } from "../../../../common/utils"; 33 import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice"; 34 import { selBucketDetailsLoading } from "./bucketDetailsSlice"; 35 import { useAppDispatch } from "../../../../store"; 36 import { Policy } from "../../../../api/consoleApi"; 37 38 const AccessDetails = () => { 39 const dispatch = useAppDispatch(); 40 const navigate = useNavigate(); 41 const params = useParams(); 42 43 const loadingBucket = useSelector(selBucketDetailsLoading); 44 45 const [curTab, setCurTab] = useState<string>("simple-tab-0"); 46 const [loadingPolicies, setLoadingPolicies] = useState<boolean>(true); 47 const [bucketPolicy, setBucketPolicy] = useState<Policy[] | undefined>([]); 48 const [loadingUsers, setLoadingUsers] = useState<boolean>(true); 49 const [bucketUsers, setBucketUsers] = useState<string[]>([]); 50 51 const bucketName = params.bucketName || ""; 52 53 const displayPoliciesList = hasPermission(bucketName, [ 54 IAM_SCOPES.ADMIN_LIST_USER_POLICIES, 55 ]); 56 57 const displayUsersList = hasPermission( 58 bucketName, 59 [ 60 IAM_SCOPES.ADMIN_GET_POLICY, 61 IAM_SCOPES.ADMIN_LIST_USERS, 62 IAM_SCOPES.ADMIN_LIST_GROUPS, 63 ], 64 true, 65 ); 66 67 const viewUser = hasPermission(CONSOLE_UI_RESOURCE, [ 68 IAM_SCOPES.ADMIN_GET_USER, 69 ]); 70 const viewPolicy = hasPermission(CONSOLE_UI_RESOURCE, [ 71 IAM_SCOPES.ADMIN_GET_POLICY, 72 IAM_SCOPES.ADMIN_LIST_USERS, 73 IAM_SCOPES.ADMIN_LIST_GROUPS, 74 ]); 75 76 useEffect(() => { 77 if (loadingBucket) { 78 setLoadingUsers(true); 79 setLoadingPolicies(true); 80 } 81 }, [loadingBucket, setLoadingUsers, setLoadingPolicies]); 82 83 const PolicyActions = [ 84 { 85 type: "view", 86 disableButtonFunction: () => !viewPolicy, 87 onClick: (policy: any) => { 88 navigate(`${IAM_PAGES.POLICIES}/${encodeURLString(policy.name)}`); 89 }, 90 }, 91 ]; 92 93 const userTableActions = [ 94 { 95 type: "view", 96 disableButtonFunction: () => !viewUser, 97 onClick: (user: any) => { 98 navigate(`${IAM_PAGES.USERS}/${encodeURLString(user)}`); 99 }, 100 }, 101 ]; 102 103 useEffect(() => { 104 if (loadingUsers) { 105 if (displayUsersList) { 106 api.bucketUsers 107 .listUsersWithAccessToBucket(bucketName) 108 .then((res) => { 109 setBucketUsers(res.data); 110 setLoadingUsers(false); 111 }) 112 .catch((err) => { 113 dispatch(setErrorSnackMessage(errorToHandler(err))); 114 setLoadingUsers(false); 115 }); 116 } else { 117 setLoadingUsers(false); 118 } 119 } 120 }, [loadingUsers, dispatch, bucketName, displayUsersList]); 121 122 useEffect(() => { 123 dispatch(setHelpName("bucket_detail_access")); 124 // eslint-disable-next-line react-hooks/exhaustive-deps 125 }, []); 126 127 useEffect(() => { 128 if (loadingPolicies) { 129 if (displayPoliciesList) { 130 api.bucketPolicy 131 .listPoliciesWithBucket(bucketName) 132 .then((res) => { 133 setBucketPolicy(res.data.policies); 134 setLoadingPolicies(false); 135 }) 136 .catch((err) => { 137 dispatch(setErrorSnackMessage(errorToHandler(err))); 138 setLoadingPolicies(false); 139 }); 140 } else { 141 setLoadingPolicies(false); 142 } 143 } 144 }, [loadingPolicies, dispatch, bucketName, displayPoliciesList]); 145 146 return ( 147 <Fragment> 148 <SectionTitle separator> 149 <HelpTip 150 content={ 151 <Fragment> 152 Understand which{" "} 153 <a 154 target="blank" 155 href="https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html#" 156 > 157 Policies 158 </a>{" "} 159 and{" "} 160 <a 161 target="blank" 162 href="https://min.io/docs/minio/linux/administration/identity-access-management/minio-user-management.html" 163 > 164 Users 165 </a>{" "} 166 are authorized to access this Bucket. 167 </Fragment> 168 } 169 placement="right" 170 > 171 Access Audit 172 </HelpTip> 173 </SectionTitle> 174 <Tabs 175 currentTabOrPath={curTab} 176 onTabClick={(newValue: string) => { 177 setCurTab(newValue); 178 }} 179 horizontal 180 options={[ 181 { 182 tabConfig: { label: "Policies", id: "simple-tab-0" }, 183 content: ( 184 <SecureComponent 185 scopes={[IAM_SCOPES.ADMIN_LIST_USER_POLICIES]} 186 resource={bucketName} 187 errorProps={{ disabled: true }} 188 > 189 {bucketPolicy && ( 190 <DataTable 191 noBackground={true} 192 itemActions={PolicyActions} 193 columns={[{ label: "Name", elementKey: "name" }]} 194 isLoading={loadingPolicies} 195 records={bucketPolicy} 196 entityName="Policies" 197 idField="name" 198 /> 199 )} 200 </SecureComponent> 201 ), 202 }, 203 { 204 tabConfig: { label: "Users", id: "simple-tab-1" }, 205 content: ( 206 <SecureComponent 207 scopes={[ 208 IAM_SCOPES.ADMIN_GET_POLICY, 209 IAM_SCOPES.ADMIN_LIST_USERS, 210 IAM_SCOPES.ADMIN_LIST_GROUPS, 211 ]} 212 resource={bucketName} 213 matchAll 214 errorProps={{ disabled: true }} 215 > 216 <DataTable 217 noBackground={true} 218 itemActions={userTableActions} 219 columns={[{ label: "User", elementKey: "accessKey" }]} 220 isLoading={loadingUsers} 221 records={bucketUsers} 222 entityName="Users" 223 idField="accessKey" 224 /> 225 </SecureComponent> 226 ), 227 }, 228 ]} 229 /> 230 </Fragment> 231 ); 232 }; 233 234 export default AccessDetails;