2021-06-14 22:04:46 +00:00
|
|
|
const express = require('express');
|
|
|
|
const crypto = require('crypto');
|
|
|
|
const Config = require('./config.js');
|
|
|
|
const Database = require('./db_interface.js');
|
|
|
|
|
|
|
|
let router = express.Router();
|
|
|
|
|
|
|
|
router.use(express.json());
|
|
|
|
|
|
|
|
let session_entropy = {};
|
|
|
|
|
|
|
|
user_cache = {};
|
|
|
|
email_cache = {};
|
|
|
|
|
|
|
|
async function get_user_details(id) {
|
|
|
|
if (!id) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
console.log(`search for user with id ${id}`);
|
|
|
|
if (!user_cache[id]) {
|
|
|
|
let user = await Database.schemas.user.findOne({where: {id: id}});
|
|
|
|
if (!user) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
user_cache[user.id] = {
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
password_hash: user.password_hash,
|
|
|
|
};
|
|
|
|
email_cache[user.email] = user.id;
|
|
|
|
}
|
|
|
|
console.log(`returning ${JSON.stringify(user_cache[id])}`);
|
|
|
|
return user_cache[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
async function get_user_details_by_email(email) {
|
|
|
|
if (!email) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
console.log(`search for user with email ${email}}`);
|
|
|
|
if (!email_cache[email] || !user_cache[email_cache[email]]) {
|
|
|
|
let user = await Database.schemas.user.findOne({where: {email: email}});
|
|
|
|
if (!user) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
user_cache[user.id] = {
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
password_hash: user.password_hash,
|
|
|
|
};
|
|
|
|
email_cache[user.email] = user.id;
|
|
|
|
}
|
|
|
|
console.log(`returning ${JSON.stringify(user_cache[email_cache[email]])}`);
|
|
|
|
return user_cache[email_cache[email]];
|
|
|
|
}
|
|
|
|
|
|
|
|
router.get('/byEmail/:email', async (req, res) => {
|
|
|
|
if (!req.params?.email) {
|
|
|
|
res.status(400).json({
|
|
|
|
error: 'email is a required parameter',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let user = get_user_details_by_email(req.params.email);
|
|
|
|
console.log(user);
|
|
|
|
if (user != null) {
|
|
|
|
res.json({
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
res.sendStatus(404);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function hash(secret, password) {
|
|
|
|
let pw_hash = crypto.pbkdf2Sync(
|
|
|
|
password,
|
|
|
|
secret,
|
|
|
|
Config.config.key?.iterations || 1000,
|
|
|
|
Config.config.key?.length || 64,
|
|
|
|
'sha512'
|
|
|
|
);
|
|
|
|
|
|
|
|
return pw_hash.toString('base64');
|
|
|
|
}
|
|
|
|
|
|
|
|
function verify(secret, password, hash) {
|
|
|
|
let pw_hash = crypto.pbkdf2Sync(
|
|
|
|
password,
|
|
|
|
secret,
|
|
|
|
Config.config.key?.iterations || 1000,
|
|
|
|
Config.config.key?.length || 64,
|
|
|
|
'sha512'
|
|
|
|
);
|
|
|
|
|
|
|
|
return hash === pw_hash.toString('base64');
|
|
|
|
}
|
|
|
|
|
|
|
|
function hash_password(password) {
|
|
|
|
return hash(Config.config.secret, password);
|
|
|
|
}
|
|
|
|
|
|
|
|
function verify_password(password, hash) {
|
|
|
|
return verify(Config.config.secret, password, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_session_token(id, token) {
|
|
|
|
session_entropy[id] = crypto.randomBytes(Config.config.session_entropy || 32);
|
|
|
|
return hash(session_entropy[id], token);
|
|
|
|
}
|
|
|
|
|
|
|
|
function verify_session_token(id, hash, token) {
|
|
|
|
if (session_entropy[id]) {
|
|
|
|
return verify(session_entropy[id], hash, token);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function enforce_session_login(req, res, next) {
|
|
|
|
let userid = req.cookies?.userid;
|
|
|
|
let session_token = req.cookies?._session;
|
|
|
|
console.log('a', userid, session_token);
|
|
|
|
if (!userid || !session_token) {
|
|
|
|
return res.sendStatus(401);
|
|
|
|
}
|
|
|
|
let user = await get_user_details(userid);
|
|
|
|
if (!user) {
|
|
|
|
return res.sendStatus(401);
|
|
|
|
}
|
|
|
|
let verified_session = verify_session_token(
|
|
|
|
userid,
|
|
|
|
user.password_hash,
|
|
|
|
session_token
|
|
|
|
);
|
|
|
|
if (!verified_session) {
|
|
|
|
return res.sendStatus(401);
|
|
|
|
}
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
|
|
|
|
router.post('/new', async (req, res) => {
|
|
|
|
if (!req.body?.email || !req.body?.password) {
|
|
|
|
return res.status(400).json({
|
|
|
|
error: 'must have email and password fields',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let user = await get_user_details_by_email(req.body.email);
|
|
|
|
console.log(user);
|
|
|
|
if (user != null) {
|
|
|
|
return res.status(403).json({
|
|
|
|
error: `email ${req.body.email} is already in use.`,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
let user = await Database.schemas.user.create({
|
|
|
|
email: String(req.body.email),
|
|
|
|
password_hash: hash_password(req.body.password),
|
|
|
|
});
|
|
|
|
|
|
|
|
return res.json({
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
router.post('/login', async (req, res) => {
|
|
|
|
if (!req.body?.email || !req.body?.password) {
|
|
|
|
return res.status(400).json({
|
|
|
|
error: 'must have email and password fields',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let user = await get_user_details_by_email(req.body.email);
|
|
|
|
if (!user) {
|
|
|
|
return res.status(401).json({
|
|
|
|
error: 'incorrect email or password',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let verified = verify_password(req.body.password, user.password_hash);
|
|
|
|
|
|
|
|
if (!verified) {
|
|
|
|
return res.status(401).json({
|
|
|
|
error: 'incorrect email or password',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
res.cookie('userid', user.id, {
|
|
|
|
httpOnly: true,
|
|
|
|
secure: true,
|
|
|
|
});
|
|
|
|
res.cookie('_session', get_session_token(user.id, user.password_hash), {
|
|
|
|
httpOnly: true,
|
|
|
|
secure: true,
|
|
|
|
});
|
|
|
|
return res.sendStatus(204);
|
|
|
|
});
|
|
|
|
|
|
|
|
router.get('/:id([a-f0-9-]+)', async (req, res) => {
|
|
|
|
console.log(req.params);
|
|
|
|
if (!req.params?.id) {
|
|
|
|
return res.status(400).json({
|
|
|
|
error: 'must have id parameter',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let id = req.params?.id;
|
|
|
|
console.log(id);
|
|
|
|
let user = await get_user_details(id);
|
|
|
|
console.log(user);
|
|
|
|
if (user != null) {
|
|
|
|
return res.json({
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return res.sendStatus(404);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
router.use('/authorized', enforce_session_login);
|
|
|
|
router.get('/authorized', async (req, res) => {
|
|
|
|
let userid = req.cookies?.userid;
|
|
|
|
let user = await get_user_details(userid);
|
|
|
|
return res.json({
|
|
|
|
authorized: true,
|
|
|
|
user: {
|
|
|
|
id: user.id,
|
|
|
|
email: user.email,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
router: router,
|
|
|
|
enforce_session_login: enforce_session_login,
|
|
|
|
};
|