335 lines
9.2 KiB
JavaScript
Executable File
335 lines
9.2 KiB
JavaScript
Executable File
"use strict";
|
|
|
|
const express = require("express"),
|
|
config = require("config"),
|
|
{ sendVerifyMail, sendPasswordChangedMail, sendVerifiedMail } = require("../lib/mail"),
|
|
crypto = require("crypto");
|
|
|
|
const router = express.Router();
|
|
const autoAuthenticate = 1;
|
|
|
|
let userDB;
|
|
|
|
require("../db/users.js").then(function(db) {
|
|
userDB = db;
|
|
});
|
|
|
|
router.get("/", function(req, res/*, next*/) {
|
|
console.log("/users/");
|
|
return getSessionUser(req).then((user) => {
|
|
return res.status(200).send(user);
|
|
}).catch((error) => {
|
|
console.log("User not logged in: " + error);
|
|
return res.status(200).send({});
|
|
});
|
|
});
|
|
|
|
router.put("/password", function(req, res) {
|
|
console.log("/users/password");
|
|
|
|
const changes = {
|
|
currentPassword: req.query.c || req.body.c,
|
|
newPassword: req.query.n || req.body.n
|
|
};
|
|
|
|
if (!changes.currentPassword || !changes.newPassword) {
|
|
return res.status(400).send("Missing current password and/or new password.");
|
|
}
|
|
|
|
if (changes.currentPassword == changes.newPassword) {
|
|
return res.status(400).send("Attempt to set new password to current password.");
|
|
}
|
|
|
|
return getSessionUser(req).then(function(user) {
|
|
return userDB.sequelize.query("SELECT id FROM users " +
|
|
"WHERE uid=:username AND password=:password", {
|
|
replacements: {
|
|
username: user.username,
|
|
password: crypto.createHash('sha256').update(changes.currentPassword).digest('base64')
|
|
},
|
|
type: userDB.Sequelize.QueryTypes.SELECT,
|
|
raw: true
|
|
}).then(function(users) {
|
|
if (users.length != 1) {
|
|
return null;
|
|
}
|
|
return user;
|
|
});
|
|
}).then(function(user) {
|
|
if (!user) {
|
|
console.log("Invalid password");
|
|
/* Invalid password */
|
|
res.status(401).send("Invalid password");
|
|
return null;
|
|
}
|
|
|
|
return userDB.sequelize.query("UPDATE users SET password=:password WHERE uid=:username", {
|
|
replacements: {
|
|
username: user.username,
|
|
password: crypto.createHash('sha256').update(changes.newPassword).digest('base64')
|
|
}
|
|
}).then(function() {
|
|
console.log("Password changed for user " + user.username + " to '" + changes.newPassword + "'.");
|
|
|
|
res.status(200).send(user);
|
|
user.id = req.session.userId;
|
|
return sendPasswordChangedMail(userDB, req, user);
|
|
});
|
|
});
|
|
});
|
|
|
|
router.get("/csrf", (req, res) => {
|
|
console.log("/users/csrf");
|
|
res.json({ csrfToken: req.csrfToken() });
|
|
});
|
|
|
|
router.post("/signup", function(req, res) {
|
|
console.log("/users/signup");
|
|
|
|
const user = {
|
|
uid: req.body.email,
|
|
familyName: req.body.familyName,
|
|
firstName: req.body.firstName,
|
|
password: req.body.password,
|
|
email: req.body.email,
|
|
};
|
|
|
|
if (!user.uid
|
|
|| !user.email
|
|
|| !user.password
|
|
|| !user.familyName
|
|
|| !user.firstName) {
|
|
return res.status(400).send({
|
|
message: `Missing email address, password, and/or name.`
|
|
});
|
|
}
|
|
|
|
user.password = crypto.createHash('sha256')
|
|
.update(user.password).digest('base64');
|
|
user.md5 = crypto.createHash('md5')
|
|
.update(user.email).digest('base64');
|
|
|
|
return userDB.sequelize.query("SELECT * FROM users WHERE uid=:uid", {
|
|
replacements: user,
|
|
type: userDB.Sequelize.QueryTypes.SELECT,
|
|
raw: true
|
|
}).then(async function(results) {
|
|
if (results.length != 0 && results[0].mailVerified) {
|
|
return res.status(400).send({
|
|
message: `Email address already used.`
|
|
});
|
|
}
|
|
|
|
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
if (!re.exec(user.email)) {
|
|
const error = `Invalid email address: ${user.email}.`;
|
|
console.log(error);
|
|
return res.status(401).send({
|
|
message: error
|
|
});
|
|
}
|
|
|
|
try {
|
|
if (results.length != 0) {
|
|
await userDB.sequelize.query("UPDATE users " +
|
|
"SET mailVerified=0");
|
|
req.session.userId = results[0].id;
|
|
} else {
|
|
let [, metadata] = await userDB.sequelize.query("INSERT INTO users " +
|
|
"(uid,firstName,familyName,password,email,memberSince," +
|
|
"authenticated,md5) " +
|
|
`VALUES(:uid,:firstName,:familyName,:password,` +
|
|
`:email,CURRENT_TIMESTAMP,${autoAuthenticate},:md5)`, {
|
|
replacements: user
|
|
});
|
|
req.session.userId = metadata.lastID;
|
|
}
|
|
return getSessionUser(req).then(function(user) {
|
|
res.status(200).send(user);
|
|
user.id = req.session.userId;
|
|
return sendVerifyMail(userDB, req, user);
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
const getSessionUser = function(req) {
|
|
return Promise.resolve().then(function() {
|
|
if (!req.session || !req.session.userId) {
|
|
throw "Unauthorized. You must be logged in.";
|
|
}
|
|
|
|
let query = "SELECT " +
|
|
"uid AS username,firstName,familyName,mailVerified,authenticated,memberSince,email,md5 " +
|
|
"FROM users WHERE id=:id";
|
|
return userDB.sequelize.query(query, {
|
|
replacements: {
|
|
id: req.session.userId
|
|
},
|
|
type: userDB.Sequelize.QueryTypes.SELECT,
|
|
raw: true
|
|
}).then(function(results) {
|
|
if (results.length != 1) {
|
|
throw "Invalid account.";
|
|
}
|
|
|
|
let user = results[0];
|
|
|
|
if (!user.mailVerified) {
|
|
user.restriction = user.restriction || "Email address not verified.";
|
|
return user;
|
|
}
|
|
|
|
if (!user.authenticated) {
|
|
user.restriction = user.restriction || "Accout not authorized.";
|
|
return user;
|
|
}
|
|
|
|
return user;
|
|
});
|
|
}).then(function(user) {
|
|
/* Strip out any fields that shouldn't be there. The allowed fields are: */
|
|
let allowed = [
|
|
"maintainer", "username", "firstName", "familyName", "mailVerified", "authenticated", "name", "email", "restriction", "md5"
|
|
];
|
|
for (let field in user) {
|
|
if (allowed.indexOf(field) == -1) {
|
|
delete user[field];
|
|
}
|
|
}
|
|
return user;
|
|
});
|
|
}
|
|
|
|
router.post("/verify-email", async (req, res) => {
|
|
console.log("/users/verify-email");
|
|
const key = req.body.token;
|
|
|
|
let results = await userDB.sequelize.query(
|
|
"SELECT * FROM authentications WHERE key=:key", {
|
|
replacements: { key },
|
|
type: userDB.sequelize.QueryTypes.SELECT
|
|
});
|
|
|
|
let token;
|
|
if (results.length == 0) {
|
|
console.log("Invalid key. Ignoring.");
|
|
return res.status(400).send({
|
|
message: `Invalid authentication token.`
|
|
});
|
|
}
|
|
|
|
token = results[0];
|
|
console.log(token);
|
|
console.log("Matched token: " + JSON.stringify(token, null, 2));
|
|
switch (token.type) {
|
|
case "account-setup":
|
|
return userDB.sequelize.query(
|
|
"UPDATE users SET mailVerified=1 WHERE id=:userId", {
|
|
replacements: token
|
|
})
|
|
.then(function () {
|
|
return userDB.sequelize.query(
|
|
"DELETE FROM authentications WHERE key=:key", {
|
|
replacements: { key }
|
|
});
|
|
})
|
|
.then(function () {
|
|
return userDB.sequelize.query(
|
|
"SELECT * FROM users WHERE id=:userId", {
|
|
replacements: token,
|
|
type: userDB.sequelize.QueryTypes.SELECT
|
|
});
|
|
})
|
|
.then(function (results) {
|
|
if (results.length == 0) {
|
|
return res.status(500).send({
|
|
message: `Internal authentication error.`
|
|
});
|
|
}
|
|
return results[0];
|
|
})
|
|
.then((user) => {
|
|
sendVerifiedMail(userDB, req, user);
|
|
req.session.userId = user.id;
|
|
}).then(function (user) {
|
|
return getSessionUser(req).then(function (user) {
|
|
return res.status(200).send(user);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
router.post("/signin", function(req, res) {
|
|
console.log("/users/signin");
|
|
|
|
let { email, password } = req.body;
|
|
|
|
if (!email || !password) {
|
|
return res.status(400).send({
|
|
message: `Missing email and/or password`
|
|
});
|
|
}
|
|
|
|
console.log("Looking up user in DB.");
|
|
|
|
let query = "SELECT " +
|
|
"id,mailVerified,authenticated," +
|
|
"uid AS username," +
|
|
"familyName,firstName,email " +
|
|
"FROM users WHERE uid=:username AND password=:password";
|
|
return userDB.sequelize.query(query, {
|
|
replacements: {
|
|
username: email,
|
|
password: crypto.createHash('sha256').update(password).digest('base64')
|
|
},
|
|
type: userDB.Sequelize.QueryTypes.SELECT
|
|
})
|
|
.then(function(users) {
|
|
if (users.length != 1) {
|
|
return null;
|
|
}
|
|
let user = users[0];
|
|
req.session.userId = user.id;
|
|
return user;
|
|
})
|
|
.then(function(user) {
|
|
if (!user) {
|
|
console.log(email + " not found (or invalid password.)");
|
|
req.session.userId = null;
|
|
return res.status(401).send({
|
|
message: `Invalid sign in credentials`
|
|
});
|
|
}
|
|
|
|
let message = "Logged in as " + user.email + " (" + user.id + ")";
|
|
if (!user.mailVerified) {
|
|
console.log(message + ", who is not verified email.");
|
|
} else if (!user.authenticated) {
|
|
console.log(message + ", who is not authenticated.");
|
|
} else {
|
|
console.log(message);
|
|
}
|
|
|
|
return getSessionUser(req).then(function(user) {
|
|
return res.status(200).send(user);
|
|
});
|
|
})
|
|
.catch(function(error) {
|
|
console.log(error);
|
|
return res.status(403).send(error);
|
|
});
|
|
});
|
|
|
|
router.post("/signout", (req, res) => {
|
|
console.log("/users/signout");
|
|
if (req.session && req.session.userId) {
|
|
req.session.userId = null;
|
|
}
|
|
res.status(200).send({});
|
|
});
|
|
|
|
module.exports = router;
|