import bcrypt from "bcrypt" import { User, OperationLog, PasswordRecover } from "@db_models" import Account from "@classes/account" import AuthToken from "@shared-classes/AuthToken" export default async ( { user_id, old_hash, old_password, new_password, code, verificationToken, log_comment, }, req, ) => { if (!code && !old_password) { throw new OperationError( 400, "Either code or old_password must be provided", ) } let verificationTokenDecoded = null if (verificationToken) { verificationTokenDecoded = await AuthToken.basicDecode(verificationToken) } let user = await User.findById( user_id || verificationTokenDecoded?.user_id, ).select("+password") if (!user) { throw new OperationError(404, "User not found") } if (code) { const passwordRecoverSession = await PasswordRecover.findOne({ code }) if (!passwordRecoverSession) { throw new OperationError(401, "Invalid code") } if ( verificationTokenDecoded.recoverySessionId !== passwordRecoverSession._id.toString() ) { throw new OperationError(401, "Invalid code") } if (passwordRecoverSession.expires < Date.now()) { throw new OperationError(401, "Code expired") } // delete passwordRecoverSession await PasswordRecover.findByIdAndDelete( passwordRecoverSession._id.toString(), ) } else { user = await Account.loginStrategy( { password: old_password, hash: old_hash }, user, ) } await Account.passwordMeetPolicy(new_password) user.password = bcrypt.hashSync( new_password, parseInt(process.env.BCRYPT_ROUNDS ?? 3), ) await user.save() const operation = { type: "password:changed", user_id: user._id.toString(), date: Date.now(), comments: [], } if (log_comment) { operation.comments.push(log_comment) } if (typeof req === "object") { operation.ip_address = req.headers["x-forwarded-for"]?.split(",")[0] ?? req.socket?.remoteAddress ?? req.ip operation.client = req.headers["user-agent"] } const log = new OperationLog(operation) await log.save() ipc.invoke("ems", "password:changed", operation) return user }