From 2e2513f533507d7fc4167b56eb1a52e7ae2fadea Mon Sep 17 00:00:00 2001 From: flifloo Date: Tue, 25 Aug 2020 16:46:02 +0200 Subject: [PATCH] Add forget password management --- app.js | 2 ++ locales/en.json | 6 ++-- locales/fr.json | 7 +++-- models/user.js | 7 +++++ routes/forget.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ views/forget.pug | 20 ++++++++++++++ views/layout.pug | 2 +- views/login.pug | 23 ++++++++++------ 8 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 routes/forget.js create mode 100644 views/forget.pug diff --git a/app.js b/app.js index 60073c1..fc317e4 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,7 @@ let config = process.env.NODE_ENV === "test" ? {} : require("./config/config.jso let indexRouter = require("./routes/index"); let registerRouter = require("./routes/register"); let loginRouter = require("./routes/login"); +let forgetRouter = require("./routes/forget"); let logoutRouter = require("./routes/logout"); let orderRouter = require("./routes/order"); let ordersRouter = require("./routes/orders"); @@ -66,6 +67,7 @@ app.use((req, res, next) => { app.use("/", indexRouter); app.use("/register", registerRouter); app.use("/login", loginRouter); +app.use("/forget", forgetRouter); app.use("/logout", logoutRouter); app.use("/order", orderRouter); app.use("/orders", ordersRouter); diff --git a/locales/en.json b/locales/en.json index 545779e..b6b0304 100644 --- a/locales/en.json +++ b/locales/en.json @@ -42,7 +42,8 @@ "title": "Profile", "infos": "Infos", "emailCheck": "Email verification", - "emailCheckMessage": "We need to validate your email address, please click on the link below to validate it\n\n%s" + "emailCheckMessage": "We need to validate your email address, please click on the link below to validate it\n\n%s", + "forgetPasswordMessage": "A request to change your password has been made on your account, if it comes from clicking on the link below, otherwise ignore this email.\n\n%s" }, "admin": { "title": "Administration", @@ -79,5 +80,6 @@ "save": "Save", "sandwich": "Sandwich", "contact": "Contact", - "messageSend": "The message has been sent" + "messageSend": "The message has been sent", + "forgetPassword": "Forgot your password" } diff --git a/locales/fr.json b/locales/fr.json index cd839ac..2a0e68a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -42,7 +42,8 @@ "title": "Profil", "infos": "Infos", "emailCheck": "Vérification e-mail", - "emailCheckMessage": "Nous avons besoin de valider votre adresse email, merci de clicker sur le lien si dessous pour la valider\n\n%s" + "emailCheckMessage": "Nous avons besoin de valider votre adresse email, merci de clicker sur le lien si dessous pour la valider.\n\n%s", + "forgetPasswordMessage": "Une demande de changement de mot de passe a été faite sur votre compte, si cela proviens bine de vous clicquer sur le lien ci-dessous, sinon ingorer cette email.\n\n%s" }, "admin": { "title": "Administration", @@ -79,5 +80,7 @@ "save": "Enregistrer", "sandwich": "Sandwich", "contact": "Contact", - "messageSend": "Le message a bien été envoyé" + "messageSend": "Le message a bien été envoyé", + "forgetPassword": "Mot de passe oublié", + "send": "send" } \ No newline at end of file diff --git a/models/user.js b/models/user.js index 0254de9..7aca9e8 100644 --- a/models/user.js +++ b/models/user.js @@ -60,6 +60,13 @@ module.exports = (sequelize, DataTypes) => { digest("base64")); } }, + passwordToken: { + type: DataTypes.STRING, + unique: true + }, + passwordTokenDate: { + type: DataTypes.DATE + }, permissions: { // 1 = sandwich page, 2 = order page, 3 = admin type: DataTypes.INTEGER, defaultValue: 0, diff --git a/routes/forget.js b/routes/forget.js new file mode 100644 index 0000000..4e0fa53 --- /dev/null +++ b/routes/forget.js @@ -0,0 +1,71 @@ +const express = require("express"); +const router = express.Router(); +const error = require("./utils/error"); +const models = require("../models"); +const crypto = require("crypto"); +const Message = require("emailjs").Message; + + +async function checkToken(req, res, token) { + let user = await models.User.findOne({where: {passwordToken: token}}); + if (!user) + return error(req, res, "Can't reset password", 400, "Invalid token"); + else if (user.passwordTokenDate && ((new Date().getTime() - user.passwordTokenDate.getTime())/1000 > 3600)) + return error(req, res, "Can't reset password", 400, "Token expired"); + else + return user; +} + + +router.get("/", async (req, res) => { + if (req.session.user) + res.redirect("/"); + else + if (!req.query.token) + res.render("forget", {title: "SOD - Forget password"}); + else { + if (await checkToken(req, res, req.query.token)) + res.render("forget", {title: "SOD - Change password", token: req.query.token}) + } +}).post("/", async (req, res) => { + if (req.body.email && !req.body.password && !req.body.token) { + let user = await models.User.findOne({where: {email: req.body.email}}); + let config = req.app.get("config"); + + if (!user) + return error(req, res, "Can't reset password", 400, "Invalid email"); + + let token = crypto.randomBytes(16).toString("hex"); + while (await models.User.findOne({where: {passwordToken: token}})) + token = crypto.randomBytes(16).toString("hex"); + + req.app.get("mailClient").send( new Message({ + text: res.__("profile.forgetPasswordMessage", `${req.protocol}://${req.hostname}/forget?token=${token}`), + from: config.email.from, + to: user.email, + subject: res.__("forgetPassword") + }), async (err, message) => { + if (err) + return error(req, res, "Fail to send message !", 500, + req.app.get("env") !== "production" ? err : undefined); + else { + user.passwordToken = token; + user.passwordTokenDate = new Date(); + await user.save(); + res.redirect("/"); + } + }); + } else if (req.body.password && req.body.token && !req.body.email) { + let user = await checkToken(res, res, req.body.token); + if (user) { + user.passwordToken = null; + user.passwordTokenDate = null; + user.passwordHash = req.body.password; + await user.save(); + res.redirect("/login"); + } + } else + return error(req, res, "Can't change password", 400, "Invalid args"); +}); + +module.exports = router; diff --git a/views/forget.pug b/views/forget.pug new file mode 100644 index 0000000..332068d --- /dev/null +++ b/views/forget.pug @@ -0,0 +1,20 @@ +extends layout + +block content + div.card + h1=__("forgetPassword") + if (!token) + form(action="/forget" method="POST") + div.field + label(for="email")=__("email") + input#email(type="email" name="email" required) + div.field + +submit(value=__("send")) + else + form(action="/forget" method="POST") + input(type="hidden" name="token" value=token) + div.field + label(for="password")=__("password") + input#password(type="password" name="password" required) + div.field + +submit(value=__("send")) diff --git a/views/layout.pug b/views/layout.pug index b28d3a7..e6f9974 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -88,7 +88,7 @@ html label(for="messageContact")=__("layout.message") textarea#messageContact(name="message" required) div.field - +submit(value=__("layout.send")) + +submit(value=__("send")) script(src="/javascripts/layout.js") if !test diff --git a/views/login.pug b/views/login.pug index 41f20e6..8867af1 100644 --- a/views/login.pug +++ b/views/login.pug @@ -1,13 +1,18 @@ extends layout block content - form.card(action="/login" method="POST") - h1=__("login.title") + div.card + form(action="/login" method="POST") + h1=__("login.title") + div.field + label(for="username")=__("username")+":" + input#username(type="text" name="username" required) + div.field + label(for="password")=__("password")+":" + input#password(type="password" name="password" required) + div.field + +submit(value=__("login.submit")) + div.field - label(for="username")=__("username")+":" - input#username(type="text" name="username" required) - div.field - label(for="password")=__("password")+":" - input#password(type="password" name="password" required) - div.field - +submit(value=__("login.submit")) + a(href="/forget") + input(type="button" value=__("forgetPassword"))