ff-admin-server/src/controller/inviteController.ts

180 lines
5.7 KiB
TypeScript

import { Request, Response } from "express";
import { JWTHelper } from "../helpers/jwtHelper";
import InternalException from "../exceptions/internalException";
import RefreshCommandHandler from "../command/refreshCommandHandler";
import { CreateRefreshCommand } from "../command/refreshCommand";
import speakeasy from "speakeasy";
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
import QRCode from "qrcode";
import { CreateUserCommand } from "../command/management/user/userCommand";
import UserCommandHandler from "../command/management/user/userCommandHandler";
import { CreateInviteCommand, DeleteInviteCommand } from "../command/management/user/inviteCommand";
import InviteCommandHandler from "../command/management/user/inviteCommandHandler";
import MailHelper from "../helpers/mailHelper";
import InviteService from "../service/management/inviteService";
import UserService from "../service/management/userService";
import CustomRequestException from "../exceptions/customRequestException";
import InviteFactory from "../factory/admin/management/invite";
import SettingHelper from "../helpers/settingsHelper";
import { LoginRoutineEnum } from "../enums/loginRoutineEnum";
/**
* @description get all invites
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function getInvites(req: Request, res: Response): Promise<any> {
let invites = await InviteService.getAll();
res.json(InviteFactory.mapToBase(invites));
}
/**
* @description start first user
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function inviteUser(req: Request, res: Response, isSetup: boolean = false): Promise<any> {
let origin = req.headers.origin;
let username = req.body.username;
let mail = req.body.mail;
let firstname = req.body.firstname;
let lastname = req.body.lastname;
let users = await UserService.getByMailOrUsername(mail, username);
if (users.length == 1) {
// username or mail is used
if (users[0].username == username && users[0].mail == mail) {
throw new CustomRequestException(409, "Username and Mail are already in use");
} else if (users[0].username == username) {
throw new CustomRequestException(409, "Username is already in use");
} else {
throw new CustomRequestException(409, "Mail is already in use");
}
} else if (users.length >= 2) {
throw new CustomRequestException(409, "Username and Mail are already in use");
}
var secret = speakeasy.generateSecret({ length: 20, name: `FF Admin ${SettingHelper.getSetting("club.name")}` });
let createInvite: CreateInviteCommand = {
username: username,
mail: mail,
firstname: firstname,
lastname: lastname,
secret: secret.base32,
};
let token = await InviteCommandHandler.create(createInvite);
// sendmail
await MailHelper.sendMail(
mail,
`Email Bestätigung für Mitglieder Admin-Portal von ${SettingHelper.getSetting("club.name")}`,
`Öffne folgenden Link: ${origin}/${isSetup ? "setup" : "invite"}/verify?mail=${mail}&token=${token}`
);
res.sendStatus(204);
}
/**
* @description Create first user
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function verifyInvite(req: Request, res: Response): Promise<any> {
let mail = req.body.mail;
let token = req.body.token;
let { secret, username } = await InviteService.getByMailAndToken(mail, token);
const url = `otpauth://totp/FF Admin ${SettingHelper.getSetting("club.name")}?secret=${secret}`;
QRCode.toDataURL(url)
.then((result) => {
res.json({
dataUrl: result,
otp: secret,
username,
});
})
.catch((err) => {
throw new InternalException("QRCode not created", err);
});
}
/**
* @description Create first user
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function finishInvite(req: Request, res: Response, grantAdmin: boolean = false): Promise<any> {
let mail = req.body.mail;
let routine = req.body.routine;
let token = req.body.token;
let passedSecret = req.body.secret;
let { secret, username, firstname, lastname } = await InviteService.getByMailAndToken(mail, token);
let valid = false;
if (routine == LoginRoutineEnum.totp) {
valid = speakeasy.totp.verify({
secret: secret,
encoding: "base32",
token: passedSecret,
window: 2,
});
} else {
valid = passedSecret != "";
}
if (!valid) {
throw new UnauthorizedRequestException("Credentials not valid or expired");
}
let createUser: CreateUserCommand = {
username: username,
firstname: firstname,
lastname: lastname,
mail: mail,
secret: routine == LoginRoutineEnum.totp ? secret : passedSecret,
isOwner: grantAdmin,
routine,
};
let id = await UserCommandHandler.create(createUser);
let accessToken = await JWTHelper.buildToken(id);
let refreshCommand: CreateRefreshCommand = {
userId: id,
};
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
let deleteInvite: DeleteInviteCommand = {
mail: mail,
token: token,
};
await InviteCommandHandler.deleteByTokenAndMail(deleteInvite);
res.json({
accessToken,
refreshToken,
});
}
/**
* @description delete invite by mail
* @param req {Request} Express req object
* @param res {Response} Express res object
* @returns {Promise<*>}
*/
export async function deleteInvite(req: Request, res: Response): Promise<any> {
const mail = req.params.mail;
await InviteCommandHandler.deleteByMail(mail);
res.sendStatus(204);
}