From 78d0446dc2fd9e2b5d23ff9a9c273d4378d3b133 Mon Sep 17 00:00:00 2001
From: airizarryOSU <67487429+airizarryOSU@users.noreply.github.com>
Date: Thu, 30 Jul 2020 16:48:07 -0400
Subject: [PATCH 1/3] added files for password reset
---
index.js | 11 ++++++++
package-lock.json | 11 +++-----
public/css/styles.css | 58 +++++++++++++++++++++++++++++++++++++++++
reset.js | 15 +++++++++++
views/reset.handlebars | 20 ++++++++++++++
views/signup.handlebars | 3 +++
6 files changed, 110 insertions(+), 8 deletions(-)
create mode 100644 reset.js
create mode 100644 views/reset.handlebars
diff --git a/index.js b/index.js
index 0b36bd6..365b854 100644
--- a/index.js
+++ b/index.js
@@ -71,6 +71,8 @@ app.use('/task', require('./task.js'));
app.use('/mytasks', require('./myTasks.js'));
+app.use('/pass/reset', require('./reset.js'));
+
app.get('/', function(req, res, next) {
var context = {};
res.render('signup', context);
@@ -81,6 +83,15 @@ app.get('/login', function(req, res, next) {
res.render('login', context);
});
+app.get('/reset', function(req, res, next) {
+ const context = { email: ''};
+ res.render('reset', context);
+});
+
+app.post('/pass/reset', function(req, res) {
+ console.log(req.body.user_email);
+});
+
app.post('/add-new-user', function(req, res) {
mysql.pool.query(
"SELECT id from users where email=" + mysql.pool.escape(req.body.user_email),
diff --git a/package-lock.json b/package-lock.json
index 6d2d77b..412b655 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,7 +44,6 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
"integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
- "optional": true,
"requires": {
"kind-of": "^3.0.2",
"longest": "^1.0.1",
@@ -805,8 +804,7 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
- "optional": true
+ "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4="
},
"is-ci": {
"version": "2.0.0",
@@ -951,7 +949,6 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
@@ -1009,8 +1006,7 @@
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
- "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
- "optional": true
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
},
"lowercase-keys": {
"version": "1.0.1",
@@ -1402,8 +1398,7 @@
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
- "optional": true
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
},
"responselike": {
"version": "1.0.2",
diff --git a/public/css/styles.css b/public/css/styles.css
index 86cb634..e41199f 100644
--- a/public/css/styles.css
+++ b/public/css/styles.css
@@ -181,6 +181,64 @@ body {
margin-bottom: 30px;
}
+/* reset page CSS */
+#reset-page #danger {
+ color: red;
+}
+
+#reset-page * {
+ box-sizing: border-box;
+}
+
+#reset-page {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+#reset-page .form-content {
+ width: 50%;
+ max-width: 600px;
+ background-color: white;
+ border-radius: 3px;
+ border: 1px solid #808080;
+ padding: 2% 5%;
+}
+
+#reset-page label {
+ margin: 10px 0;
+ display: block;
+}
+
+#reset-page input {
+ background: none;
+ border: 1px solid #808080;
+ border-radius: 0px;
+ width: 100%;
+ padding: 10px;
+}
+
+#reset-page .button.button-block {
+ display: block;
+ margin-top: 20px;
+ background: #fcbd01;
+ border-color: #fcbd01;
+ color: #fff;
+ font-size: 0.9em;
+ font-weight: 900;
+ letter-spacing: 1px;
+}
+
+#reset-page .button.button-block:hover {
+ background: #fcb101;
+ border-color: #fcb101;
+}
+
+#reset_form .input-group:last-of-type {
+ margin-bottom: 30px;
+}
+
/* buttons */
.btn.btn-primary {
diff --git a/reset.js b/reset.js
new file mode 100644
index 0000000..8e45d35
--- /dev/null
+++ b/reset.js
@@ -0,0 +1,15 @@
+//Was trying to put all the password reset stuff here, but was unable to grab it
+//so its still currently in index.js
+
+//const express = require('express');
+//const mysql = require('./dbcon.js');
+//const router = express.Router();
+//
+//router.post('/pass/reset', function(req, res) {
+//
+//console.log(req.body);
+//
+//
+//});
+//
+//module.exports = router;
\ No newline at end of file
diff --git a/views/reset.handlebars b/views/reset.handlebars
new file mode 100644
index 0000000..905afa6
--- /dev/null
+++ b/views/reset.handlebars
@@ -0,0 +1,20 @@
+
+
+
Enter Email Address
+
+
+
\ No newline at end of file
diff --git a/views/signup.handlebars b/views/signup.handlebars
index db6e749..4d24bdf 100644
--- a/views/signup.handlebars
+++ b/views/signup.handlebars
@@ -40,6 +40,9 @@
Already have an account? Login
+
+
Forgot your password? Reset
+
\ No newline at end of file
From 721d4a7e284daf8dd439f00a4389bb5f2f388797 Mon Sep 17 00:00:00 2001
From: Sarah Forest
Date: Thu, 30 Jul 2020 20:58:27 -0400
Subject: [PATCH 2/3] password reset functionality
---
EC3_DDL.sql | 6 +-
index.js | 157 ++++++++++++++++++++++++++++++++++++++--
package-lock.json | 16 +++-
package.json | 3 +-
public/css/styles.css | 26 +++++--
views/forgot.handlebars | 27 +++++++
views/login.handlebars | 3 +
views/reset.handlebars | 41 ++++++-----
views/signup.handlebars | 3 -
9 files changed, 242 insertions(+), 40 deletions(-)
create mode 100644 views/forgot.handlebars
diff --git a/EC3_DDL.sql b/EC3_DDL.sql
index 56e8860..6857e42 100644
--- a/EC3_DDL.sql
+++ b/EC3_DDL.sql
@@ -15,7 +15,9 @@ CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
-`password` varchar(500) DEFAULT NULL
+`password` varchar(500) DEFAULT NULL,
+`token` varchar(255) DEFAULT NULL,
+`tokenExpiration` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `users`
@@ -77,4 +79,4 @@ INSERT INTO `tasks` (`project_id`, `name`, `assignee_id`, `due_date`, `status`,
(2, 'Create template files.', 2, '2020-07-10', 'To Do', 'Use stagehand.');
INSERT INTO `subtasks` (`project_id`, `task_id`, `name`, `assignee_id`, `due_date`, `status`, `description`) VALUES
-(2, 1, 'sample subtask', 1, '2020-07-15', 'To Do', 'sample subtask description');
+(2, 1, 'sample subtask', 1, '2020-07-15', 'To Do', 'sample subtask description');
\ No newline at end of file
diff --git a/index.js b/index.js
index 365b854..6b236cb 100644
--- a/index.js
+++ b/index.js
@@ -3,6 +3,8 @@
var express = require('express');
var bodyParser = require('body-parser');
var CryptoJS = require("crypto-js");
+var crypto = require("crypto");
+var nodemailer = require('nodemailer');
var path = require('path');
var session = require('express-session');
@@ -71,7 +73,7 @@ app.use('/task', require('./task.js'));
app.use('/mytasks', require('./myTasks.js'));
-app.use('/pass/reset', require('./reset.js'));
+//app.use('/pass/reset', require('./reset.js'));
app.get('/', function(req, res, next) {
var context = {};
@@ -83,13 +85,158 @@ app.get('/login', function(req, res, next) {
res.render('login', context);
});
-app.get('/reset', function(req, res, next) {
+app.get('/forgot', function(req, res, next) {
const context = { email: ''};
- res.render('reset', context);
+ res.render('forgot', context);
});
-app.post('/pass/reset', function(req, res) {
- console.log(req.body.user_email);
+
+function getToken(res, user, complete) {
+ crypto.randomBytes(20, function(err, buf) {
+ var token = buf.toString('hex');
+ user.resetPasswordToken = token;
+ complete();
+ });
+}
+
+function checkIfUserExists(email, res, user, complete) {
+ mysql.pool.query(
+ "SELECT id from users where email=" + mysql.pool.escape(email),
+ function(err, result) {
+ // if error, handle by outputting issue encountered
+ if (err) {
+ console.log(JSON.stringify(err));
+ res.write(JSON.stringify(err));
+ res.end();
+ }
+ // if id exists in the db, user already exists, don't proceed with sign up
+ else if (!result[0] || !result[0].id) {
+ console.log("Doesnt exist")
+ res.render('forgot', {
+ errors: 'Email address doesn\'t exist.',
+ });
+ }
+ else {
+ user.email = email;
+ user.id = result[0].id;
+ user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
+ complete();
+ }
+ })
+}
+
+function updateUserTokens(res, user, complete) {
+ var sql = "UPDATE users SET token = ?, tokenExpiration = ? WHERE id = ?";
+ var inserts = [user.resetPasswordToken, user.resetPasswordExpires, user.id];
+ sql = mysql.pool.query(sql, inserts, function(error, results, fields){
+ if(error){
+ res.write(JSON.stringify(error));
+ res.status(400);
+ res.end();
+ }
+ complete();
+ })
+}
+
+app.post('/pass/reset', function(req, res, next) {
+ var callbackCount = 0;
+ var user = {};
+
+ getToken(res, user, complete);
+ function complete(){
+ callbackCount++;
+ if(callbackCount == 1){
+ checkIfUserExists(req.body.user_email, res, user, complete);
+ } else if (callbackCount == 2) {
+ updateUserTokens(res, user, complete);
+ } else if (callbackCount == 3) {
+
+ const transporter = nodemailer.createTransport({
+ service: 'gmail',
+ auth: {
+ user: 'cs361osu@gmail.com',
+ pass: 'superlongstringtest123'
+ }
+ });
+
+ const mailOptions = {
+ from: 'admin@ec3taskmanagement.com',
+ to: user.email,
+ subject: 'Password Reset Requested',
+ text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
+ 'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
+ 'http://' + req.headers.host + '/reset/' + user.resetPasswordToken + '\n\n' +
+ 'If you did not request this, please ignore this email and your password will remain unchanged.\n'
+ };
+
+ transporter.sendMail(mailOptions, function(error, info) {
+ if (error) {
+ console.log(error);
+ } else {
+ console.log('Email sent: ' + info.response);
+ }
+ });
+ res.render('forgot', {
+ info: 'An e-mail has been sent to ' + user.email + ' with further instructions.',
+ });
+ }
+ }
+});
+
+
+app.get('/reset/:token', function(req, res) {
+
+ var sql = "SELECT id, tokenExpiration from users WHERE token = ?";
+ var inserts = [req.params.token];
+ sql = mysql.pool.query(sql, inserts, function(error, result, fields){
+ if(error){
+ res.write(JSON.stringify(error));
+ res.status(400);
+ res.end();
+ }
+ else if (!result[0] || !result[0].id) {
+
+ res.render('reset', {
+ errors: 'Password reset token is invalid.',
+ link: 'http://' + req.headers.host + '/forgot'
+ });
+ }
+ else if (Date.now() > result[0].tokenExpiration) {
+ res.render('reset', {
+ errors: 'Password reset token is expired.',
+ link: 'http://' + req.headers.host + '/forgot'
+ });
+ }
+ else {
+ var context = {
+ id: result[0].id,
+ token: Date.now(),
+ showForm: true
+ };
+ res.render('reset', context)
+ }
+ })
+});
+
+app.post('/reset-password', function(req, res, next) {
+
+ var ciphertext = CryptoJS.AES.encrypt(req.body.password, config.cryptoSecret).toString();
+
+ var sql = "UPDATE users SET password = ?, tokenExpiration = ? WHERE id = ?";
+ var inserts = [ciphertext, req.body.token, req.body.id];
+
+ sql = mysql.pool.query(sql, inserts, function(error, result, fields){
+ if(error){
+ res.write(JSON.stringify(error));
+ res.status(400);
+ res.end();
+ }
+
+ res.render('reset', {
+ info: 'Password reset successfully.',
+ link: 'http://' + req.headers.host + '/login'
+ });
+ })
});
app.post('/add-new-user', function(req, res) {
diff --git a/package-lock.json b/package-lock.json
index 412b655..b2d2700 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,6 +44,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
"integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "optional": true,
"requires": {
"kind-of": "^3.0.2",
"longest": "^1.0.1",
@@ -804,7 +805,8 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4="
+ "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
+ "optional": true
},
"is-ci": {
"version": "2.0.0",
@@ -949,6 +951,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
@@ -1006,7 +1009,8 @@
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
- "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "optional": true
},
"lowercase-keys": {
"version": "1.0.1",
@@ -1109,6 +1113,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs="
},
+ "nodemailer": {
+ "version": "6.4.11",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz",
+ "integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ=="
+ },
"nodemon": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz",
@@ -1398,7 +1407,8 @@
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "optional": true
},
"responselike": {
"version": "1.0.2",
diff --git a/package.json b/package.json
index 119a960..53b7144 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,8 @@
"express-handlebars": "^2.0.1",
"express-session": "^1.17.0",
"jsonwebtoken": "^8.5.1",
- "mysql": "^2.8.0"
+ "mysql": "^2.8.0",
+ "nodemailer": "^6.4.11"
},
"bugs": {
"url": "https://github.com/sarahforest/cs361/issues"
diff --git a/public/css/styles.css b/public/css/styles.css
index e41199f..121ca75 100644
--- a/public/css/styles.css
+++ b/public/css/styles.css
@@ -186,18 +186,18 @@ body {
color: red;
}
-#reset-page * {
+#reset-page *, #forgot-page * {
box-sizing: border-box;
}
-#reset-page {
+#reset-page, #forgot-page {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
-#reset-page .form-content {
+#reset-page .form-content, #forgot-page .form-content {
width: 50%;
max-width: 600px;
background-color: white;
@@ -206,12 +206,12 @@ body {
padding: 2% 5%;
}
-#reset-page label {
+#reset-page label, #forgot-page label {
margin: 10px 0;
display: block;
}
-#reset-page input {
+#reset-page input, #forgot-page input {
background: none;
border: 1px solid #808080;
border-radius: 0px;
@@ -219,7 +219,7 @@ body {
padding: 10px;
}
-#reset-page .button.button-block {
+#reset-page .button.button-block, #forgot-page .button.button-block {
display: block;
margin-top: 20px;
background: #fcbd01;
@@ -230,12 +230,12 @@ body {
letter-spacing: 1px;
}
-#reset-page .button.button-block:hover {
+#reset-page .button.button-block:hover, #forgot-page .button.button-block:hover {
background: #fcb101;
border-color: #fcb101;
}
-#reset_form .input-group:last-of-type {
+#reset_form .input-group:last-of-type, #forgot-page .input-group:last-of-type {
margin-bottom: 30px;
}
@@ -284,3 +284,13 @@ body {
border-top: 4px solid #fcbd01;
}
+#login-page .login-section {
+ color: #FFB703;
+}
+
+#login-page .login-section a, #forgot-page a, #reset-page a {
+ color: #FFB703 !important;
+ text-decoration: underline !important;
+ cursor: pointer !important;
+}
+
diff --git a/views/forgot.handlebars b/views/forgot.handlebars
new file mode 100644
index 0000000..e187076
--- /dev/null
+++ b/views/forgot.handlebars
@@ -0,0 +1,27 @@
+
+
+
Enter Email Address
+ {{#if errors}}
+
{{errors}}
+ {{/if}}
+ {{#if info}}
+
{{info}}
+ {{else}}
+
+ {{/if}}
+
+
\ No newline at end of file
diff --git a/views/login.handlebars b/views/login.handlebars
index f6d0b19..f6565fa 100644
--- a/views/login.handlebars
+++ b/views/login.handlebars
@@ -29,5 +29,8 @@
+
+
Forgot your password? Reset
+
\ No newline at end of file
diff --git a/views/reset.handlebars b/views/reset.handlebars
index 905afa6..47cfc8d 100644
--- a/views/reset.handlebars
+++ b/views/reset.handlebars
@@ -1,20 +1,25 @@
-
-
Enter Email Address
-
-
+
+
Reset Password
+
+ {{#if errors}}
+
+ {{/if}}
+ {{#if info}}
+
+ {{/if}}
+ {{#if showForm}}
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/views/signup.handlebars b/views/signup.handlebars
index 4d24bdf..db6e749 100644
--- a/views/signup.handlebars
+++ b/views/signup.handlebars
@@ -40,9 +40,6 @@
Already have an account? Login
-
-
Forgot your password? Reset
-
\ No newline at end of file
From 843803c2ea90510077baa4393611f5da98604d56 Mon Sep 17 00:00:00 2001
From: airizarryOSU <67487429+airizarryOSU@users.noreply.github.com>
Date: Fri, 31 Jul 2020 18:12:18 -0400
Subject: [PATCH 3/3] Delete reset.js
File no longer needed
---
reset.js | 15 ---------------
1 file changed, 15 deletions(-)
delete mode 100644 reset.js
diff --git a/reset.js b/reset.js
deleted file mode 100644
index 8e45d35..0000000
--- a/reset.js
+++ /dev/null
@@ -1,15 +0,0 @@
-//Was trying to put all the password reset stuff here, but was unable to grab it
-//so its still currently in index.js
-
-//const express = require('express');
-//const mysql = require('./dbcon.js');
-//const router = express.Router();
-//
-//router.post('/pass/reset', function(req, res) {
-//
-//console.log(req.body);
-//
-//
-//});
-//
-//module.exports = router;
\ No newline at end of file