* chore: incrementing version number - v2.8.1 * chore: update changelog for v2.8.1 * fix: accidental clearing of reset rate limiting on reset send * test: move user reset tests to its own file, add failing test for user reset locks * fix: #11119, counter attempted flooding of user reset route * test: fix password reset socket test to check for error now * test: same user sending multiple reset emails should work after waiting the correct amount of time * lint: fixes * chore: rename outdated `cleanTokensAndUids` method * test: no need to create user for new test Co-authored-by: Misty Release Bot <deploy@nodebb.org> Co-authored-by: Barış Soner Uşaklı <barisusakli@gmail.com>isekai-main
parent
19c2b3509a
commit
a344e6ec0c
@ -0,0 +1,163 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
|
||||
const db = require('../mocks/databasemock');
|
||||
|
||||
const user = require('../../src/user');
|
||||
const groups = require('../../src/groups');
|
||||
const password = require('../../src/password');
|
||||
const utils = require('../../src/utils');
|
||||
|
||||
const socketUser = require('../../src/socket.io/user');
|
||||
|
||||
describe('Password reset (library methods)', () => {
|
||||
let uid;
|
||||
let code;
|
||||
before(async () => {
|
||||
uid = await user.create({ username: 'resetuser', password: '123456' });
|
||||
await user.setUserField(uid, 'email', 'reset@me.com');
|
||||
await user.email.confirmByUid(uid);
|
||||
});
|
||||
|
||||
it('.generate() should generate a new reset code', (done) => {
|
||||
user.reset.generate(uid, (err, _code) => {
|
||||
assert.ifError(err);
|
||||
assert(_code);
|
||||
|
||||
code = _code;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('.generate() should invalidate a previous generated reset code', async () => {
|
||||
const _code = await user.reset.generate(uid);
|
||||
const valid = await user.reset.validate(code);
|
||||
assert.strictEqual(valid, false);
|
||||
|
||||
code = _code;
|
||||
});
|
||||
|
||||
it('.validate() should ensure that this new code is valid', (done) => {
|
||||
user.reset.validate(code, (err, valid) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(valid, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('.validate() should correctly identify an invalid code', (done) => {
|
||||
user.reset.validate(`${code}abcdef`, (err, valid) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(valid, false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('.send() should create a new reset code and reset password', async () => {
|
||||
code = await user.reset.send('reset@me.com');
|
||||
});
|
||||
|
||||
it('.commit() should update the user\'s password and confirm their email', (done) => {
|
||||
user.reset.commit(code, 'newpassword', (err) => {
|
||||
assert.ifError(err);
|
||||
|
||||
async.parallel({
|
||||
userData: function (next) {
|
||||
user.getUserData(uid, next);
|
||||
},
|
||||
password: function (next) {
|
||||
db.getObjectField(`user:${uid}`, 'password', next);
|
||||
},
|
||||
}, (err, results) => {
|
||||
assert.ifError(err);
|
||||
password.compare('newpassword', results.password, true, (err, match) => {
|
||||
assert.ifError(err);
|
||||
assert(match);
|
||||
assert.strictEqual(results.userData['email:confirmed'], 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('.should error if same password is used for reset', async () => {
|
||||
const uid = await user.create({ username: 'badmemory', email: 'bad@memory.com', password: '123456' });
|
||||
const code = await user.reset.generate(uid);
|
||||
let err;
|
||||
try {
|
||||
await user.reset.commit(code, '123456');
|
||||
} catch (_err) {
|
||||
err = _err;
|
||||
}
|
||||
assert.strictEqual(err.message, '[[error:reset-same-password]]');
|
||||
});
|
||||
|
||||
it('should not validate email if password reset is due to expiry', async () => {
|
||||
const uid = await user.create({ username: 'resetexpiry', email: 'reset@expiry.com', password: '123456' });
|
||||
let confirmed = await user.getUserField(uid, 'email:confirmed');
|
||||
let [verified, unverified] = await groups.isMemberOfGroups(uid, ['verified-users', 'unverified-users']);
|
||||
assert.strictEqual(confirmed, 0);
|
||||
assert.strictEqual(verified, false);
|
||||
assert.strictEqual(unverified, true);
|
||||
await user.setUserField(uid, 'passwordExpiry', Date.now());
|
||||
const code = await user.reset.generate(uid);
|
||||
await user.reset.commit(code, '654321');
|
||||
confirmed = await user.getUserField(uid, 'email:confirmed');
|
||||
[verified, unverified] = await groups.isMemberOfGroups(uid, ['verified-users', 'unverified-users']);
|
||||
assert.strictEqual(confirmed, 0);
|
||||
assert.strictEqual(verified, false);
|
||||
assert.strictEqual(unverified, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe.only('locks', () => {
|
||||
let uid;
|
||||
let email;
|
||||
beforeEach(async () => {
|
||||
const [username, password] = [utils.generateUUID().slice(0, 10), utils.generateUUID()];
|
||||
uid = await user.create({ username, password });
|
||||
email = `${username}@nodebb.org`;
|
||||
await user.setUserField(uid, 'email', email);
|
||||
await user.email.confirmByUid(uid);
|
||||
});
|
||||
|
||||
it('should disallow reset request if one was made within the minute', async () => {
|
||||
await user.reset.send(email);
|
||||
await assert.rejects(user.reset.send(email), {
|
||||
message: '[[error:reset-rate-limited]]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow multiple calls to the reset method at the same time', async () => {
|
||||
await assert.rejects(Promise.all([
|
||||
user.reset.send(email),
|
||||
user.reset.send(email),
|
||||
]), {
|
||||
message: '[[error:reset-rate-limited]]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow multiple socket calls to the reset method either', async () => {
|
||||
await assert.rejects(Promise.all([
|
||||
socketUser.reset.send({ uid: 0 }, email),
|
||||
socketUser.reset.send({ uid: 0 }, email),
|
||||
]), {
|
||||
message: '[[error:reset-rate-limited]]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly unlock user reset', async () => {
|
||||
await user.reset.send(email);
|
||||
await assert.rejects(user.reset.send(email), {
|
||||
message: '[[error:reset-rate-limited]]',
|
||||
});
|
||||
user.reset.minSecondsBetweenEmails = 3;
|
||||
const util = require('util');
|
||||
const sleep = util.promisify(setTimeout);
|
||||
await sleep(4 * 1000); // wait 4 seconds
|
||||
await user.reset.send(email);
|
||||
user.reset.minSecondsBetweenEmails = 60;
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue