Skip to content
8 changes: 1 addition & 7 deletions webui/pub/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 1 addition & 12 deletions webui/src/lib/components/navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,7 @@ const TopNavLink = ({ href, children }) => {
};

const TopNav = ({logged = true}) => {
if (!logged) {
return (
<Navbar variant="dark" bg="dark" expand="md">
<Container fluid={true}>
<Link component={Navbar.Brand} href="/">
<img src="/logo.png" alt="lakeFS" className="logo"/>
</Link>
</Container>
</Navbar>
);
}
return (
return logged && (
<Navbar variant="dark" bg="dark" expand="md" className="border-bottom">
<Container fluid={true}>
<Link component={Navbar.Brand} href="/">
Expand Down
137 changes: 72 additions & 65 deletions webui/src/pages/auth/login.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, {useState} from "react";
import Row from "react-bootstrap/Row";
import Card from "react-bootstrap/Card";
import Form from "react-bootstrap/Form";
import Col from "react-bootstrap/Col";
import Button from "react-bootstrap/Button";
import {auth, AuthenticationError, setup, SETUP_STATE_INITIALIZED} from "../../lib/api";
import {AlertError, Loading} from "../../lib/components/controls"
Expand Down Expand Up @@ -32,77 +30,86 @@ export interface LoginConfig {
const LoginForm = ({loginConfig}: {loginConfig: LoginConfig}) => {
const router = useRouter();
const navigate = useNavigate();
const [loginError, setLoginError] = useState(null);
const [loginError, setLoginError] = useState<React.ReactNode>(null);
const { next } = router.query;
const usernamePlaceholder = loginConfig.username_ui_placeholder || "Access Key ID";
const passwordPlaceholder = loginConfig.password_ui_placeholder || "Secret Access Key";

return (
<Row>
<Col md={{offset: 4, span: 4}}>
<Card className="login-widget shadow-lg border-0">
<Card.Header className="text">
<h4 className="mb-0">Login</h4>
</Card.Header>
<Card.Body className="p-4">
<Form onSubmit={async (e) => {
e.preventDefault()
try {
setLoginError(null);
await auth.login(e.target.username.value, e.target.password.value)
router.push(next || '/');
navigate(0);
} catch(err) {
if (err instanceof AuthenticationError && err.status === 401) {
const contents = {__html: `${loginConfig.login_failed_message}` ||
<div className="d-flex align-items-center justify-content-center">
<Card className="shadow-lg border-0 login-card">
<Card.Header className="text-center">
<div className="mt-3 mb-3">
<img src="/logo.svg" alt="lakeFS" className="login-logo" />
</div>
</Card.Header>
<Card.Body className="p-4">
<Form onSubmit={async (e) => {
e.preventDefault()
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
try {
setLoginError(null);
const username = formData.get('username');
const password = formData.get('password');
await auth.login(username, password);
router.push(next || '/');
navigate(0);
} catch(err) {
if (err instanceof AuthenticationError && err.status === 401) {
const contents = {__html: `${loginConfig.login_failed_message}` ||
"Credentials don't match."};
setLoginError(<span dangerouslySetInnerHTML={contents}/>);
}
setLoginError(<span dangerouslySetInnerHTML={contents}/>);
}
}}>
<Form.Group controlId="username" className="mb-3">
<Form.Control
type="text"
placeholder={usernamePlaceholder}
autoFocus
className="bg-light"
/>
</Form.Group>
}
}}>
<Form.Group controlId="username" className="mb-3">
<Form.Control
name="username"
type="text"
placeholder={usernamePlaceholder}
autoFocus
className="bg-light"
/>
</Form.Group>

<Form.Group controlId="password" className="mb-3">
<Form.Control
type="password"
placeholder={passwordPlaceholder}
className="bg-light"
/>
</Form.Group>
<Form.Group controlId="password" className="mb-3">
<Form.Control
name="password"
type="password"
placeholder={passwordPlaceholder}
className="bg-light"
/>
</Form.Group>

{(!!loginError) && <AlertError error={loginError}/>}
{(!!loginError) && <AlertError error={loginError}/>}

<Button
variant="primary"
type="submit"
className="w-100 mt-3 py-2"
>
Login
</Button>
</Form>
<div className={"mt-2 mb-1"}>
{ loginConfig.fallback_login_url ?
<Button variant="link" className="text-secondary mt-2" onClick={async ()=> {
loginConfig.login_cookie_names?.forEach(
cookie => {
document.cookie = `${cookie}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
}
);
window.location = loginConfig.fallback_login_url;
}}>{loginConfig.fallback_login_label || 'Try another way to login'}</Button>
: ""
}
</div>
</Card.Body>
</Card>
</Col>
</Row>
<Button
variant="primary"
type="submit"
className="w-100 mt-3 py-2"
>
Login
</Button>
</Form>
<div className={"mt-2 mb-1"}>
{ loginConfig.fallback_login_url ?
<Button variant="link" className="text-secondary mt-2" onClick={async ()=> {
loginConfig.login_cookie_names?.forEach(
cookie => {
document.cookie = `${cookie}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
}
);
if (loginConfig.fallback_login_url) {
window.location.href = loginConfig.fallback_login_url;
}
}}>{loginConfig.fallback_login_label || 'Try another way to login'}</Button>
: ""
}
</div>
</Card.Body>
</Card>
</div>
)
}

Expand Down Expand Up @@ -151,4 +158,4 @@ const LoginPage = () => {
);
};

export default LoginPage;
export default LoginPage;
11 changes: 11 additions & 0 deletions webui/src/styles/auth.css
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,14 @@ body .auth-page .nav-pills .nav-link:hover {
.auth-page .nav-pills .nav-link.active {
background-color: var(--success) !important;
}

.login-card {
max-width: 600px;
width: 90%;
margin: 50px auto auto auto;
}

.login-logo {
width: 269px;
height: 62px;
}
Loading