github.com/minio/console@v1.4.1/web-app/src/screens/Console/Policies/ListPolicies.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 { 19 AddIcon, 20 Button, 21 DataTable, 22 Grid, 23 HelpBox, 24 IAMPoliciesIcon, 25 PageLayout, 26 } from "mds"; 27 import { useNavigate } from "react-router-dom"; 28 import { actionsTray } from "../Common/FormComponents/common/styleLibrary"; 29 import { ErrorResponseHandler } from "../../../common/types"; 30 import { 31 CONSOLE_UI_RESOURCE, 32 createPolicyPermissions, 33 deletePolicyPermissions, 34 IAM_PAGES, 35 IAM_SCOPES, 36 listPolicyPermissions, 37 permissionTooltipHelper, 38 viewPolicyPermissions, 39 } from "../../../common/SecureComponent/permissions"; 40 import { 41 hasPermission, 42 SecureComponent, 43 } from "../../../common/SecureComponent"; 44 import { Policy } from "../../../api/consoleApi"; 45 import { encodeURLString } from "../../../common/utils"; 46 import { setErrorSnackMessage, setHelpName } from "../../../systemSlice"; 47 import { useAppDispatch } from "../../../store"; 48 import { api } from "../../../api"; 49 import SearchBox from "../Common/SearchBox"; 50 import withSuspense from "../Common/Components/withSuspense"; 51 import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper"; 52 import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper"; 53 import HelpMenu from "../HelpMenu"; 54 55 const DeletePolicy = withSuspense(React.lazy(() => import("./DeletePolicy"))); 56 57 const ListPolicies = () => { 58 const dispatch = useAppDispatch(); 59 const navigate = useNavigate(); 60 61 const [records, setRecords] = useState<Policy[]>([]); 62 const [loading, setLoading] = useState<boolean>(false); 63 const [deleteOpen, setDeleteOpen] = useState<boolean>(false); 64 const [selectedPolicy, setSelectedPolicy] = useState<string>(""); 65 const [filterPolicies, setFilterPolicies] = useState<string>(""); 66 const viewPolicy = hasPermission(CONSOLE_UI_RESOURCE, [ 67 IAM_SCOPES.ADMIN_GET_POLICY, 68 ]); 69 70 const canDeletePolicy = hasPermission( 71 CONSOLE_UI_RESOURCE, 72 deletePolicyPermissions, 73 ); 74 75 const canDisplayPolicies = hasPermission( 76 CONSOLE_UI_RESOURCE, 77 listPolicyPermissions, 78 ); 79 80 const canCreatePolicy = hasPermission( 81 CONSOLE_UI_RESOURCE, 82 createPolicyPermissions, 83 ); 84 85 const canViewPolicy = hasPermission( 86 CONSOLE_UI_RESOURCE, 87 viewPolicyPermissions, 88 ); 89 90 useEffect(() => { 91 fetchRecords(); 92 }, []); 93 94 useEffect(() => { 95 if (loading) { 96 if (canDisplayPolicies) { 97 api.policies 98 .listPolicies() 99 .then((res) => { 100 const policies = res.data.policies ?? []; 101 102 policies.sort((pa, pb) => { 103 if (pa.name! > pb.name!) { 104 return 1; 105 } 106 107 if (pa.name! < pb.name!) { 108 return -1; 109 } 110 111 return 0; 112 }); 113 114 setLoading(false); 115 setRecords(policies); 116 }) 117 .catch((err: ErrorResponseHandler) => { 118 setLoading(false); 119 dispatch(setErrorSnackMessage(err)); 120 }); 121 } else { 122 setLoading(false); 123 } 124 } 125 }, [loading, setLoading, setRecords, dispatch, canDisplayPolicies]); 126 127 const fetchRecords = () => { 128 setLoading(true); 129 }; 130 131 const closeDeleteModalAndRefresh = (refresh: boolean) => { 132 setDeleteOpen(false); 133 134 if (refresh) { 135 fetchRecords(); 136 } 137 }; 138 139 const confirmDeletePolicy = (policy: string) => { 140 setDeleteOpen(true); 141 setSelectedPolicy(policy); 142 }; 143 144 const viewAction = (policy: any) => { 145 navigate(`${IAM_PAGES.POLICIES}/${encodeURLString(policy.name)}`); 146 }; 147 148 const tableActions = [ 149 { 150 type: "view", 151 onClick: viewAction, 152 disableButtonFunction: () => !viewPolicy, 153 }, 154 { 155 type: "delete", 156 onClick: confirmDeletePolicy, 157 sendOnlyId: true, 158 disableButtonFunction: () => !canDeletePolicy, 159 }, 160 ]; 161 162 const filteredRecords = records.filter((elementItem) => 163 elementItem.name?.includes(filterPolicies), 164 ); 165 166 useEffect(() => { 167 dispatch(setHelpName("list_policies")); 168 // eslint-disable-next-line react-hooks/exhaustive-deps 169 }, []); 170 171 return ( 172 <Fragment> 173 {deleteOpen && ( 174 <DeletePolicy 175 deleteOpen={deleteOpen} 176 selectedPolicy={selectedPolicy} 177 closeDeleteModalAndRefresh={closeDeleteModalAndRefresh} 178 /> 179 )} 180 <PageHeaderWrapper label="IAM Policies" actions={<HelpMenu />} /> 181 182 <PageLayout> 183 <Grid container> 184 <Grid item xs={12} sx={actionsTray.actionsTray}> 185 <SearchBox 186 onChange={setFilterPolicies} 187 placeholder="Search Policies" 188 value={filterPolicies} 189 sx={{ maxWidth: 380 }} 190 /> 191 192 <SecureComponent 193 scopes={[IAM_SCOPES.ADMIN_CREATE_POLICY]} 194 resource={CONSOLE_UI_RESOURCE} 195 errorProps={{ disabled: true }} 196 > 197 <TooltipWrapper 198 tooltip={ 199 canCreatePolicy 200 ? "" 201 : permissionTooltipHelper( 202 createPolicyPermissions, 203 "create a Policy", 204 ) 205 } 206 > 207 <Button 208 id={"create-policy"} 209 label={"Create Policy"} 210 variant="callAction" 211 icon={<AddIcon />} 212 onClick={() => { 213 navigate(`${IAM_PAGES.POLICY_ADD}`); 214 }} 215 disabled={!canCreatePolicy} 216 /> 217 </TooltipWrapper> 218 </SecureComponent> 219 </Grid> 220 <Grid item xs={12}> 221 <SecureComponent 222 scopes={[IAM_SCOPES.ADMIN_LIST_USER_POLICIES]} 223 resource={CONSOLE_UI_RESOURCE} 224 errorProps={{ disabled: true }} 225 > 226 <TooltipWrapper 227 tooltip={ 228 canViewPolicy 229 ? "" 230 : permissionTooltipHelper( 231 viewPolicyPermissions, 232 "view Policy details", 233 ) 234 } 235 > 236 <DataTable 237 itemActions={tableActions} 238 columns={[{ label: "Name", elementKey: "name" }]} 239 isLoading={loading} 240 records={filteredRecords} 241 entityName="Policies" 242 idField="name" 243 /> 244 </TooltipWrapper> 245 </SecureComponent> 246 </Grid> 247 <Grid item xs={12} sx={{ marginTop: 15 }}> 248 <HelpBox 249 title={"Learn more about IAM POLICIES"} 250 iconComponent={<IAMPoliciesIcon />} 251 help={ 252 <Fragment> 253 MinIO uses Policy-Based Access Control (PBAC) to define the 254 authorized actions and resources to which an authenticated 255 user has access. Each policy describes one or more actions and 256 conditions that outline the permissions of a user or group of 257 users. 258 <br /> 259 <br /> 260 MinIO PBAC is built for compatibility with AWS IAM policy 261 syntax, structure, and behavior. The MinIO documentation makes 262 a best-effort to cover IAM-specific behavior and 263 functionality. Consider deferring to the IAM documentation for 264 more complete documentation on AWS IAM-specific topics. 265 <br /> 266 <br /> 267 You can learn more at our{" "} 268 <a 269 href="https://min.io/docs/minio/linux/administration/identity-access-management.html?ref=con#access-management" 270 target="_blank" 271 rel="noopener" 272 > 273 documentation 274 </a> 275 . 276 </Fragment> 277 } 278 /> 279 </Grid> 280 </Grid> 281 </PageLayout> 282 </Fragment> 283 ); 284 }; 285 286 export default ListPolicies;