This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
68 lines
2.4 KiB
68 lines
2.4 KiB
import { API } from "../lib/api.js";
|
|
import logging from "../lib/logging.js";
|
|
import assert from "assert";
|
|
import { User } from "../lib/models.js";
|
|
import { send_reset, send_reset_finished } from '../lib/queues.js';
|
|
import differenceInHours from 'date-fns/differenceInHours/index.js';
|
|
|
|
const rules_request = {
|
|
email: 'required|email',
|
|
}
|
|
|
|
const rules_finish = {
|
|
code: 'required',
|
|
password: 'required|same:password_repeat',
|
|
password_repeat: 'required',
|
|
}
|
|
|
|
const log = logging.create("/api/password_reset.js");
|
|
// this doesn't change so just do it here
|
|
|
|
const RESET_MAX = 10;
|
|
const RESET_HOURS_MAX = 10;
|
|
|
|
export const post = async (req, res) => {
|
|
const api = new API(req, res);
|
|
const finalize = req.body.finalize;
|
|
const rules = finalize ? rules_finish : rules_request
|
|
const form = api.validate(rules);
|
|
|
|
try {
|
|
if(!form._valid) {
|
|
// abort with errors
|
|
return api.validation_error(res, form);
|
|
} else {
|
|
assert(form._valid, "Form is invalid but shouldn't be.");
|
|
const { email } = form;
|
|
const user = await User.first({email});
|
|
|
|
if(!user) {
|
|
return api.error(400, "User with that email doesn't exist.");
|
|
} else if(finalize) {
|
|
const { code, password } = form;
|
|
|
|
if(user.reset_count > RESET_MAX) {
|
|
return api.error(400, "Too many reset attempts in a short time. Contact support to unlock your account.");
|
|
} else if(user.reset_code !== code.trim()) {
|
|
await User.update({id: user.id}, { reset_count: user.reset_count + 1 });
|
|
return api.error(400, "Reset code doesn't match.");
|
|
} else {
|
|
// SECURITY: encrypt the password here so that it's not left in the queue unencrypted
|
|
user.password = User.encrypt_password(password);
|
|
send_reset_finished(user, req.ip, req.headers['user-agent']);
|
|
return api.reply(200, {message: "OK"});
|
|
}
|
|
} else if(user.reset_sent_at && differenceInHours(Date.now(), user.reset_sent_at) < RESET_HOURS_MAX) {
|
|
return api.error(400, `Only one request every ${RESET_HOURS_MAX} hours allowed.`);
|
|
} else {
|
|
// new request, send out a code
|
|
const user_req = { email, id: user.id};
|
|
send_reset(user_req, req.ip, req.headers['user-agent']);
|
|
return api.reply(200, {message: "OK"});
|
|
}
|
|
}
|
|
} catch(error) {
|
|
log.error(error);
|
|
return api.error(500, "Internal Server Error");
|
|
}
|
|
}
|
|
|