reset password

Should check if can reset pass of another user
This commit is contained in:
2020-09-12 20:47:41 +02:00
parent 4d2d62d777
commit cec24fa01a
9 changed files with 160 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
export default function makeResetPassMessage(newPass) { export default function makeResetPassMessage(link) {
return { return {
html: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> html: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
@@ -36,7 +36,8 @@ export default function makeResetPassMessage(newPass) {
line-height: 1.6; line-height: 1.6;
} }
.logo { .logo {
margin: 24px; padding-top: 30px;
padding-bottom: 30px;
} }
.footer { .footer {
font-size: 13px; font-size: 13px;
@@ -50,14 +51,16 @@ export default function makeResetPassMessage(newPass) {
margin-bottom: 26px; margin-bottom: 26px;
color: #d68000; color: #d68000;
} }
.span { .link {
color: #262626; padding: 16px;
padding-top: 10px; font-size: 14px;
padding-bottom: 10px;
font-size: 16px;
font-weight: 700; font-weight: 700;
background-color: #d68000; background-color: #d68000;
} }
.link a {
color: #262626;
text-decoration: none;
}
</style> </style>
</head> </head>
<body bgcolor="#262626"> <body bgcolor="#262626">
@@ -72,15 +75,10 @@ export default function makeResetPassMessage(newPass) {
> >
<tr> <tr>
<td> <td>
<table align="center"> <table align="center" class="logo">
<tr> <tr>
<td> <td>
<img <img src="cid:logo" width="100" alt="Menui - food guide" />
class="logo"
src="cid:logo"
width="100"
alt="Menui - food guide"
/>
</td> </td>
</tr> </tr>
</table> </table>
@@ -107,12 +105,21 @@ export default function makeResetPassMessage(newPass) {
Drogi użytkowniku, dostałeś tę wiadomość, ponieważ użyłeś Drogi użytkowniku, dostałeś tę wiadomość, ponieważ użyłeś
opcji "Nie pamiętam hasła" w aplikacji Menui. opcji "Nie pamiętam hasła" w aplikacji Menui.
</p> </p>
<p>Kliknij w ten link, by ustawić nowe hasło:</p>
<table width="100%">
<tr>
<td>
<table width="30%" align="center" class="link">
<tr>
<td>
<a href="${link}">Resetuj hasło</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<p> <p>
Twoje tymczasowe hasło to:
<span><h5 class="span">${newPass}</h5></span>
</p>
<p>
Zaloguj się za jego pomocą i ustaw nowe bezpieczne hasło.
Jeżeli nie wysyłałeś prośby o zmianę hasła, prosimy zignoruj Jeżeli nie wysyłałeś prośby o zmianę hasła, prosimy zignoruj
tę wiadomość. tę wiadomość.
</p> </p>
@@ -134,7 +141,8 @@ export default function makeResetPassMessage(newPass) {
</tr> </tr>
</table> </table>
</body> </body>
</html>`, </html>
text: `Drogi użytkowniku, dostałeś tę wiadomość, ponieważ użyłeś opcji "Nie pamiętam hasła" w aplikacji Menui. Twoje tymczasowe hasło to: ${newPass}. Zaloguj się za jego pomocą i ustaw nowe bezpieczne hasło. Jeżeli nie wysyłałeś prośby o zmianę hasła, prosimy zignoruj tę wiadomość. Pozdrawiamy - Zespół Menui`, `,
text: `Drogi użytkowniku, dostałeś tę wiadomość, ponieważ użyłeś opcji "Nie pamiętam hasła" w aplikacji Menui. Twoje tymczasowe hasło to: ${link}. Zaloguj się za jego pomocą i ustaw nowe bezpieczne hasło. Jeżeli nie wysyłałeś prośby o zmianę hasła, prosimy zignoruj tę wiadomość. Pozdrawiamy - Zespół Menui`,
}; };
} }

11
node_modules/bl/bl.js generated vendored
View File

@@ -186,18 +186,22 @@ BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) {
if (bytes > l) { if (bytes > l) {
this._bufs[i].copy(dst, bufoff, start) this._bufs[i].copy(dst, bufoff, start)
bufoff += l
} else { } else {
this._bufs[i].copy(dst, bufoff, start, start + bytes) this._bufs[i].copy(dst, bufoff, start, start + bytes)
bufoff += l
break break
} }
bufoff += l
bytes -= l bytes -= l
if (start) if (start)
start = 0 start = 0
} }
// safeguard so that we don't return uninitialized memory
if (dst.length > bufoff) return dst.slice(0, bufoff)
return dst return dst
} }
@@ -233,6 +237,11 @@ BufferList.prototype.toString = function toString (encoding, start, end) {
} }
BufferList.prototype.consume = function consume (bytes) { BufferList.prototype.consume = function consume (bytes) {
// first, normalize the argument, in accordance with how Buffer does it
bytes = Math.trunc(bytes)
// do nothing if not a positive number
if (Number.isNaN(bytes) || bytes <= 0) return this
while (this._bufs.length) { while (this._bufs.length) {
if (bytes >= this._bufs[0].length) { if (bytes >= this._bufs[0].length) {
bytes -= this._bufs[0].length bytes -= this._bufs[0].length

68
node_modules/bl/package.json generated vendored
View File

@@ -1,21 +1,48 @@
{ {
"_from": "bl@2.2.1",
"_id": "bl@2.2.1",
"_inBundle": false,
"_integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"_location": "/bl",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "bl@2.2.1",
"name": "bl", "name": "bl",
"version": "2.2.0", "escapedName": "bl",
"description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!", "rawSpec": "2.2.1",
"main": "bl.js", "saveSpec": null,
"scripts": { "fetchSpec": "2.2.1"
"test": "node test/test.js | faucet"
}, },
"repository": { "_requiredBy": [
"type": "git", "/mongodb"
"url": "https://github.com/rvagg/bl.git" ],
}, "_resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"homepage": "https://github.com/rvagg/bl", "_shasum": "8c11a7b730655c5d56898cdc871224f40fd901d5",
"_spec": "bl@2.2.1",
"_where": "C:\\Users\\Jonasz\\Desktop\\Menui\\menui_backend\\node_modules\\mongodb",
"authors": [ "authors": [
"Rod Vagg <rod@vagg.org> (https://github.com/rvagg)", "Rod Vagg <rod@vagg.org> (https://github.com/rvagg)",
"Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)", "Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
"Jarett Cruger <jcrugzz@gmail.com> (https://github.com/jcrugzz)" "Jarett Cruger <jcrugzz@gmail.com> (https://github.com/jcrugzz)"
], ],
"bugs": {
"url": "https://github.com/rvagg/bl/issues"
},
"bundleDependencies": false,
"dependencies": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
},
"deprecated": false,
"description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!",
"devDependencies": {
"faucet": "0.0.1",
"hash_file": "~0.1.1",
"tape": "~4.9.0"
},
"homepage": "https://github.com/rvagg/bl",
"keywords": [ "keywords": [
"buffer", "buffer",
"buffers", "buffers",
@@ -23,17 +50,14 @@
"awesomesauce" "awesomesauce"
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "main": "bl.js",
"readable-stream": "^2.3.5", "name": "bl",
"safe-buffer": "^5.1.1" "repository": {
"type": "git",
"url": "git+https://github.com/rvagg/bl.git"
}, },
"devDependencies": { "scripts": {
"faucet": "0.0.1", "test": "node test/test.js | faucet"
"hash_file": "~0.1.1", },
"tape": "~4.9.0" "version": "2.2.1"
}
,"_resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz"
,"_integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA=="
,"_from": "bl@2.2.0"
} }

16
node_modules/bl/test/test.js generated vendored
View File

@@ -431,6 +431,22 @@ tape('test toString encoding', function (t) {
t.end() t.end()
}) })
tape('uninitialized memory', function (t) {
const secret = crypto.randomBytes(256)
for (let i = 0; i < 1e6; i++) {
const clone = Buffer.from(secret)
const bl = new BufferList()
bl.append(Buffer.from('a'))
bl.consume(-1024)
const buf = bl.slice(1)
if (buf.indexOf(clone) !== -1) {
t.fail(`Match (at ${i})`)
break
}
}
t.end()
})
!process.browser && tape('test stream', function (t) { !process.browser && tape('test stream', function (t) {
var random = crypto.randomBytes(65534) var random = crypto.randomBytes(65534)
, rndhash = hash(random, 'md5') , rndhash = hash(random, 'md5')

16
package-lock.json generated
View File

@@ -79,11 +79,6 @@
"version": "0.10.2", "version": "0.10.2",
"resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.10.2.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.10.2.tgz",
"integrity": "sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw==" "integrity": "sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
} }
} }
}, },
@@ -2299,9 +2294,9 @@
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
}, },
"bl": { "bl": {
"version": "2.2.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"requires": { "requires": {
"readable-stream": "^2.3.5", "readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
@@ -5905,6 +5900,11 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.1.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.1.tgz",
"integrity": "sha512-YUpjl57P55u2yUaKX5Bgy4t5s6SCNYMg+62XNg+k41aYbBL1NgWrZfcgljR5MxDxHDjzl0qHDNtH6SkW4DXNCA==" "integrity": "sha512-YUpjl57P55u2yUaKX5Bgy4t5s6SCNYMg+62XNg+k41aYbBL1NgWrZfcgljR5MxDxHDjzl0qHDNtH6SkW4DXNCA=="
}, },
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-int64": { "node-int64": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",

