github.com/minio/console@v1.4.1/web-app/src/screens/LoginPage/StrategyForm.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 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 Box, 20 Button, 21 DropdownSelector, 22 Grid, 23 InputBox, 24 LockFilledIcon, 25 LogoutIcon, 26 PasswordKeyIcon, 27 ProgressBar, 28 Select, 29 UserFilledIcon, 30 } from "mds"; 31 import { 32 setAccessKey, 33 setDisplayEmbeddedIDPForms, 34 setSecretKey, 35 setSTS, 36 setUseSTS, 37 } from "./loginSlice"; 38 39 import { AppState, useAppDispatch } from "../../store"; 40 import { useSelector } from "react-redux"; 41 import { doLoginAsync } from "./loginThunks"; 42 import { RedirectRule } from "api/consoleApi"; 43 44 const StrategyForm = ({ redirectRules }: { redirectRules: RedirectRule[] }) => { 45 const dispatch = useAppDispatch(); 46 47 const [ssoOptionsOpen, ssoOptionsSetOpen] = useState<boolean>(false); 48 const [anchorEl, setAnchorEl] = React.useState< 49 (EventTarget & HTMLButtonElement) | null 50 >(null); 51 52 const accessKey = useSelector((state: AppState) => state.login.accessKey); 53 const secretKey = useSelector((state: AppState) => state.login.secretKey); 54 const sts = useSelector((state: AppState) => state.login.sts); 55 const useSTS = useSelector((state: AppState) => state.login.useSTS); 56 const displaySSOForm = useSelector( 57 (state: AppState) => state.login.ssoEmbeddedIDPDisplay, 58 ); 59 60 const loginSending = useSelector( 61 (state: AppState) => state.login.loginSending, 62 ); 63 64 const formSubmit = (e: React.FormEvent<HTMLFormElement>) => { 65 e.preventDefault(); 66 dispatch(doLoginAsync()); 67 }; 68 69 let selectOptions = [ 70 { 71 label: useSTS ? "Use Credentials" : "Use STS", 72 value: useSTS ? "use-sts-cred" : "use-sts", 73 }, 74 ]; 75 let ssoOptions: any[] = []; 76 77 if (redirectRules.length > 0) { 78 ssoOptions = redirectRules.map((r) => ({ 79 label: `${r.displayName}${r.serviceType ? ` - ${r.serviceType}` : ""}`, 80 value: r.redirect, 81 icon: <LogoutIcon />, 82 })); 83 84 selectOptions = [ 85 { label: "Use Credentials", value: "use-sts-cred" }, 86 { label: "Use STS", value: "use-sts" }, 87 ]; 88 } 89 90 const extraActionSelector = (value: string) => { 91 if (value) { 92 if (redirectRules.length > 0) { 93 let stsState = true; 94 95 if (value === "use-sts-cred") { 96 stsState = false; 97 } 98 99 dispatch(setUseSTS(stsState)); 100 dispatch(setDisplayEmbeddedIDPForms(true)); 101 102 return; 103 } 104 105 if (value.includes("use-sts")) { 106 dispatch(setUseSTS(!useSTS)); 107 return; 108 } 109 } 110 }; 111 112 const submitSSOInitRequest = (value: string) => { 113 window.location.href = value; 114 }; 115 116 return ( 117 <React.Fragment> 118 {redirectRules.length > 0 && ( 119 <Fragment> 120 <Box sx={{ marginBottom: 40 }}> 121 <Button 122 id={"SSOSelector"} 123 variant={"subAction"} 124 label={ 125 redirectRules.length === 1 126 ? `${redirectRules[0].displayName}${ 127 redirectRules[0].serviceType 128 ? ` - ${redirectRules[0].serviceType}` 129 : "" 130 }` 131 : `Login with SSO` 132 } 133 fullWidth 134 sx={{ height: 50 }} 135 onClick={(e) => { 136 if (redirectRules.length > 1) { 137 ssoOptionsSetOpen(!ssoOptionsOpen); 138 setAnchorEl(e.currentTarget); 139 return; 140 } 141 submitSSOInitRequest(`${redirectRules[0].redirect}`); 142 }} 143 /> 144 {redirectRules.length > 1 && ( 145 <DropdownSelector 146 id={"redirect-rules"} 147 options={ssoOptions} 148 selectedOption={""} 149 onSelect={(nValue) => submitSSOInitRequest(nValue)} 150 hideTriggerAction={() => { 151 ssoOptionsSetOpen(false); 152 }} 153 open={ssoOptionsOpen} 154 anchorEl={anchorEl} 155 useAnchorWidth={true} 156 /> 157 )} 158 </Box> 159 </Fragment> 160 )} 161 162 <form noValidate onSubmit={formSubmit} style={{ width: "100%" }}> 163 {((displaySSOForm && redirectRules.length > 0) || 164 redirectRules.length === 0) && ( 165 <Fragment> 166 <Grid 167 container 168 sx={{ 169 marginTop: redirectRules.length > 0 ? 55 : 0, 170 }} 171 > 172 <Grid item xs={12} sx={{ marginBottom: 14 }}> 173 <InputBox 174 fullWidth 175 id="accessKey" 176 value={accessKey} 177 onChange={(e: React.ChangeEvent<HTMLInputElement>) => 178 dispatch(setAccessKey(e.target.value)) 179 } 180 placeholder={useSTS ? "STS Username" : "Username"} 181 name="accessKey" 182 autoComplete="username" 183 disabled={loginSending} 184 startIcon={<UserFilledIcon />} 185 /> 186 </Grid> 187 <Grid item xs={12} sx={{ marginBottom: useSTS ? 14 : 0 }}> 188 <InputBox 189 fullWidth 190 value={secretKey} 191 onChange={(e: React.ChangeEvent<HTMLInputElement>) => 192 dispatch(setSecretKey(e.target.value)) 193 } 194 name="secretKey" 195 type="password" 196 id="secretKey" 197 autoComplete="current-password" 198 disabled={loginSending} 199 placeholder={useSTS ? "STS Secret" : "Password"} 200 startIcon={<LockFilledIcon />} 201 /> 202 </Grid> 203 {useSTS && ( 204 <Grid item xs={12}> 205 <InputBox 206 fullWidth 207 id="sts" 208 value={sts} 209 onChange={(e: React.ChangeEvent<HTMLInputElement>) => 210 dispatch(setSTS(e.target.value)) 211 } 212 placeholder={"STS Token"} 213 name="STS" 214 autoComplete="sts" 215 disabled={loginSending} 216 startIcon={<PasswordKeyIcon />} 217 /> 218 </Grid> 219 )} 220 </Grid> 221 222 <Grid 223 item 224 xs={12} 225 sx={{ 226 textAlign: "right", 227 marginTop: 30, 228 }} 229 > 230 <Button 231 type="submit" 232 variant="callAction" 233 color="primary" 234 id="do-login" 235 disabled={ 236 (!useSTS && (accessKey === "" || secretKey === "")) || 237 (useSTS && 238 (accessKey === "" || secretKey === "" || sts === "")) || 239 loginSending 240 } 241 label={"Login"} 242 sx={{ 243 margin: "30px 0px 8px", 244 height: 40, 245 width: "100%", 246 boxShadow: "none", 247 padding: "16px 30px", 248 }} 249 fullWidth 250 /> 251 </Grid> 252 <Grid 253 item 254 xs={12} 255 sx={{ 256 height: 10, 257 }} 258 > 259 {loginSending && <ProgressBar />} 260 </Grid> 261 </Fragment> 262 )} 263 <Grid item xs={12} sx={{ marginTop: 45 }}> 264 <Select 265 id="alternativeMethods" 266 name="alternativeMethods" 267 fixedLabel="Other Authentication Methods" 268 options={selectOptions} 269 onChange={extraActionSelector} 270 value={""} 271 /> 272 </Grid> 273 </form> 274 </React.Fragment> 275 ); 276 }; 277 278 export default StrategyForm;