You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/test/user/reset.js

164 lines
5.0 KiB
JavaScript

'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', '[email protected]');
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('[email protected]');
});
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: '[email protected]', 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: '[email protected]', 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('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;
});
});