View File

@@ -5,7 +5,12 @@ import {
fetchRestaurant, fetchRestaurant,
fetchAllDishesForRestaurant, fetchAllDishesForRestaurant,
} from "../services/databaseServices.js"; } from "../services/databaseServices.js";
import * as services from "../services/services.js"; import {
decodeAndSanitize,
validateRestaurant,
handleError,
validateUserToken,
} from "../services/services.js";
import Restaurant from "../models/restaurant.js"; import Restaurant from "../models/restaurant.js";
var router = express.Router(); var router = express.Router();
@@ -14,11 +19,11 @@ var router = express.Router();
router.get("/", async (req, res) => { router.get("/", async (req, res) => {
try { try {
const query = services.decodeAndSanitize(req.query.restaurantId); const query = decodeAndSanitize(req.query.restaurantId);
await services.validateRestaurant(query); await validateRestaurant(query);
Restaurant.findById(query).then((data) => res.send(data)); Restaurant.findById(query).then((data) => res.send(data));
} catch (error) { } catch (error) {
services.handleError(error, res); handleError(error, res);
} }
}); });
@@ -27,13 +32,13 @@ router.get("/", async (req, res) => {
router.post("/", async (req, res) => { router.post("/", async (req, res) => {
try { try {
const token = req.headers["x-auth-token"]; const token = req.headers["x-auth-token"];
const user = services.validateUserToken(token); const user = validateUserToken(token);
const restaurant = createRestaurant(req.body); const restaurant = createRestaurant(req.body);
await restaurant.save(); await restaurant.save();
await addRestaurantToUser(user, restaurant); await addRestaurantToUser(user, restaurant);
res.sendStatus(201); res.sendStatus(201);
} catch (error) { } catch (error) {
services.handleError(error, res); handleError(error, res);
} }
}); });
@@ -41,13 +46,28 @@ router.post("/", async (req, res) => {
router.get("/dishes", async (req, res) => { router.get("/dishes", async (req, res) => {
try { try {
const query = services.decodeAndSanitize(req.query.restaurantId); const query = decodeAndSanitize(req.query.restaurantId);
await services.validateRestaurant(query); await validateRestaurant(query);
let restaurant = await fetchRestaurant(query); let restaurant = await fetchRestaurant(query);
let dishes = await fetchAllDishesForRestaurant(restaurant); let dishes = await fetchAllDishesForRestaurant(restaurant);
res.send(dishes); res.send(dishes);
} catch (error) { } catch (error) {
services.handleError(error, res); handleError(error, res);
}
});
// DELETE RESTAURANT
router.post("/delete", async (req, res) => {
try {
const token = req.headers["x-auth-token"];
const user = validateUserToken(token);
await validateRestaurant(req.body.restaurantId);
//check access
//delete restaurant
res.send("Restauracja została pomyślnie usunięta.");
} catch (error) {
handleError(error, res);
} }
}); });

View File

@@ -26,7 +26,7 @@ var agileAPI = new AgileCRMManager(CRM_USER, CRM_KEY, CRM_EMAIL);
router.post("/login", async (req, res) => { router.post("/login", async (req, res) => {
try { try {
if (!req.body.password || !req.body.email) { if (!req.body.password || !req.body.email) {
throw newError("No input data", 204); throw newError("Niepełne dane.", 204);
} }
const user = await fetchUser(req.body.email); const user = await fetchUser(req.body.email);
await checkPassword(req.body.password, user.password); await checkPassword(req.body.password, user.password);
@@ -56,7 +56,7 @@ router.post("/register", async (req, res) => {
router.post("/changepass", async (req, res) => { router.post("/changepass", async (req, res) => {
try { try {
if (!req.body.password || !req.body.email || !req.body.newPass) { if (!req.body.password || !req.body.email || !req.body.newPass) {
throw newError("No input data", 204); throw newError("Niepełne dane.", 204);
} }
const token = req.headers["x-auth-token"]; const token = req.headers["x-auth-token"];
validateUserToken(token); validateUserToken(token);
@@ -64,7 +64,7 @@ router.post("/changepass", async (req, res) => {
await checkPassword(req.body.password, user.password); await checkPassword(req.body.password, user.password);
const newPassword = await hashPass(req.body.newPass); const newPassword = await hashPass(req.body.newPass);
await changeUserPass(user._id, newPassword); await changeUserPass(user._id, newPassword);
res.status(200).send("Password changed"); res.status(200).send("Hasło zostało zmienione.");
} catch (error) { } catch (error) {
handleError(error, res); handleError(error, res);
} }
@@ -83,5 +83,17 @@ router.post("/forgotpassword", async (req, res) => {
}); });
// RESET PASS // RESET PASS
router.post("/resetpass", async (req, res) => {
try {
validateUserToken(req.body.token);
const user = await fetchUser(req.body.email);
const newPassword = await hashPass(req.body.newPass);
await changeUserPass(user._id, newPassword);
res.send("Hasło zostało zmienione.");
} catch (error) {
console.log(error);
handleError(error, res);
}
});
export default router; export default router;

View File

@@ -1,5 +1,4 @@
import Restaurant from "../models/restaurant.js"; import Restaurant from "../models/restaurant.js";
import crypto from "crypto";
import Dish from "../models/dish.js"; import Dish from "../models/dish.js";
import User from "../models/users.js"; import User from "../models/users.js";
import mongoose from "mongoose"; import mongoose from "mongoose";
@@ -72,7 +71,8 @@ function generatePasswordResetToken(email) {
export function generatePasswordResetLink(email) { export function generatePasswordResetLink(email) {
const token = generatePasswordResetToken(email); const token = generatePasswordResetToken(email);
const link = `htt`; const link = `https://www.menui.pl/forgot?token=${token}`;
return link;
} }
export async function checkEmailTaken(email) { export async function checkEmailTaken(email) {
@@ -133,6 +133,9 @@ export function halfYearFromNowDate() {
} }
export async function hashPass(pass) { export async function hashPass(pass) {
if (pass.length < 6) {
throw newError("Hasło za krótkie.", 500);
}
try { try {
const salt = await bcrypt.genSalt(10); const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(pass, salt); const hash = await bcrypt.hash(pass, salt);

View File

@@ -17,8 +17,3 @@ jest.mock("bcrypt", () => {
test("should return false for no date on input", () => { test("should return false for no date on input", () => {
expect(toShortDate()).toBe(false); expect(toShortDate()).toBe(false);
}); });
test("should generate random 10 characters long password", () => {
let generatedPass = generateNewPassword();
expect(generatedPass.length).toBe(10);
});