github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/webui/src/pages/auth/login.tsx (about) 1 import React, {useState} from "react"; 2 import Row from "react-bootstrap/Row"; 3 import Card from "react-bootstrap/Card"; 4 import Form from "react-bootstrap/Form"; 5 import Col from "react-bootstrap/Col"; 6 import Button from "react-bootstrap/Button"; 7 import {auth, AuthenticationError, setup, SETUP_STATE_INITIALIZED} from "../../lib/api"; 8 import {AlertError} from "../../lib/components/controls" 9 import {useRouter} from "../../lib/hooks/router"; 10 import {useAPI} from "../../lib/hooks/api"; 11 12 interface LoginConfig { 13 login_url: string; 14 login_failed_message?: string; 15 fallback_login_url?: string; 16 fallback_login_label?: string; 17 login_cookie_names: string[]; 18 logout_url: string; 19 } 20 21 const LoginForm = ({loginConfig}: {loginConfig: LoginConfig}) => { 22 const router = useRouter(); 23 const [loginError, setLoginError] = useState(null); 24 const { next } = router.query; 25 26 return ( 27 <Row> 28 <Col md={{offset: 4, span: 4}}> 29 <Card className="login-widget"> 30 <Card.Header>Login</Card.Header> 31 <Card.Body> 32 <Form onSubmit={async (e) => { 33 e.preventDefault() 34 try { 35 await auth.login(e.target.username.value, e.target.password.value) 36 setLoginError(null); 37 router.push(next ? next : '/'); 38 } catch(err) { 39 if (err instanceof AuthenticationError && err.status === 401) { 40 const contents = {__html: `${loginConfig.login_failed_message}` || 41 "Credentials don't match."}; 42 setLoginError(<span dangerouslySetInnerHTML={contents}/>); 43 } 44 } 45 }}> 46 <Form.Group controlId="username" className="mb-3"> 47 <Form.Control type="text" placeholder={"Access Key ID"} autoFocus/> 48 </Form.Group> 49 50 <Form.Group controlId="password" className="mb-3"> 51 <Form.Control type="password" placeholder={"Secret Access Key"}/> 52 </Form.Group> 53 54 {(!!loginError) && <AlertError error={loginError}/>} 55 56 <Button variant="primary" type="submit">Login</Button> 57 </Form> 58 <div className={"mt-2 mb-1"}> 59 { loginConfig.fallback_login_url ? 60 <Button variant="link" className="text-secondary mt-2" onClick={async ()=> { 61 loginConfig.login_cookie_names?.forEach( 62 cookie => { 63 document.cookie = `${cookie}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`; 64 } 65 ); 66 window.location = loginConfig.fallback_login_url; 67 }}>{loginConfig.fallback_login_label || 'Try another way to login'}</Button> 68 : "" 69 } 70 </div> 71 </Card.Body> 72 </Card> 73 </Col> 74 </Row> 75 ) 76 } 77 78 79 const LoginPage = () => { 80 const router = useRouter(); 81 const { response, error, loading } = useAPI(() => setup.getState()); 82 if (loading) { 83 return null; 84 } 85 86 // if we are not initialized, or we are not done with comm prefs, redirect to 'setup' page 87 if (!error && response && (response.state !== SETUP_STATE_INITIALIZED || response.comm_prefs_missing === true)) { 88 router.push({pathname: '/setup', query: router.query}) 89 return null; 90 } 91 const loginConfig = response?.login_config; 92 if (router.query.redirected) { 93 if(!error && loginConfig?.login_url) { 94 window.location = loginConfig.login_url; 95 return null; 96 } 97 delete router.query.redirected; 98 99 router.push({pathname: '/auth/login', query: router.query}) 100 } 101 return ( 102 <LoginForm loginConfig={loginConfig}/> 103 ); 104 }; 105 106 export default LoginPage;