Files
NodeBB/test/user/emails.js
Barış Soner Uşaklı 04998908ba Fixes for "validate email" & "send validation email" in ACP (#11677)
* confirmObj changes

dont expire confirm:<code>, add a expires field instead
dont expire confirm:byUid:<uid>

on admin manage users display the users email status
	1. verified
	2. verify email sent (pending)
	3. verify email sent (expired)
	4. no email entered

fix validate email in acp to use
	email in user:<uid> if they have one
	if not check if its in confirm:<code>
	if its not in above cant validate throw error

fix send validate email to use
	email in user:<uid> if they have one
	if not check if its in confirm:<code>
	if its not in above too cant validate throw error

* add back socket.io tests

* test: fix confirm tests

no longer using pexpire
return correct time left on token

* chore: update openapi

* fix: delete call

* test: mget test fixes

* test: fix tests
2023-06-05 12:12:48 -04:00

234 lines
7.1 KiB
JavaScript

'use strict';
const assert = require('assert');
const nconf = require('nconf');
const util = require('util');
const db = require('../mocks/databasemock');
const helpers = require('../helpers');
const meta = require('../../src/meta');
const user = require('../../src/user');
const groups = require('../../src/groups');
const plugins = require('../../src/plugins');
const utils = require('../../src/utils');
describe('email confirmation (library methods)', () => {
let uid;
async function dummyEmailerHook(data) {
// pretend to handle sending emails
}
before(() => {
// Attach an emailer hook so related requests do not error
plugins.hooks.register('emailer-test', {
hook: 'filter:email.send',
method: dummyEmailerHook,
});
});
beforeEach(async () => {
uid = await user.create({
username: utils.generateUUID().slice(0, 10),
password: utils.generateUUID(),
});
});
after(async () => {
plugins.hooks.unregister('emailer-test', 'filter:email.send');
});
describe('isValidationPending', () => {
it('should return false if user did not request email validation', async () => {
const pending = await user.email.isValidationPending(uid);
assert.strictEqual(pending, false);
});
it('should return false if user did not request email validation (w/ email checking)', async () => {
const email = 'test@example.org';
const pending = await user.email.isValidationPending(uid, email);
assert.strictEqual(pending, false);
});
it('should return true if user requested email validation', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
const pending = await user.email.isValidationPending(uid);
assert.strictEqual(pending, true);
});
it('should return true if user requested email validation (w/ email checking)', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
const pending = await user.email.isValidationPending(uid, email);
assert.strictEqual(pending, true);
});
});
describe('getValidationExpiry', () => {
it('should return null if there is no validation available', async () => {
const expiry = await user.email.getValidationExpiry(uid);
assert.strictEqual(expiry, null);
});
it('should return a number smaller than configured expiry if validation available', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
const expiry = await user.email.getValidationExpiry(uid);
assert(isFinite(expiry));
assert(expiry > 0);
assert(expiry <= meta.config.emailConfirmExpiry * 24 * 60 * 60 * 1000);
});
});
describe('expireValidation', () => {
it('should invalidate any confirmation in-progress', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
await user.email.expireValidation(uid);
assert.strictEqual(await user.email.isValidationPending(uid), false);
assert.strictEqual(await user.email.isValidationPending(uid, email), false);
assert.strictEqual(await user.email.canSendValidation(uid, email), true);
});
});
describe('canSendValidation', () => {
it('should return true if no validation is pending', async () => {
const ok = await user.email.canSendValidation(uid, 'test@example.com');
assert(ok);
});
it('should return false if it has been too soon to re-send confirmation', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
const ok = await user.email.canSendValidation(uid, email);
assert.strictEqual(ok, false);
});
it('should return true if it has been long enough to re-send confirmation', async () => {
const email = 'test@example.org';
await user.email.sendValidationEmail(uid, {
email,
});
const code = await db.get(`confirm:byUid:${uid}`);
await db.setObjectField(`confirm:${code}`, 'expires', Date.now() + 1000);
const ok = await user.email.canSendValidation(uid, email);
assert(ok);
});
});
});
describe('email confirmation (v3 api)', () => {
let userObj;
let jar;
const register = data => new Promise((resolve, reject) => {
helpers.registerUser(data, (err, jar, response, body) => {
if (err) {
return reject(err);
}
resolve({ jar, response, body });
});
});
before(async () => {
// If you're running this file directly, uncomment these lines
await register({
username: 'fake-user',
password: 'derpioansdosa',
email: 'b@c.com',
gdpr_consent: true,
});
({ body: userObj, jar } = await register({
username: 'email-test',
password: 'abcdef',
email: 'test@example.org',
gdpr_consent: true,
}));
});
it('should have a pending validation', async () => {
const code = await db.get(`confirm:byUid:${userObj.uid}`);
assert.strictEqual(await user.email.isValidationPending(userObj.uid, 'test@example.org'), true);
});
it('should not list their email', async () => {
const { res, body } = await helpers.request('get', `/api/v3/users/${userObj.uid}/emails`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{"emails":[]}}'));
});
it('should not allow confirmation if they are not an admin', async () => {
const { res } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 403);
});
it('should not confirm an email that is not pending or set', async () => {
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('fake@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 404);
await groups.leave('administrators', userObj.uid);
});
it('should confirm their email (using the pending validation)', async () => {
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{}}'));
await groups.leave('administrators', userObj.uid);
});
it('should still confirm the email (as email is set in user hash)', async () => {
await user.email.remove(userObj.uid);
await user.setUserField(userObj.uid, 'email', 'test@example.org');
({ jar } = await helpers.loginUser('email-test', 'abcdef')); // email removal logs out everybody
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{}}'));
await groups.leave('administrators', userObj.uid);
});
});