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;