Added rate limiter on customer forgotten password
parent
c086c942bc
commit
c992673bc6
|
@ -3598,6 +3598,11 @@
|
||||||
"promise": "^8.0.2"
|
"promise": "^8.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-rate-limit": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-dhT57wqxfqmkOi4HM7NuT4Gd7gbUgSK2ocG27Y6lwm8lbOAw9XQfeANawGq8wLDtlGPO1ZgDj0HmKsykTxfFAg=="
|
||||||
|
},
|
||||||
"express-session": {
|
"express-session": {
|
||||||
"version": "1.17.0",
|
"version": "1.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz",
|
||||||
|
@ -5058,9 +5063,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.4.5",
|
"version": "4.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
|
||||||
"integrity": "sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==",
|
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
|
@ -9223,9 +9228,9 @@
|
||||||
"integrity": "sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ=="
|
"integrity": "sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ=="
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.6.4",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz",
|
||||||
"integrity": "sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==",
|
"integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"commander": "~2.20.3",
|
"commander": "~2.20.3",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"cookie-parser": "^1.4.4",
|
"cookie-parser": "^1.4.4",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-handlebars": "^3.1.0",
|
"express-handlebars": "^3.1.0",
|
||||||
|
"express-rate-limit": "^5.0.0",
|
||||||
"express-session": "^1.17.0",
|
"express-session": "^1.17.0",
|
||||||
"glob": "^7.1.5",
|
"glob": "^7.1.5",
|
||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
|
|
|
@ -143,6 +143,29 @@ $(document).ready(function (){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#customerForgotten').validator().on('submit', function(e){
|
||||||
|
if(!e.isDefaultPrevented()){
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/customer/forgotten_action',
|
||||||
|
data: {
|
||||||
|
email: $('#email').val()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function(msg){
|
||||||
|
showNotification(msg.message, 'success');
|
||||||
|
})
|
||||||
|
.fail(function(msg){
|
||||||
|
if(msg.message){
|
||||||
|
showNotification(msg.responseJSON.message, 'danger');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showNotification(msg.responseText, 'danger');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#createCustomerAccount').validator().on('click', function(e){
|
$('#createCustomerAccount').validator().on('click', function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if($('#shipping-form').validator('validate').has('.has-error').length === 0){
|
if($('#shipping-form').validator('validate').has('.has-error').length === 0){
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,10 +4,16 @@ const colors = require('colors');
|
||||||
const randtoken = require('rand-token');
|
const randtoken = require('rand-token');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const common = require('../lib/common');
|
const common = require('../lib/common');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
const { indexCustomers } = require('../lib/indexing');
|
const { indexCustomers } = require('../lib/indexing');
|
||||||
const { validateJson } = require('../lib/schema');
|
const { validateJson } = require('../lib/schema');
|
||||||
const { restrict } = require('../lib/auth');
|
const { restrict } = require('../lib/auth');
|
||||||
|
|
||||||
|
const apiLimiter = rateLimit({
|
||||||
|
windowMs: 300000, // 5 minutes
|
||||||
|
max: 5
|
||||||
|
});
|
||||||
|
|
||||||
// insert a customer
|
// insert a customer
|
||||||
router.post('/customer/create', async (req, res) => {
|
router.post('/customer/create', async (req, res) => {
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
|
@ -281,21 +287,21 @@ router.get('/customer/forgotten', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// forgotten password
|
// forgotten password
|
||||||
router.post('/customer/forgotten_action', async (req, res) => {
|
router.post('/customer/forgotten_action', apiLimiter, async (req, res) => {
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
const config = req.app.config;
|
const config = req.app.config;
|
||||||
const passwordToken = randtoken.generate(30);
|
const passwordToken = randtoken.generate(30);
|
||||||
|
|
||||||
// find the user
|
// find the user
|
||||||
const customer = await db.customers.findOne({ email: req.body.email });
|
const customer = await db.customers.findOne({ email: req.body.email });
|
||||||
// if we have a customer, set a token, expiry and email it
|
|
||||||
if(!customer){
|
|
||||||
req.session.message = 'Account does not exist';
|
|
||||||
req.session.message_type = 'danger';
|
|
||||||
res.redirect('/customer/forgotten');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try{
|
try{
|
||||||
|
if(!customer){
|
||||||
|
// if don't have an email on file, silently fail
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'If your account exists, a password reset has been sent to your email'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const tokenExpiry = Date.now() + 3600000;
|
const tokenExpiry = Date.now() + 3600000;
|
||||||
await db.customers.updateOne({ email: req.body.email }, { $set: { resetToken: passwordToken, resetTokenExpiry: tokenExpiry } }, { multi: false });
|
await db.customers.updateOne({ email: req.body.email }, { $set: { resetToken: passwordToken, resetTokenExpiry: tokenExpiry } }, { multi: false });
|
||||||
// send forgotten password email
|
// send forgotten password email
|
||||||
|
@ -311,13 +317,13 @@ router.post('/customer/forgotten_action', async (req, res) => {
|
||||||
// send the email with token to the user
|
// send the email with token to the user
|
||||||
// TODO: Should fix this to properly handle result
|
// TODO: Should fix this to properly handle result
|
||||||
common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body);
|
common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body);
|
||||||
req.session.message = 'An email has been sent to ' + req.body.email + ' with further instructions';
|
res.status(200).json({
|
||||||
req.session.message_type = 'success';
|
message: 'If your account exists, a password reset has been sent to your email'
|
||||||
res.redirect('/customer/forgotten');
|
});
|
||||||
}catch(ex){
|
}catch(ex){
|
||||||
req.session.message = 'Account does not exist';
|
res.status(400).json({
|
||||||
req.session.message_type = 'danger';
|
message: 'Password reset failed.'
|
||||||
res.redirect('/customer/forgotten');
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<div class="col-md-offset-4 col-md-4 col-lg-offset-4 col-lg-4" style="padding-top: 100px" >
|
<div class="col-md-offset-4 col-md-4 col-lg-offset-4 col-lg-4" style="padding-top: 100px" >
|
||||||
<form class="form-signin" action="/{{route}}/forgotten_action" method="post" role="form" data-toggle="validator">
|
<form class="form-signin" id="customerForgotten" data-toggle="validator">
|
||||||
<h2 class="form-signin-heading">{{ @root.__ "Please enter your email address" }}</h2>
|
<h2 class="form-signin-heading">{{ @root.__ "Please enter your email address" }}</h2>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="email" name="email" class="form-control" placeholder="email address" required autofocus>
|
<input type="email" id="email" class="form-control" placeholder="email address" required autofocus>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ @root.__ "Reset" }}</button>
|
<button class="btn btn-lg btn-primary btn-block">{{ @root.__ "Reset" }}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue