github.com/minio/console@v1.4.1/web-app/src/screens/Console/IDP/LDAP/LDAPEntitiesQuery.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2023 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, useState } from "react"; 18 import { 19 AddIcon, 20 Box, 21 Button, 22 Grid, 23 InputBox, 24 Loader, 25 RemoveIcon, 26 SearchIcon, 27 SectionTitle, 28 TimeIcon, 29 } from "mds"; 30 import { useSelector } from "react-redux"; 31 import { DateTime } from "luxon"; 32 import { api } from "api"; 33 import { errorToHandler } from "api/errors"; 34 import { LdapEntities } from "api/consoleApi"; 35 import { setErrorSnackMessage } from "../../../../systemSlice"; 36 import { AppState, useAppDispatch } from "../../../../store"; 37 import LDAPResultsBlock from "./LDAPResultsBlock"; 38 import PolicySelectors from "../../Policies/PolicySelectors"; 39 40 const LDAPEntitiesQuery = () => { 41 const dispatch = useAppDispatch(); 42 43 const [loading, setLoading] = useState<boolean>(false); 44 const [users, setUsers] = useState<string[]>([""]); 45 const [groups, setGroups] = useState<string[]>([""]); 46 const [results, setResults] = useState<LdapEntities | null>(null); 47 48 const selectedPolicies = useSelector( 49 (state: AppState) => state.createUser.selectedPolicies, 50 ); 51 52 const searchEntities = () => { 53 setLoading(true); 54 55 let data: any = {}; 56 57 let cleanPolicies = selectedPolicies.filter((pol) => pol !== ""); 58 let cleanUsers = users.filter((usr) => usr !== ""); 59 let cleanGroups = groups.filter((grp) => grp !== ""); 60 61 if (cleanPolicies.length > 0) { 62 data["policies"] = cleanPolicies; 63 } 64 65 if (cleanUsers.length > 0) { 66 data["users"] = cleanUsers; 67 } 68 69 if (cleanGroups.length > 0) { 70 data["groups"] = cleanGroups; 71 } 72 73 api.ldapEntities 74 .getLdapEntities(data) 75 .then((result) => { 76 setResults(result.data); 77 setLoading(false); 78 }) 79 .catch((err) => { 80 dispatch(setErrorSnackMessage(errorToHandler(err.error))); 81 setLoading(false); 82 }); 83 }; 84 85 const alterUsersList = (addItem: boolean, index: number) => { 86 if (addItem) { 87 const alterUsers = [...users, ""]; 88 setUsers(alterUsers); 89 90 return; 91 } 92 93 const filteredUsers = users.filter((_, indx) => indx !== index); 94 95 setUsers(filteredUsers); 96 }; 97 98 const alterGroupsList = (addItem: boolean, index: number) => { 99 if (addItem) { 100 const alterGroups = [...groups, ""]; 101 setGroups(alterGroups); 102 103 return; 104 } 105 106 const filteredGroups = groups.filter((_, indx) => indx !== index); 107 108 setGroups(filteredGroups); 109 }; 110 111 return ( 112 <Box sx={{ marginTop: 15, paddingTop: 0 }}> 113 <Grid container sx={{ marginTop: 5 }}> 114 <Grid item sm={12} md={6} lg={5} sx={{ padding: 10, paddingTop: 0 }}> 115 <SectionTitle>Query Filters</SectionTitle> 116 117 <Box 118 sx={{ 119 padding: "0 10px", 120 display: "flex", 121 flexDirection: "column", 122 gap: 40, 123 }} 124 > 125 <Box sx={{ padding: "10px 26px" }} withBorders> 126 <Box sx={{ display: "flex" }}> 127 <h4 style={{ margin: 0, marginBottom: 10, fontSize: 14 }}> 128 Users 129 </h4> 130 </Box> 131 <Box 132 sx={{ 133 overflowY: "auto", 134 minHeight: 50, 135 maxHeight: 250, 136 "& > div > div": { 137 width: "100%", 138 }, 139 }} 140 > 141 {users.map((userDat, index) => { 142 return ( 143 <InputBox 144 id={`search-user-${index}`} 145 key={`search-user-${index}`} 146 value={userDat} 147 onChange={(e: React.ChangeEvent<HTMLInputElement>) => { 148 const usersElements = [...users]; 149 usersElements[index] = e.target.value; 150 setUsers(usersElements); 151 }} 152 overlayIcon={ 153 users.length === index + 1 ? ( 154 <AddIcon /> 155 ) : ( 156 <RemoveIcon /> 157 ) 158 } 159 overlayAction={() => { 160 alterUsersList(users.length === index + 1, index); 161 }} 162 /> 163 ); 164 })} 165 </Box> 166 </Box> 167 <Box sx={{ padding: "10px 26px" }} withBorders> 168 <h4 style={{ margin: 0, marginBottom: 10, fontSize: 14 }}> 169 Groups 170 </h4> 171 <Box 172 sx={{ 173 overflowY: "auto", 174 minHeight: 50, 175 maxHeight: "calc(100vh - 340px)", 176 "& > div > div": { 177 width: "100%", 178 }, 179 }} 180 > 181 {groups.map((groupDat, index) => { 182 return ( 183 <InputBox 184 id={`search-group-${index}`} 185 key={`search-group-${index}`} 186 value={groupDat} 187 onChange={(e: React.ChangeEvent<HTMLInputElement>) => { 188 const groupsElements = [...groups]; 189 groupsElements[index] = e.target.value; 190 setGroups(groupsElements); 191 }} 192 overlayIcon={ 193 groups.length === index + 1 ? ( 194 <AddIcon /> 195 ) : ( 196 <RemoveIcon /> 197 ) 198 } 199 overlayAction={() => { 200 alterGroupsList(groups.length === index + 1, index); 201 }} 202 /> 203 ); 204 })} 205 </Box> 206 </Box> 207 <Box sx={{ padding: "10px 26px" }} withBorders> 208 <h4 style={{ margin: 0, marginBottom: 10, fontSize: 14 }}> 209 Policies 210 </h4> 211 <Box 212 sx={{ 213 minHeight: 265, 214 maxHeight: "calc(100vh - 740px)", 215 }} 216 > 217 <PolicySelectors selectedPolicy={selectedPolicies} noTitle /> 218 </Box> 219 </Box> 220 </Box> 221 </Grid> 222 <Grid 223 item 224 sm={12} 225 md={6} 226 lg={7} 227 sx={{ 228 padding: 10, 229 paddingTop: 0, 230 display: "flex", 231 flexDirection: "column", 232 }} 233 > 234 {loading ? ( 235 <Box sx={{ textAlign: "center" }}> 236 <Loader /> 237 </Box> 238 ) : ( 239 <Fragment> 240 <SectionTitle 241 actions={ 242 <Box 243 sx={{ 244 display: "flex", 245 flexDirection: "row", 246 alignItems: "center", 247 fontSize: 14, 248 }} 249 > 250 {results?.timestamp ? ( 251 <Fragment> 252 <TimeIcon 253 style={{ 254 width: 14, 255 height: 14, 256 marginRight: 5, 257 fill: "#BEBFBF", 258 }} 259 /> 260 {DateTime.fromISO(results.timestamp).toFormat( 261 "D HH:mm:ss", 262 )} 263 </Fragment> 264 ) : ( 265 "" 266 )} 267 </Box> 268 } 269 > 270 Query Results 271 </SectionTitle> 272 {results ? ( 273 <Box 274 sx={{ 275 backgroundColor: "#FBFAFA", 276 padding: "8px 22px", 277 flexGrow: 1, 278 overflowY: "auto", 279 }} 280 > 281 {!results.groups && !results.users && !results.policies && ( 282 <Box sx={{ textAlign: "center" }}> 283 <h4>No Results Available</h4> 284 </Box> 285 )} 286 {!!results.groups && ( 287 <LDAPResultsBlock results={results} entityName={"Group"} /> 288 )} 289 {!!results.users && ( 290 <LDAPResultsBlock results={results} entityName={"User"} /> 291 )} 292 {!!results.policies && ( 293 <LDAPResultsBlock results={results} entityName={"Policy"} /> 294 )} 295 </Box> 296 ) : ( 297 <Box sx={{ textAlign: "center" }}>No query results yet</Box> 298 )} 299 </Fragment> 300 )} 301 </Grid> 302 </Grid> 303 <Grid container> 304 <Grid 305 item 306 xs={12} 307 sx={{ 308 display: "flex", 309 justifyContent: "flex-start", 310 marginTop: 45, 311 padding: "0 20px", 312 }} 313 > 314 <Button 315 id={"search-entity"} 316 type={"button"} 317 variant={"callAction"} 318 onClick={searchEntities} 319 icon={<SearchIcon />} 320 > 321 Search 322 </Button> 323 </Grid> 324 </Grid> 325 </Box> 326 ); 327 }; 328 329 export default LDAPEntitiesQuery;