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.
164 lines
5.0 KiB
JavaScript
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;
|
|
});
|
|
});
|