Skip to content

Commit

Permalink
REQUIREMENT : Credentials should be stored in a cookie
Browse files Browse the repository at this point in the history
  • Loading branch information
Nizreenana committed Jul 29, 2024
1 parent 9c4c3c9 commit 55e2afb
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 52 deletions.
77 changes: 48 additions & 29 deletions frontend/src/components/Menu.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
import '../styles/Menu.css';

import React, { useState, useEffect } from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Container from 'react-bootstrap/Container';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Form from 'react-bootstrap/Form';
import { Link } from 'react-router-dom';
import { useState } from 'react';

function Menu({backend, user, setUser}) {
function Menu({ backend }) {
const [user, setUser] = useState(null);

useEffect(() => {
fetchUser();
const interval = setInterval(fetchUser, 60000);
return () => clearInterval(interval);
}, []);

const fetchUser = () => {
fetch('/api/_session', { credentials: 'include' })
.then(res => res.json())
.then(data => setUser(data.userCtx.name))
.catch(() => setUser(null));
};

const handleLogin = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const credentials = Object.fromEntries(formData.entries());
backend.authenticate(credentials)
.then(() => {
fetchUser();
})
.catch(console.error);
};

const handleLogout = (e) => {
e.preventDefault();
backend.logout()
.then(() => setUser(null))
.catch(console.error);
};

return (
<Navbar bg="dark" variant="dark">
<Container>
Expand All @@ -21,44 +53,31 @@ function Menu({backend, user, setUser}) {
/>
</Link>
</Navbar.Brand>
<Authentication {...{backend, user, setUser}} />
<Authentication {...{ user, handleLogin, handleLogout }} />
</Container>
</Navbar>
);
}

function Authentication({backend, user, setUser}) {

let handleSubmit = (e) => {
e.preventDefault();
let credentials = Object.fromEntries(new FormData(e.target).entries());
backend.authenticate(credentials)
.then(() => setUser(credentials.name))
.catch(console.error);
};

if (user) return (
<Navbar.Text>
{user}
</Navbar.Text>
);
function Authentication({ user, handleLogin, handleLogout }) {
if (user) {
return (
<Navbar.Text>
{user} <a href="#logout" onClick={handleLogout}>Logout</a>
</Navbar.Text>
);
}
return (
<Form onSubmit={handleSubmit}>
<Form onSubmit={handleLogin}>
<Row className="g-1">
<Col>
<input placeholder="Username" name="name"
className="form-control-sm"
/>
<input placeholder="Username" name="name" className="form-control-sm" />
</Col>
<Col>
<input placeholder="Password" name="password" type="password"
className="form-control-sm"
/>
<input placeholder="Password" name="password" type="password" className="form-control-sm" />
</Col>
<Col>
<button className="btn btn-outline-light" type="submit">
Sign in
</button>
<button className="btn btn-outline-light" type="submit">Sign in</button>
</Col>
</Row>
</Form>
Expand Down
52 changes: 29 additions & 23 deletions frontend/src/hyperglosae.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import {Buffer} from 'buffer';
const service = '/api';

function Hyperglosae(logger) {

this.credentials = {};

this.getView = ({view, id, options = []}) =>
fetch(`${
service
Expand All @@ -23,18 +20,10 @@ function Hyperglosae(logger) {
fetch(`${service}/${id}`)
.then(x => x.json());

let basicAuthentication = ({force}) => {
let {name, password} = this.credentials;
if (!force && !name && !password) return ({});
return ({
'Authorization': 'Basic ' + Buffer.from(`${name}:${password}`).toString('base64')
});
};

this.putDocument = (doc) =>
fetch(`${service}/${doc._id}`, {
method: 'PUT',
headers: basicAuthentication({force: false}),
credentials: 'include',
body: JSON.stringify(doc)
})
.then(x => x.json())
Expand All @@ -49,7 +38,7 @@ function Hyperglosae(logger) {
this.deleteDocument = ({_id, _rev}) =>
fetch(`${service}/${_id}?rev=${_rev}`, {
method: 'DELETE',
headers: basicAuthentication({ force: false })
credentials: 'include'
})
.then(x => x.json())
.then(x => {
Expand All @@ -63,7 +52,7 @@ function Hyperglosae(logger) {
this.getDocumentMetadata = (id) =>
fetch(`${service}/${id}`, {
method: 'HEAD',
headers: basicAuthentication({ force: false })
credentials: 'include'
});

this.putAttachment = (id, attachment, callback) =>
Expand All @@ -75,9 +64,8 @@ function Hyperglosae(logger) {

fetch(`${service}/${id}/${attachment.name}`, {
method: 'PUT',
credentials: 'include',
headers: {
...basicAuthentication({ force: false }),
// ETag is the header that carries the current rev.
'If-Match': x.headers.get('ETag'),
'Content-Type': attachment.type
},
Expand All @@ -87,18 +75,36 @@ function Hyperglosae(logger) {
});

this.authenticate = ({name, password}) => {
this.credentials = {name, password};
return fetch(`${service}`, {
method: 'GET',
headers: basicAuthentication({force: true})
return fetch(`${service}/_session`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `name=${name}&password=${password}`,
credentials: 'include'
})
.then(x => x.json())
.then(x => {
if (x.reason) {
this.credentials = {};
if (x.error) {
logger(x.reason);
throw new Error(x.reason);
}
return x;
});
};

this.logout = () => {
return fetch(`${service}/_session`, {
method: 'DELETE',
credentials: 'include'
})
.then(x => x.json())
.then(x => {
if (x.error) {
logger(x.reason);
throw new Error(x.reason);
}
return x;
});
};

Expand All @@ -125,7 +131,7 @@ function Hyperglosae(logger) {
};

this.refreshDocuments = (callback) => {
let id = this.credentials.name || 'PUBLIC';
let id = 'PUBLIC';
this.getView({view: 'all_documents', id, options: ['include_docs']})
.then((rows) => {
callback(
Expand Down

0 comments on commit 55e2afb

Please sign in to comment.