import JwtService from "@services/jwt.service"; import type { IReqEditUserData } from "@interfaces/IReqEditUserData"; import UserService from "@services/user.service"; import type { Request, Response } from "express"; import { Logger } from "tslog"; const logger = new Logger({ name: "AuthController", }); //FIX Better return object interface /** * Registers a user with the given request data. * * @param {Request} req - The request object containing user data. * @param {Response} res - The response object to send the registration result. * * @return {Promise} A promise that resolves to the registration result. * It can have the following properties: * - error: "gdprNotApproved" if GDPR is not approved * - error: "exist" if the user already exists * - Otherwise, the registered user data */ async function registerUser( req: Request, res: Response, ): Promise { const body = req.body; if (!body) { logger.warn(`Invalid input data (${req.ip})`); return res.type("application/json").status(400).json({ error: "Invalid input data", }); } if ( !body.password || !body.username || !body.firstName || !body.lastName || !body.displayName ) { logger.warn(`Field(s) missing (${req.ip})`); return res.type("application/json").status(400).json({ error: "Field(s) missing", }); } let gdpr = false; if (body.gdpr === true) { gdpr = true; } const sanitizeData = { username: `${body.username}`, displayName: `${body.displayName}`, gdpr: gdpr, password: `${body.password}`, firstName: `${body.firstName}`, lastName: `${body.lastName}`, }; const RegisterServiceResult = await UserService.register(sanitizeData); if (RegisterServiceResult.error === "gdprNotApproved") { logger.warn(`GDPR not approved (${req.ip})`); return res.status(400).json({ error: RegisterServiceResult.error, message: "GDPR not accepted.", }); } if (RegisterServiceResult.error === "exist") { logger.warn(`The user already exists (${req.ip})`); return res.type("application/json").status(400).json({ error: RegisterServiceResult.error, message: "The user already exists.", }); } // SUCCESS logger.info(`User registered successfully (${req.ip})`); return res .type("application/json") .status(201) .json(RegisterServiceResult); } /** * Logs in a user with the provided credentials. * * @param {Request} req - The request object. * @param {Response} res - The response object. * * @return {Promise} A promise that resolves when the user is logged in or rejects with an error. */ async function loginUser( req: Request, res: Response, ): Promise { const body = req.body; if (!body) { res.type("application/json").status(400).json({ error: "Invalid input data", }); } if (!body.password || !body.username) { logger.warn(`Field(s) missing (${req.ip})`); res.type("application/json").status(400).json({ error: "Field(s) missing", }); } const loginData = { username: `${body.username}`, password: `${body.password}`, }; console.log(body); const LoginServiceResult = await UserService.login(loginData); console.log(LoginServiceResult); if (LoginServiceResult.error === "userNotFound") { console.log("POOL"); res.type("application/json").status(404).json({ error: LoginServiceResult.error, message: "User not found.", }); } if (LoginServiceResult.error === "invalidPassword") { res.type("application/json").status(401).json({ error: LoginServiceResult.error, message: "Invalid password.", }); } res .type("application/json") .status(200) .json(LoginServiceResult); } async function getAllUsers(req: Request, res: Response) { const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { logger.warn(`Bearer token not provided (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const sourceUser = await UserService.getFromId(payload.sub); if (!sourceUser) { return res.type("application/json").status(404).json({ error: "You dont exist anymore", }); } if (!sourceUser.is_admin) { return res.type("application/json").status(403).json({ error: "Unauthorized", }); } const AllUserResponse = await UserService.getAll(); if (!AllUserResponse.users) { return res.type("application/json").status(500).json({ error: "Internal server error", }); } return res .type("application/json") .status(200) .json(AllUserResponse); } async function getUser(req: Request, res: Response) { const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { logger.warn(`Bearer token not provided (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const sourceUser = await UserService.getFromId(payload.sub); if (!sourceUser) { return res.type("application/json").status(404).json({ error: "You dont exist anymore", }); } if (!sourceUser.is_admin) { return res.type("application/json").status(403).json({ error: "Unauthorized", }); } const userId = req.params["id"]; const dbUser = await UserService.getFromId(userId); if (!dbUser) { logger.warn(`User not found (${req.ip})`); return res.type("application/json").status(404).json({ error: "User not found", }); } // @ts-ignore delete dbUser.passwordHash; // @ts-ignore delete dbUser._id; return res.type("application/json").status(200).json(dbUser); } //FEAT - Implement re-auth by current password in case of password change async function editUser(req: Request, res: Response) { const body: IReqEditUserData | null = req.body; if (!body) { return res.type("application/json").status(400).json({ error: "Field(s) missing", }); } const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { logger.warn(`Bearer token not provided (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const sourceUser = await UserService.getFromId(payload.sub); //@ts-ignore const targetUserId = req.params.id || payload.sub; console.log(targetUserId); if (!sourceUser) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(404).json({ error: "You dont exist anymore", }); } if (sourceUser.is_admin || sourceUser.id === payload.sub) { if (sourceUser.is_admin) { logger.info( `EDIT :> Source user is an admin (${sourceUser.firstname} ${sourceUser.lastname})`, ); } else { logger.info( `EDIT :> Source user modify itself (${sourceUser.firstname} ${sourceUser.lastname})`, ); } //TODO Interface const modifiedData = {}; //@ts-ignore if (body.firstName) modifiedData.firstName = `${body.firstName}`; //@ts-ignore if (body.lastName) modifiedData.lastName = `${body.lastName}`; //@ts-ignore if (body.displayName) modifiedData.displayName = `${body.displayName}`; //TODO Case handled with hashing by the service. //if (body.password) modifiedData.password = `${body.password}`; //Call service const EditUserServiceResult = await UserService.edit( `${targetUserId}`, modifiedData, ); if (EditUserServiceResult.error === "userNotFound") { logger.warn(`User not found (${req.ip})`); return res.type("application/json").status(404).json({ error: "User not found", }); } if (EditUserServiceResult.error !== "none") { logger.error( `Error occurred during user edit (${req.ip})`, ); return res.type("application/json").status(500).json({ error: "Internal server error", }); } return res .type("application/json") .status(200) .json(EditUserServiceResult); } //Not itself or logger.warn( `Unauthorized access attempt, not self or admin (${req.ip})`, ); return res.type("application/json").status(403).json({ error: "Unauthorized", }); } async function deleteUser( req: Request, res: Response, ): Promise { const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { logger.warn(`Bearer token not provided (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Invalid token (${req.ip})`); return res.type("application/json").status(401).json({ error: "Invalid token", }); } const sourceUser = await UserService.getFromId(payload?.sub); const targetUserId = req.params["id"]; if (!sourceUser) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(404).json({ error: "You dont exist anymore", }); } if (sourceUser.is_admin || sourceUser.id === payload.sub) { const deleteUserServiceResult = await UserService.delete( `${targetUserId}`, ); if (!deleteUserServiceResult) { logger.error( `Error occurred during user delete (${req.ip})`, ); return res.type("application/json").status(500).json({ error: "Internal server error", }); } return res.type("application/json").status(200).json({ message: "User deleted successfully", }); } return res.type("application/json").status(403).json({ error: "Unauthorized", }); } async function deleteSelf(req: Request, res: Response) { const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { logger.warn(`Bearer token not provided (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const sourceUser = await UserService.getFromId(payload.sub); if (!sourceUser) { return res.type("application/json").status(404).json({ error: "You dont exist anymore", }); } if (sourceUser.id !== req.params["id"]) { return res.type("application/json").status(403).json({ error: "Unauthorized", }); } const deleteResult = await UserService.delete(sourceUser.id); if (!deleteResult) { logger.error(`Failed to delete user (${req.ip})`); return res.type("application/json").status(500).json({ error: "Failed to delete user", }); } return res.type("application/json").status(200).json({ message: "User deleted successfully", }); } async function getSelf(req: Request, res: Response) { const authHeader = req.headers.authorization; const bearerToken = authHeader?.split(" ")[1]; if (!bearerToken) { return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const payload = await JwtService.verify(bearerToken); if (!payload) { logger.warn(`Unauthorized access attempt (${req.ip})`); return res.type("application/json").status(401).json({ error: "Unauthorized", }); } const dbUser = await UserService.getFromId(payload.sub); if (!dbUser) { return res.type("application/json").status(404).json({ error: "User not found", }); } return res.type("application/json").status(200).json({ id: dbUser.id, username: dbUser.username, firstName: dbUser.firstname, lastName: dbUser.firstname, isAdmin: dbUser.firstname, }); } const AuthController = { register: registerUser, login: loginUser, getAllUsers, getUser, editUser, deleteUser, deleteSelf, getSelf, }; export default AuthController;