From 555d44220b016978cd9cbddf8d1ac6bb84a1f142 Mon Sep 17 00:00:00 2001 From: Bobby Date: Fri, 24 Jun 2022 21:53:20 +0530 Subject: basic email validation on account page --- functions/validate.js | 26 ++++++ package-lock.json | 14 +++ package.json | 1 + routes/account.routes.js | 219 +++++++++++++++++++++++++++++++++++------------ views/account.ejs | 19 +++- views/error.ejs | 38 +++++--- 6 files changed, 247 insertions(+), 70 deletions(-) diff --git a/functions/validate.js b/functions/validate.js index ecf90564..3e9ff111 100644 --- a/functions/validate.js +++ b/functions/validate.js @@ -15,6 +15,32 @@ function validateAuthorization(auth) { } } +var emailRegex = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/; + +function isEmailValid(email) { + if (!email) + return false; + + if(email.length>254) + return false; + + var valid = emailRegex.test(email); + if(!valid) + return false; + + // Further checking of some things regex can't handle + var parts = email.split("@"); + if(parts[0].length>64) + return false; + + var domainParts = parts[1].split("."); + if(domainParts.some(function(part) { return part.length>63; })) + return false; + + return true; +} + module.exports = { validateAuthorization, + isEmailValid }; diff --git a/package-lock.json b/package-lock.json index 17e83ffa..41bf5f09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "mysql2": "^2.3.3", "node-cron": "^3.0.1", "node-fetch": "^3.2.5", + "nodemailer": "^6.7.5", "uuid": "^8.3.2" }, "devDependencies": { @@ -1588,6 +1589,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/nodemailer": { + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz", + "integrity": "sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.16.tgz", @@ -3597,6 +3606,11 @@ "formdata-polyfill": "^4.0.10" } }, + "nodemailer": { + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz", + "integrity": "sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==" + }, "nodemon": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.16.tgz", diff --git a/package.json b/package.json index d20de2f3..73edc846 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "mysql2": "^2.3.3", "node-cron": "^3.0.1", "node-fetch": "^3.2.5", + "nodemailer": "^6.7.5", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/routes/account.routes.js b/routes/account.routes.js index f2f72e29..d7cbdfc5 100644 --- a/routes/account.routes.js +++ b/routes/account.routes.js @@ -4,70 +4,183 @@ const mysql = require("mysql2"); const jwt = require("jsonwebtoken"); const connectionString = process.env.DATABASE_URL; const md5 = require("md5"); +const { isEmailValid } = require("../functions/validate"); -router.get('/', (req, res) => { - const username = jwt.decode(req.cookies.token).username; - const connection = mysql.createConnection(connectionString); - connection.connect(); - const sql = "SELECT * FROM Profiles WHERE username = ?"; - connection.query(sql, [username], (err, results, fields) => { - if (err) { - res.status(500).render('error', { - error: err, - }); +router.get("/", (req, res) => { + const username = jwt.decode(req.cookies.token).username; + const connection = mysql.createConnection(connectionString); + connection.connect(); + const sql = "SELECT * FROM Profiles WHERE username = ?"; + connection.query(sql, [username], (err, results, fields) => { + if (err) { + res.status(500).render("error", { + error: err, + }); + } else { + if (results.length > 0) { + const user = results[0]; + renderRoute(req, res, "account", "My Account", true, { + user: { + ...user, + avatar: md5(user.gravatarEmail || user.email || ""), + url: + user.public == 1 + ? `${req.protocol + "://" + req.get("host")}/profile/${ + user.username + }` + : "", + }, + }); + } else { + renderRoute(req, res, "account", "My Account", true, { + user: null, + }); + } + } + }); + connection.end(); +}); + +router.post("/sendVerificationEmail", (req, res) => { + jwt.verify( + req.cookies.token, + process.env.AUTHORIZATION_STRING, + (err, decoded) => { + if (err) { + renderRoute(req, res, "error", "Error", false, { + error: err, + }); + } else { + const username = decoded.username; + const newEmail = req.body.email; + if (!newEmail || !isEmailValid(newEmail)) { + req.flash("mailsenderror", "Error sending verification email. Provided email is invalid.",); + res.redirect(req.get("referer")); } else { - if (results.length > 0) { - const user = results[0]; - renderRoute(req, res, 'account', 'My Account', true, { - user: { - ...user, - avatar: md5(user.email || '') - } - }); + const connection = mysql.createConnection(connectionString); + connection.connect(); + const sql = "SELECT * FROM Profiles WHERE username = ?"; + connection.query(sql, [username], (err, results, fields) => { + if (err) { + renderRoute(req, res, "error", "Error", false, { + error: err.message, + }); } else { - renderRoute(req, res, 'account', 'My Account', true, { - user: null + if (results.length > 0) { + const user = results[0]; + // const transporter = require("nodemailer").createTransport({ + // service: "gmail", + // auth: { + // user: process.env.EMAIL_USER, + // pass: process.env.EMAIL_PASSWORD, + // }, + // }); + // // Generate a verification URL and send it to the user + const verificationUrl = `${req.get( + "origin" + )}/verifyemail?token=${jwt.sign( + { + username: user.username, + email: newEmail, + }, + process.env.AUTHORIZATION_STRING, + { + expiresIn: "1h", + } + )}`; + // const mailOptions = { + // from: process.env.EMAIL_USER, + // to: newEmail, + // priority: "high", + // subject: + // "[That Computer Scientist] Request to change your email address", + // html: `

Hi ${user.firstname || user.username},

+ //

We received a request to change your email address to ${newEmail}.

+ //

If you made this request, please click the link below to verify your new email address:

+ //

${verificationUrl}

+ //

If you did not make this request, you can ignore this email.

+ //

Thanks,

+ //

Kumar Priyansh

+ //

That Computer Scientist

`, + // }; + // transporter.sendMail(mailOptions, (err, info) => { + // if (err) { + // req.flash("mailsenderror", "Error sending verification email. Please try again later.",); + // req.redirect(req.get("referer")); + // } else { + req.flash( + "mailsendsuccess", + `Verification email sent! The link expires in 1 hour. Please check your email. Also, make sure to check your spam folder. Verification URL: ${verificationUrl}` + ); + res.redirect(req.get("referer")); + // } + // }); + } else { + res.status(500).render("error", { + error: "User not found", }); + } } + }); + connection.end(); } - }); - connection.end(); + } + } + ); }); -router.post('/updateAccount', (req, res) => { - jwt.verify(req.cookies.token, process.env.AUTHORIZATION_STRING, (err, decoded) => { - if (err) { - renderRoute(req, res, 'error', 'Error', false) - } else { - const username = decoded.username; - const firstname = req.body.firstname; - const lastname = req.body.lastname; - const location = req.body.location; - const bio = req.body.bio; - const public = req.body.isPublic; - const emailPublic = req.body.emailPublic; - const connection = mysql.createPool(connectionString); - connection.getConnection((err, connection) => { +router.post("/updateAccount", (req, res) => { + jwt.verify( + req.cookies.token, + process.env.AUTHORIZATION_STRING, + (err, decoded) => { + if (err) { + renderRoute(req, res, "error", "Error", false); + } else { + const username = decoded.username; + const firstname = req.body.firstname; + const lastname = req.body.lastname; + const location = req.body.location; + const bio = req.body.bio; + const gravatarEmail = req.body.gravatarEmail; + const public = req.body.isPublic; + const emailPublic = req.body.emailPublic || 0; + const connection = mysql.createPool(connectionString); + connection.getConnection((err, connection) => { + if (err) { + renderRoute(req, res, "error", "Error", false, { + error: err.message, + }); + } else { + const sql = + "UPDATE Profiles SET firstname = ?, lastname = ?, location = ?, bio = ?, gravatarEmail = ?, public = ?, emailPublic = ? WHERE username = ?"; + connection.query( + sql, + [ + firstname, + lastname, + location, + bio, + gravatarEmail, + public, + emailPublic, + username, + ], + (err, results, fields) => { if (err) { - renderRoute(req, res, 'error', 'Error', false, { - error: err.message - }); + req.flash("updateaccerror", err.message); + res.redirect(req.get("referer")); } else { - const sql = "UPDATE Profiles SET firstname = ?, lastname = ?, location = ?, bio = ?, public = ?, emailPublic = ? WHERE username = ?"; - connection.query(sql, [firstname, lastname, location, bio, public, emailPublic, username], (err, results, fields) => { - if (err) { - req.flash('updateaccerror', err.message); - res.redirect(req.get('referer')); - } else { - req.flash('updateaccsuccess', 'Account updated successfully'); - res.redirect(req.get('referer')); - } - }); + req.flash("updateaccsuccess", "Account updated successfully"); + res.redirect(req.get("referer")); } - }); - } - }); + } + ); + } + }); + } + } + ); }); - module.exports = router; diff --git a/views/account.ejs b/views/account.ejs index 6649eac4..06e4ca9e 100644 --- a/views/account.ejs +++ b/views/account.ejs @@ -10,9 +10,9 @@
  • Do not send multiple support requests.
  • Please note that this is a support request related to your account. Please do not file any bugs here. If you have noticed a bug, please report it to the GitHub Issues page.
  • -

    Your avatar is fetched from gravatar. Update your gravatar email to fetch the avatar. If you don't have an account, you can sign up for one here.

    - <% if (user.public == 1) { %> -

    Your account is publicly accessible at: .

    +

    Your avatar is fetched from gravatar. Update your gravatar email to fetch the avatar. If you don't have an account, you can sign up for one here. If you haven't set up your gravatar email, we would try to fetch your profile picture from your account email, by default. If your account email and gravatar email are the same, you do not need to set a gravatar email.

    + <% if (user.url !== "") { %> +

    Your account is publicly accessible at: <%= user.url %>

    <% } %>
    @@ -51,6 +51,8 @@ + + @@ -76,12 +78,21 @@ <% } %> -
    +
    Change Email + <% if (locals.messages.mailsenderror) { %> +

    <%= messages.mailsenderror %>

    + <% } %> + <% if (locals.messages.mailsendsuccess) { %> +

    <%= messages.mailsendsuccess %>

    + + <% } %>
    diff --git a/views/error.ejs b/views/error.ejs index d11cc4be..16a9f991 100644 --- a/views/error.ejs +++ b/views/error.ejs @@ -1,14 +1,26 @@ -<%- include('partials/header.ejs') %> <%- include('partials/sidebar.ejs') %> -
    -
    -

    error

    -

    - <%= error %> -

    -
    -
    -

    Recent Posts

    -
    -
    + + + + + + + + That Computer Scientist - <%= title %> + + + + +
    +
    +

    Whoops! Encountered an error!

    +

    <%= error %>

    +

    Please click here to go back to the home page.

    +
    +
    -<%- include('partials/footer.ejs') %> + <%- include('partials/footer.ejs') %> + + -- cgit v1.2.3