1257 lines
43 KiB
TypeScript
1257 lines
43 KiB
TypeScript
import * as querystring from "querystring";
|
|
import supertest from "supertest";
|
|
|
|
import User from "../src/auth/models/User.js";
|
|
import UserEmail from "../src/auth/models/UserEmail.js";
|
|
import UserNameComponent from "../src/auth/models/UserNameComponent.js";
|
|
import UserPasswordComponent from "../src/auth/password/UserPasswordComponent.js";
|
|
import useApp from "./_app.js";
|
|
import {authAppProvider, followMagicLinkFromMail, testLogout} from "./_authentication_common.js";
|
|
import {popEmail} from "./_mail_server.js";
|
|
|
|
const app = useApp(authAppProvider());
|
|
|
|
let agent: supertest.SuperTest<supertest.Test>;
|
|
|
|
beforeAll(() => {
|
|
agent = supertest(app().getExpressApp());
|
|
});
|
|
|
|
test('Approval Mode', () => {
|
|
expect(User.isApprovalMode()).toStrictEqual(false);
|
|
});
|
|
|
|
describe('Register with username and password (password)', () => {
|
|
let cookies: string[];
|
|
let csrf: string;
|
|
|
|
test('General case', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
// Register user
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'entrapta',
|
|
password: 'darla_is_cute',
|
|
password_confirmation: 'darla_is_cute',
|
|
terms: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
// Verify saved user
|
|
const user = await User.select()
|
|
.where('name', 'entrapta')
|
|
.first();
|
|
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserNameComponent).getName()).toStrictEqual('entrapta');
|
|
await expect(user?.as(UserPasswordComponent).verifyPassword('darla_is_cute')).resolves.toStrictEqual(true);
|
|
|
|
// Proof must not be revoked
|
|
await agent.get('/has-any-password-auth-proof')
|
|
.set('Cookie', cookies)
|
|
.expect(200);
|
|
});
|
|
|
|
test('Can\'t register when logged in', async () => {
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'entrapta2',
|
|
password: 'darla_is_cute',
|
|
password_confirmation: 'darla_is_cute',
|
|
terms: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
const user2 = await User.select()
|
|
.where('name', 'entrapta2')
|
|
.first();
|
|
expect(user2).toBeNull();
|
|
});
|
|
|
|
test('Logout', async () => {
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Cannot register taken username', async () => {
|
|
// Check that there is no hordak in DB
|
|
expect(await User.select()
|
|
.where('name', 'hordak')
|
|
.count()).toStrictEqual(0);
|
|
|
|
const res1 = await agent.get('/csrf').expect(200);
|
|
const cookies = res1.get('Set-Cookie');
|
|
const csrf = res1.text;
|
|
|
|
// Register user
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'hordak',
|
|
password: 'horde_prime_will_rise',
|
|
password_confirmation: 'horde_prime_will_rise',
|
|
terms: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
await testLogout(agent, cookies, csrf);
|
|
|
|
// Verify saved user
|
|
expect(await User.select()
|
|
.where('name', 'hordak')
|
|
.count()).toStrictEqual(1);
|
|
|
|
|
|
// Attempt register same user
|
|
const res = await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'hordak',
|
|
password: 'horde_prime_will_rise_unless',
|
|
password_confirmation: 'horde_prime_will_rise_unless',
|
|
terms: 'on',
|
|
})
|
|
.expect(400);
|
|
// username field should be translated from identifier
|
|
expect(res.body.messages?.identifier?.name).toStrictEqual('AlreadyExistsValidationError');
|
|
|
|
// Verify nothing changed
|
|
expect(await User.select()
|
|
.where('name', 'hordak')
|
|
.count()).toStrictEqual(1);
|
|
});
|
|
});
|
|
|
|
describe('Register with email (magic_link)', () => {
|
|
test('General case', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
await agent.post('/auth/register?' + querystring.stringify({redirect_uri: '/redirect-uri'}))
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'glimmer@example.org',
|
|
name: 'glimmer',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby?redirect_uri=%2Fredirect-uri');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
// Proof must not be revoked
|
|
await agent.get('/has-any-magic-link')
|
|
.set('Cookie', cookies)
|
|
.expect(200);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
|
|
// Verify saved user
|
|
const user = await User.select()
|
|
.with('mainEmail')
|
|
.where('name', 'glimmer')
|
|
.first();
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
const email = user?.mainEmail.getOrFail();
|
|
expect(email).toBeDefined();
|
|
expect(email?.email).toStrictEqual('glimmer@example.org');
|
|
|
|
expect(user?.as(UserNameComponent).getName()).toStrictEqual('glimmer');
|
|
await expect(user?.as(UserPasswordComponent).verifyPassword('')).resolves.toStrictEqual(false);
|
|
});
|
|
|
|
test('Cannot register without specifying username', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
res = await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'no_user_name@example.org',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.name?.name).toStrictEqual('UndefinedValueValidationError');
|
|
|
|
expect(await popEmail()).toBeNull();
|
|
});
|
|
|
|
test('Cannot register taken username', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'angella@example.org',
|
|
name: 'angella',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
|
|
// Verify saved user
|
|
const user = await User.select()
|
|
.with('mainEmail')
|
|
.where('name', 'angella')
|
|
.first();
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
// Attempt register with another mail but same username
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'angella_something_else@example.org',
|
|
name: 'angella',
|
|
})
|
|
.expect(400);
|
|
|
|
expect(await popEmail()).toBeNull();
|
|
});
|
|
|
|
test('Cannot register taken email', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'bow@example.org',
|
|
name: 'bow',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
|
|
// Verify saved user
|
|
const user = await User.select()
|
|
.with('mainEmail')
|
|
.where('name', 'bow')
|
|
.first();
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
// Attempt register with another mail but same username
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'bow@example.org',
|
|
name: 'bow2',
|
|
})
|
|
.expect(400);
|
|
|
|
expect(await popEmail()).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Authenticate with username and password (password)', () => {
|
|
test('Force auth_method', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Bad password
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'entrapta',
|
|
password: 'darla_is_not_cute',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login?' + querystring.stringify({redirect_uri: '/redirect-uri'}))
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'entrapta',
|
|
password: 'darla_is_cute',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/redirect-uri');
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Automatic auth_method', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Bad password
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'entrapta',
|
|
password: 'darla_is_not_cute',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'entrapta',
|
|
password: 'darla_is_cute',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Non-existing username', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'i_do_not_exist',
|
|
password: 'there_is_no_point',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
|
|
|
|
// Authenticate (automatic method)
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'i_do_not_exist',
|
|
password: 'there_is_no_point',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
|
|
});
|
|
|
|
test('No password user', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'glimmer',
|
|
password: '',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate (automatic method)
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'glimmer',
|
|
password: '',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate without password
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'angella',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('UndefinedValueValidationError');
|
|
|
|
// Authenticate without password (automatic method)
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'angella',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('UndefinedValueValidationError');
|
|
});
|
|
});
|
|
|
|
describe('Authenticate with email (magic_link)', () => {
|
|
test('Force auth_method', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login?' + querystring.stringify({redirect_uri: '/redirect-uri'}))
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'glimmer@example.org',
|
|
auth_method: 'magic_link',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby?redirect_uri=%2Fredirect-uri');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Automatic auth_method', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'angella@example.org',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Non-existing email (forced auth_method)', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'i_do_not_exist@invalid.org',
|
|
auth_method: 'magic_link',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
});
|
|
|
|
test('Non-existing email (automatic auth_method)', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Authenticate
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'i_do_not_exist@invalid.org',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.identifier?.name).toStrictEqual('UnknownRelationValidationError');
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
});
|
|
});
|
|
|
|
describe('Authenticate with email and password (password)', () => {
|
|
test('Prepare user', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'double-trouble@example.org',
|
|
name: 'double-trouble',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
|
|
// Verify saved user
|
|
const user = await User.select()
|
|
.with('mainEmail')
|
|
.where('name', 'double-trouble')
|
|
.first();
|
|
|
|
await user?.as(UserPasswordComponent).setPassword('trick-or-treat');
|
|
await user?.save();
|
|
|
|
expect(user).toBeDefined();
|
|
|
|
const email = user?.mainEmail.getOrFail();
|
|
expect(email).toBeDefined();
|
|
expect(email?.email).toStrictEqual('double-trouble@example.org');
|
|
|
|
expect(user?.as(UserNameComponent).getName()).toStrictEqual('double-trouble');
|
|
await expect(user?.as(UserPasswordComponent).verifyPassword('trick-or-treat')).resolves.toStrictEqual(true);
|
|
});
|
|
|
|
test('Force auth_method', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Bad password
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'double-trouble@example.org',
|
|
password: 'i_have_no_imagination',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'double-trouble@example.org',
|
|
password: 'trick-or-treat',
|
|
auth_method: 'password',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
|
|
test('Automatic auth_method', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
const cookies = res.get('Set-Cookie');
|
|
const csrf = res.text;
|
|
|
|
// Not authenticated
|
|
await agent.get('/is-auth').set('Cookie', cookies).expect(401);
|
|
|
|
// Bad password
|
|
res = await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'double-trouble@example.org',
|
|
password: 'i_have_no_imagination',
|
|
})
|
|
.expect(400);
|
|
expect(res.body.messages?.password?.name).toStrictEqual('InvalidFormatValidationError');
|
|
|
|
// Authenticate
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
identifier: 'double-trouble@example.org',
|
|
password: 'trick-or-treat',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
await testLogout(agent, cookies, csrf);
|
|
});
|
|
});
|
|
|
|
|
|
describe('Change password', () => {
|
|
let cookies: string[], csrf: string;
|
|
test('Prepare user', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'aang@example.org',
|
|
name: 'aang',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
});
|
|
|
|
test('Set password to blank from blank', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': '',
|
|
'new_password': '',
|
|
'new_password_confirmation': '',
|
|
})
|
|
.expect(400);
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('bad_password')).toBeFalsy();
|
|
});
|
|
|
|
test('Set password to something from blank', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': '',
|
|
'new_password': 'a_very_strong_password',
|
|
'new_password_confirmation': 'a_very_strong_password',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('bad_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password')).toBeTruthy();
|
|
});
|
|
|
|
test('Set password to blank from something', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': 'a_very_strong_password',
|
|
'new_password': '',
|
|
})
|
|
.expect(400);
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('bad_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password')).toBeTruthy();
|
|
});
|
|
|
|
test('Set password to something from something', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': 'a_very_strong_password',
|
|
'new_password': 'a_very_strong_password_but_different',
|
|
'new_password_confirmation': 'a_very_strong_password_but_different',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('bad_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password_but_different')).toBeTruthy();
|
|
});
|
|
|
|
test('Set password to unconfirmed', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': 'a_very_strong_password_but_different',
|
|
'new_password': 'a_very_strong_password',
|
|
'new_password_confirmation': '',
|
|
})
|
|
.expect(400);
|
|
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': 'a_very_strong_password_but_different',
|
|
'new_password': 'a_very_strong_password',
|
|
'new_password_confirmation': 'a_very_strong_password_but_different2',
|
|
})
|
|
.expect(400);
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('bad_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password')).toBeFalsy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password_but_different')).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password_but_different2')).toBeFalsy();
|
|
});
|
|
|
|
test('Set password to too short password', async () => {
|
|
await agent.post('/account/change-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
'current_password': 'a_very_strong_password_but_different',
|
|
'new_password': '123',
|
|
'new_password_confirmation': '123',
|
|
})
|
|
.expect(400);
|
|
|
|
const user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('a_very_strong_password_but_different')).toBeTruthy();
|
|
expect(await user?.as(UserPasswordComponent).verifyPassword('123')).toBeFalsy();
|
|
});
|
|
|
|
test('Remove password', async () => {
|
|
let user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBe(true);
|
|
|
|
await agent.post('/account/remove-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby?redirect_uri=%2Faccount%2F');
|
|
|
|
await followMagicLinkFromMail(agent, cookies, '/account/');
|
|
|
|
user = await User.select()
|
|
.where('name', 'aang')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBe(false);
|
|
});
|
|
|
|
test('Can\'t remove password without contact email', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'eleanor',
|
|
password: 'this_is_a_very_strong_password',
|
|
password_confirmation: 'this_is_a_very_strong_password',
|
|
terms: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
|
|
let user = await User.select()
|
|
.where('name', 'eleanor')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBe(true);
|
|
|
|
await agent.post('/account/remove-password')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
user = await User.select()
|
|
.where('name', 'eleanor')
|
|
.first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserPasswordComponent).hasPassword()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Change username', () => {
|
|
let cookies: string[], csrf: string;
|
|
test('Prepare user', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'password',
|
|
identifier: 'richard',
|
|
password: 'a_very_strong_password',
|
|
password_confirmation: 'a_very_strong_password',
|
|
terms: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
});
|
|
|
|
test('Initial value of name_changed_at should be the same as created_at', async () => {
|
|
const user = await User.select().where('name', 'richard').first();
|
|
expect(user).toBeDefined();
|
|
expect(user?.as(UserNameComponent).getNameChangedAt()?.getTime()).toBe(user?.created_at?.getTime());
|
|
});
|
|
|
|
test('Cannot change username just after registration', async () => {
|
|
expect(await User.select().where('name', 'richard').count()).toBe(1);
|
|
|
|
await agent.post('/account/change-name')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
name: 'robert',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
expect(await User.select().where('name', 'richard').count()).toBe(1);
|
|
});
|
|
|
|
test('Can change username after hold period', async () => {
|
|
// Set last username change to older date
|
|
const user = await User.select().where('name', 'richard').first();
|
|
if (user) {
|
|
user.as(UserNameComponent).forgetNameChangeDate();
|
|
await user.save();
|
|
}
|
|
|
|
expect(await User.select().where('name', 'richard').count()).toBe(1);
|
|
expect(await User.select().where('name', 'robert').count()).toBe(0);
|
|
|
|
await agent.post('/account/change-name')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
name: 'robert',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
expect(await User.select().where('name', 'richard').count()).toBe(0);
|
|
expect(await User.select().where('name', 'robert').count()).toBe(1);
|
|
});
|
|
|
|
test('Cannot change username just after changing username', async () => {
|
|
expect(await User.select().where('name', 'robert').count()).toBe(1);
|
|
expect(await User.select().where('name', 'bebert').count()).toBe(0);
|
|
|
|
await agent.post('/account/change-name')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
name: 'bebert',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
expect(await User.select().where('name', 'robert').count()).toBe(1);
|
|
expect(await User.select().where('name', 'bebert').count()).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('Manage email addresses', () => {
|
|
|
|
async function testMainSecondaryState(main: string, secondary: string) {
|
|
const user = await User.select('main_email_id').where('name', 'katara').first();
|
|
|
|
const mainEmail = await UserEmail.select().where('email', main).first();
|
|
expect(mainEmail).not.toBeNull();
|
|
expect(user?.main_email_id).toBe(mainEmail?.id);
|
|
|
|
const secondaryEmail = await UserEmail.select().where('email', secondary).first();
|
|
expect(secondaryEmail).not.toBeNull();
|
|
expect(user?.main_email_id).not.toBe(secondaryEmail?.id);
|
|
|
|
return secondaryEmail;
|
|
}
|
|
|
|
let cookies: string[], csrf: string;
|
|
test('Prepare user', async () => {
|
|
const res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'katara@example.org',
|
|
name: 'katara',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
// Add fake, not owned secondary email
|
|
const user = User.create({
|
|
name: 'not_katara',
|
|
});
|
|
await user.save();
|
|
await UserEmail.create({
|
|
email: 'not_katara@example.org',
|
|
user_id: user.id,
|
|
}).save();
|
|
});
|
|
|
|
describe('Add', () => {
|
|
test('Add invalid email addresses', async () => {
|
|
await agent.post('/account/add-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
})
|
|
.expect(400);
|
|
await agent.post('/account/add-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
email: '',
|
|
})
|
|
.expect(400);
|
|
await agent.post('/account/add-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
email: 'katara@example.org',
|
|
})
|
|
.expect(400);
|
|
|
|
expect(await UserEmail.select().where('email', 'katara@example.org').count()).toBe(1);
|
|
});
|
|
|
|
test('Add valid email addresses', async () => {
|
|
const expectedUserId = (await User.select('id').where('name', 'katara').first())?.id;
|
|
|
|
for (const email of [
|
|
'katara2@example.org',
|
|
'katara3@example.org',
|
|
'katara4@example.org',
|
|
]) {
|
|
await agent.post('/account/add-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
email: email,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby?redirect_uri=%2Faccount%2F');
|
|
|
|
await followMagicLinkFromMail(agent, cookies, '/account/');
|
|
|
|
const userEmail = await UserEmail.select().where('email', email).first();
|
|
expect(userEmail).not.toBeNull();
|
|
expect(userEmail?.user_id).toBe(expectedUserId);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Set main', () => {
|
|
test('Set main email address as main email address', async () => {
|
|
await testMainSecondaryState('katara@example.org', 'katara3@example.org');
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/set-main-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: (await UserEmail.select().where('email', 'katara@example.org').first())?.id,
|
|
})
|
|
.expect(400);
|
|
|
|
await testMainSecondaryState('katara@example.org', 'katara3@example.org');
|
|
});
|
|
|
|
test('Set secondary email address as main email address', async () => {
|
|
const beforeSecondaryEmail = await testMainSecondaryState('katara@example.org', 'katara3@example.org');
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/set-main-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: beforeSecondaryEmail?.id,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
await testMainSecondaryState('katara3@example.org', 'katara@example.org');
|
|
});
|
|
|
|
test('Set non-owned address as main email address', async () => {
|
|
const beforeSecondaryEmail = await testMainSecondaryState('katara3@example.org', 'not_katara@example.org');
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/set-main-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: beforeSecondaryEmail?.id,
|
|
})
|
|
.expect(403);
|
|
|
|
await testMainSecondaryState('katara3@example.org', 'not_katara@example.org');
|
|
});
|
|
|
|
test('Set non-existing address as main email address', async () => {
|
|
await testMainSecondaryState('katara3@example.org', 'katara4@example.org');
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/set-main-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: 999,
|
|
})
|
|
.expect(404);
|
|
|
|
await testMainSecondaryState('katara3@example.org', 'katara4@example.org');
|
|
});
|
|
});
|
|
|
|
describe('Remove', () => {
|
|
test('Remove secondary email address', async () => {
|
|
expect(await UserEmail.select().where('email', 'katara2@example.org').count()).toBe(1);
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/remove-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: (await UserEmail.select().where('email', 'katara2@example.org').first())?.id,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/account/');
|
|
|
|
expect(await UserEmail.select().where('email', 'katara2@example.org').count()).toBe(0);
|
|
});
|
|
|
|
test('Remove non-owned email address', async () => {
|
|
expect(await UserEmail.select().where('email', 'not_katara@example.org').count()).toBe(1);
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/remove-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: (await UserEmail.select().where('email', 'not_katara@example.org').first())?.id,
|
|
})
|
|
.expect(403);
|
|
|
|
expect(await UserEmail.select().where('email', 'not_katara@example.org').count()).toBe(1);
|
|
});
|
|
|
|
test('Remove non-existing email address', async () => {
|
|
// Set secondary as main
|
|
await agent.post('/account/remove-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: 999,
|
|
})
|
|
.expect(404);
|
|
});
|
|
|
|
test('Remove main email address', async () => {
|
|
expect(await UserEmail.select().where('email', 'katara3@example.org').count()).toBe(1);
|
|
|
|
// Set secondary as main
|
|
await agent.post('/account/remove-email')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
id: (await UserEmail.select().where('email', 'katara3@example.org').first())?.id,
|
|
})
|
|
.expect(400);
|
|
|
|
expect(await UserEmail.select().where('email', 'katara3@example.org').count()).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Session persistence', () => {
|
|
let cookies: string[], csrf: string;
|
|
|
|
test('Not persistent at registration', async () => {
|
|
let res = await agent.get('/csrf').expect(200);
|
|
cookies = res.get('Set-Cookie');
|
|
csrf = res.text;
|
|
|
|
await agent.post('/auth/register')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'zuko@example.org',
|
|
name: 'zuko',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
expect(cookies[0]).toMatch(/^connect\.sid=.+; Path=\/; SameSite=Strict$/);
|
|
|
|
res = await agent.get('/csrf')
|
|
.set('Cookie', cookies)
|
|
.expect(200);
|
|
expect(res.get('Set-Cookie')[0]).toMatch(/^connect\.sid=.+; Path=\/; Expires=.+; SameSite=Strict$/);
|
|
|
|
// Logout
|
|
await agent.post('/auth/logout')
|
|
.set('Cookie', cookies)
|
|
.send({csrf})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
});
|
|
|
|
test('Login with persistence', async () => {
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'zuko@example.org',
|
|
persist_session: 'on',
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
const res = await agent.get('/csrf')
|
|
.set('Cookie', cookies)
|
|
.expect(200);
|
|
expect(res.get('Set-Cookie')[0]).toMatch(/^connect\.sid=.+; Path=\/; Expires=.+; SameSite=Strict$/);
|
|
|
|
// Logout
|
|
await agent.post('/auth/logout')
|
|
.set('Cookie', cookies)
|
|
.send({csrf})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
});
|
|
|
|
test('Login without persistence', async () => {
|
|
await agent.post('/auth/login')
|
|
.set('Cookie', cookies)
|
|
.send({
|
|
csrf: csrf,
|
|
auth_method: 'magic_link',
|
|
identifier: 'zuko@example.org',
|
|
persist_session: undefined,
|
|
})
|
|
.expect(302)
|
|
.expect('Location', '/magic/lobby');
|
|
|
|
await followMagicLinkFromMail(agent, cookies);
|
|
|
|
const res = await agent.get('/csrf')
|
|
.set('Cookie', cookies)
|
|
.expect(200);
|
|
expect(res.get('Set-Cookie')[0]).toMatch(/^connect\.sid=.+; Path=\/; SameSite=Strict$/);
|
|
|
|
// Logout
|
|
await agent.post('/auth/logout')
|
|
.set('Cookie', cookies)
|
|
.send({csrf})
|
|
.expect(302)
|
|
.expect('Location', '/');
|
|
});
|
|
});
|