mirror of
https://github.com/slynn1324/tinypin.git
synced 2026-01-23 10:25:34 +00:00
211 lines
No EOL
6.1 KiB
JavaScript
211 lines
No EOL
6.1 KiB
JavaScript
const tokenUtils = require('./token-utils.js');
|
|
const imageUtils = require('./image-utils.js');
|
|
const path = require('path');
|
|
const dao = require('./dao.js');
|
|
|
|
// auth helper functions
|
|
function sendAuthCookie(res, c){
|
|
res.cookie('s', tokenUtils.encrypt(c), {maxAge: 315569520000}); // 10 years
|
|
}
|
|
|
|
function maybeGetUser(req){
|
|
|
|
if ( !req.cookies ){
|
|
return null;
|
|
}
|
|
|
|
// if we made it this far, we're eady to check for the cookie
|
|
let s = req.cookies.s;
|
|
|
|
// TODO: should probably check if the user's access has been revoked,
|
|
// but we currently don't allow deleting users anyway. A key rotation would
|
|
// be the other solution, but that would log out all users and require new tokens
|
|
// to be created.
|
|
if ( s ){
|
|
try {
|
|
s = tokenUtils.decrypt(s);
|
|
if ( s.i && s.u ){
|
|
return {
|
|
id: s.i,
|
|
name: s.u
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.log(`error parsing cookie: `, err);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
module.exports = async (req, res, next) => {
|
|
|
|
// we will also accept the auth token in the x-api-key header
|
|
if ( req.headers["x-api-key"] ){
|
|
let apiKey = req.headers['x-api-key'];
|
|
try {
|
|
u = tokenUtils.decrypt(decodeURIComponent(apiKey));
|
|
req.user = {
|
|
id: u.i,
|
|
name: u.u
|
|
};
|
|
console.log("api key accepted for user " + req.user.name);
|
|
next();
|
|
return;
|
|
} catch (e) {
|
|
console.log("invalid api key");
|
|
res.sendStatus(403);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// handle one-time-links for images
|
|
if ( req.originalUrl.startsWith("/otl/" ) ){
|
|
try{
|
|
let token = tokenUtils.decrypt(req.originalUrl.substr(5));
|
|
|
|
// expire tokens in 5 minutes
|
|
if ( new Date().getTime() - token.t > 300000 ){ // 5 minutes
|
|
res.status(404).send(NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
let imagePath = imageUtils.getImagePath(token.u, token.p, 'o');
|
|
res.sendFile(imagePath.file);
|
|
return;
|
|
} catch (e){
|
|
res.status(404).send(NOT_FOUND);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// skip auth for pub resources
|
|
// handle login and register paths
|
|
if ( req.originalUrl == "/favicon.ico" ){
|
|
res.sendFile(path.resolve("./client/pub/icons/favicon.ico"));
|
|
return;
|
|
}
|
|
if ( req.originalUrl.startsWith("/pub/") ){
|
|
next();
|
|
return;
|
|
} if ( req.method == "GET" && req.originalUrl == "/login" ){
|
|
|
|
|
|
if ( maybeGetUser(req) ){
|
|
res.redirect("./");
|
|
return;
|
|
}
|
|
|
|
console.log("login");
|
|
// res.type("html").sendFile(path.resolve('./templates/login.html'));
|
|
res.render("login", { registerEnabled: dao.getProperty("registerEnabled"), csrfToken: req.csrfToken() });
|
|
return;
|
|
} else if ( req.method == "POST" && req.originalUrl == "/login" ){
|
|
let username = req.body.username;
|
|
let result = dao.getSaltForUser(username);
|
|
|
|
if ( !result ){
|
|
console.log(`login ${username} failed [unknown user]`);
|
|
res.redirect("/login#nope");
|
|
return;
|
|
}
|
|
|
|
let key = await tokenUtils.deriveKey(result.salt, req.body.password);
|
|
result = dao.getUserByNameAndKey(username, key);
|
|
|
|
if (!result){
|
|
console.log(`login ${username} failed [bad password]`);
|
|
res.redirect("/login#nope");
|
|
return;
|
|
}
|
|
|
|
sendAuthCookie(res, {
|
|
i: result.id,
|
|
u: username
|
|
});
|
|
|
|
console.log(`login ${username} ok`);
|
|
res.redirect("./");
|
|
return;
|
|
} else if ( req.method == "GET" && req.originalUrl == "/register" ){
|
|
|
|
let registerEnabled = dao.getProperty("registerEnabled");
|
|
if ( registerEnabled != 'y' ){
|
|
res.sendStatus(403);
|
|
return;
|
|
}
|
|
|
|
res.render("register", { csrfToken: req.csrfToken() });
|
|
return;
|
|
} else if ( req.method == "POST" && req.originalUrl == "/register" ){
|
|
|
|
let registerEnabled = dao.getProperty("registerEnabled");
|
|
if ( registerEnabled != 'y' ){
|
|
res.sendStatus(403);
|
|
return;
|
|
}
|
|
|
|
// if it's the first user, make them an admin - otherwise don't.
|
|
let userCount = dao.getUserCount();
|
|
let admin = userCount == 0 ? 1 : 0;
|
|
|
|
let username = req.body.username;
|
|
let salt = tokenUtils.createSalt();
|
|
let key = await tokenUtils.deriveKey(salt, req.body.password);
|
|
|
|
let result = dao.createUser(username, admin, key, salt);
|
|
|
|
if ( result && result.changes == 1 ){
|
|
sendAuthCookie(res, {
|
|
i: result.lastInsertRowid,
|
|
u: username
|
|
});
|
|
|
|
console.log(`created user ${username}`);
|
|
res.redirect("./");
|
|
} else {
|
|
console.log(`error creating account ${name}`);
|
|
res.redirect("/register#nope");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// // if we made it this far, we're eady to check for the cookie
|
|
// let s = req.cookies.s;
|
|
|
|
// // TODO: should probably check if the user's access has been revoked,
|
|
// // but we currently don't allow deleting users anyway. A key rotation would
|
|
// // be the other solution, but that would log out all users and require new tokens
|
|
// // to be created.
|
|
// if ( s ){
|
|
// try {
|
|
// s = tokenUtils.decrypt(s);
|
|
// if ( s.i && s.u ){
|
|
// req.user = {
|
|
// id: s.i,
|
|
// name: s.u
|
|
// }
|
|
// }
|
|
// } catch (err) {
|
|
// console.error(`error parsing cookie: `, err);
|
|
// }
|
|
// }
|
|
req.user = maybeGetUser(req);
|
|
|
|
if ( !req.user ){
|
|
res.redirect("/login");
|
|
return;
|
|
}
|
|
|
|
if ( req.method == "GET" && req.originalUrl == "/logout" ){
|
|
console.log(`logout ${req.user.name}`);
|
|
res.cookie('s', '', {maxAge:0});
|
|
res.redirect("/login");
|
|
return;
|
|
}
|
|
|
|
next();
|
|
|
|
} |