Files
Livebeat/backend/endpoints/user.ts
2021-05-03 20:58:40 +02:00

178 lines
5.5 KiB
TypeScript

import { Request, Response } from 'express';
import { decode, sign, verify } from 'jsonwebtoken';
import { eventManager, JWT_SECRET, logger, RABBITMQ_URI } from '../app';
import * as jwt from 'jsonwebtoken';
import { config } from '../config';
import { hashPassword, randomPepper, randomString, verifyPassword } from '../lib/crypto';
import { LivebeatRequest } from '../lib/request';
import { UserType } from '../models/user/user.interface';
import { User } from '../models/user/user.model';
export async function GetUser(req: LivebeatRequest, res: Response) {
let user: any = req.params.id === undefined ? req.user : await User.findById(req.params.id);
if (user === null) {
res.status(404).send();
return;
}
user.password = undefined;
user.salt = undefined;
user.__v = undefined;
res.status(200).send(user);
}
export async function PostUser(req: LivebeatRequest, res: Response) {
// Only admin can create new users
if (req.user?.type !== UserType.ADMIN) {
res.status(401).send({ message: 'Only admins can create new users.' });
return;
}
const name = req.body.name;
const password = req.body.password;
const type = req.body.type;
if (name === undefined || password === undefined || type === undefined) {
res.status(400).send();
return;
}
if (!Object.values(UserType).includes(type)) {
res.status(400).send({ message: 'The user type can only be \'guest\', \'user\', \'admin\'.' });
return;
}
if (await User.countDocuments({ name }) === 1) {
res.status(409).send();
return;
}
const salt = randomString(config.authentification.salt_length);
const eventToken = randomString(16);
const hashedPassword = await hashPassword(password + salt + randomPepper()).catch(error => {
res.status(400).send({ message: 'Provided password is too weak and cannot be used.' });
return;
}) as string;
const newUser = await User.create({
name,
password: hashedPassword,
salt,
eventToken,
type,
lastLogin: new Date(0)
});
// Create setup token that the new user can use to change his password.
const setupToken = jwt.sign({ setupForUser: newUser._id }, JWT_SECRET, { expiresIn: '1d' });
res.status(200).send({ setupToken });
}
export async function UserEvents(req: LivebeatRequest, res: Response) {
if (req.query.token === undefined) {
res.status(401).send({ message: 'You need to define your event token.' });
return;
}
const eventToken = req.query.token as string;
const user = await User.findOne({ eventToken });
if (user === null) {
res.status(401).send({ message: 'This event token is not valid.' });
return;
}
eventManager.join(user.id, res);
}
export async function UserSubscribeEvent(req: Request, res: Response) {
}
export async function DeleteUser(req: Request, res: Response) {
}
export async function PatchUser(req: Request, res: Response) {
}
export async function LoginUser(req: Request, res: Response) {
const username = req.body.username;
const password = req.body.password;
const twoFA = req.body.twoFA;
const user = await User.findOne({ name: username });
// Check if user exists
if (user == undefined) {
setTimeout(() => {
res.status(404).send({ message: "Either the username or password is wrong." });
}, Math.random() * 1500 + 400);
return;
}
// Check if 2FA is turned on (the attack doesn't know yet if the password is wrong)
if (user.twoFASecret != undefined) {
if (twoFA == undefined) {
res.status(401).send({ message: "2FA code is required." });
return;
}
// TODO: Implement 2FA logic here
}
// Check if password is wrong
if (!await verifyPassword(password + user.salt, user.password)) {
res.status(404).send({ message: 'Either the username or password is wrong.' });
return;
}
// We're good. Create JWT token.
const token = sign({ user: user._id }, JWT_SECRET, { expiresIn: '30d' });
user.lastLogin = new Date(Date.now());
await user.save();
logger.info(`User ${user.name} logged in.`)
res.status(200).send({ token });
}
/**
* This middleware validates any tokens that are required to access most of the endpoints.
* Note: This validation doesn't contain any permission checking.
*/
export async function MW_User(req: LivebeatRequest, res: Response, next: () => void) {
if (req.headers.token === undefined) {
res.status(401).send({ message: "Token not specified" });
return;
}
const token = req.headers.token.toString();
try {
// Verify token
if(await verify(token, JWT_SECRET, { algorithms: ['HS256'] })) {
// Token is valid, now look if user is in db (in case he got deleted)
const id = decode(token, { json: true })!.user;
const db = await User.findById(id);
if (db !== undefined && db !== null) {
req.user = db
next();
return;
} else {
res.status(401).send({ message: "Token is not valid" });
}
} else {
res.status(401).send({ message: "Token is not valid" });
}
} catch (err) {
if (err) {
res.status(500).send({ message: "We failed validating your token for some reason." });
logger.error(err);
}
}
}