apostrophe
Version:
The Apostrophe Content Management System.
1,496 lines (1,341 loc) • 33.9 kB
JavaScript
const t = require('../test-lib/test.js');
const assert = require('assert').strict;
describe('Login', function () {
let apos;
let resetUserId;
let resetToken;
this.timeout(20000);
before(async function () {
apos = await t.create({
root: module,
modules: {
'@apostrophecms/express': {
options: {
apiKeys: {
adminApiKey: {
role: 'admin'
}
}
}
},
'@apostrophecms/login': {
options: {
passwordReset: true,
environmentLabel: 'test'
}
}
}
});
});
after(function () {
return t.destroy(apos);
});
// EXISTENCE
it('should initialize', async function () {
assert(apos);
assert(apos.modules['@apostrophecms/login']);
assert(apos.user.safe.remove);
const response = await apos.user.safe.removeMany({});
assert(response.result.ok === 1);
const loginModule = apos.modules['@apostrophecms/login'];
const context = await loginModule.getContext();
assert(context.env === 'test');
});
it('should be able to insert test user', async function() {
assert(apos.user.newInstance);
const user = apos.user.newInstance();
assert(user);
user.title = 'Harry Putter';
user.username = 'HarryPutter';
user.password = 'crookshanks';
user.email = 'hputter@aol.com';
user.role = 'admin';
assert(user.type === '@apostrophecms/user');
assert(apos.user.insert);
const doc = await apos.user.insert(apos.task.getReq(), user);
assert(doc._id);
const user2 = apos.user.newInstance();
user2.title = 'Bob Smith';
user2.username = 'BobSmith';
user2.password = 'bobsmith';
user2.email = 'bobsmith@aol.com';
user2.role = 'guest';
await apos.user.insert(apos.task.getReq(), user2);
});
it('should throttle login attempts and show a proper error when the limit is reached', async function () {
const loginModule = apos.modules['@apostrophecms/login'];
const { allowedAttempts } = loginModule.options.throttle;
const jar = apos.http.jar();
const username = 'HarryPutter';
// establish session
const page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged out/));
for (let index = 0; index <= allowedAttempts; index++) {
try {
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username,
password: 'badpassword',
session: true
},
jar
}
);
} catch ({ body }) {
if (index < allowedAttempts) {
assert(body.message === 'Your credentials are incorrect, or there is no such user');
} else {
assert(body.message === 'Too many attempts. You may try again in a minute.');
}
}
}
await loginModule.clearLoginAttempts(username);
});
it('should be able to login a user with their username', async function () {
const getLoggedInCookieValue =
jar => jar.toJSON().cookies.find(cookie => cookie.key === `${apos.options.shortName}.loggedIn`).value;
const jar = apos.http.jar();
// establish session
let page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged out/));
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'crookshanks',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
assert(page.match(/Harry Putter/));
assert(getLoggedInCookieValue(jar) === 'true');
// otherwise logins are not remembered in a session
await apos.http.post(
'/api/v1/@apostrophecms/login/logout',
{
body: {
username: 'hputter@aol.com',
password: 'crookshanks',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
// are we back to being able to log in?
assert(page.match(/logged out/));
assert(getLoggedInCookieValue(jar) === 'false');
});
it('should be able to login a user with their email', async function () {
const jar = apos.http.jar();
// establish session
let page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged out/));
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
body: {
username: 'hputter@aol.com',
password: 'crookshanks',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
// Did we get our page back?
assert(page.match(/logged in/));
// otherwise logins are not remembered in a session
await apos.http.post(
'/api/v1/@apostrophecms/login/logout',
{
body: {
username: 'hputter@aol.com',
password: 'crookshanks',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
// are we back to being able to log in?
assert(page.match(/logged out/));
});
it('changing a user\'s password should invalidate sessions for that user', async function () {
const jar = apos.http.jar();
// establish session
let page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged out/));
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'crookshanks',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
const req = apos.task.getReq();
let user = await apos.user.find(req, {
username: 'HarryPutter'
}).toObject();
assert(user);
user.password = 'VeryPasswordManySecure🐶';
await apos.user.update(req, user);
page = await apos.http.get(
'/',
{
jar
}
);
assert(!page.match(/logged in/));
assert(page.match(/logged out/));
// Make sure we can come back from that
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'VeryPasswordManySecure🐶',
session: true
},
jar
}
);
page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
// So we do not have a stale _passwordUpdated flag
user = await apos.user.find(req, {
_id: user._id
}).toObject();
// Unrelated writes to user should not invalidate sessions
user.title = 'Extra Cool Putter';
await apos.user.update(req, user);
page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
// Marking a user account as disabled should invalidate sessions
user.disabled = true;
await apos.user.update(req, user);
page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged out/));
// Restore access for next test
user.disabled = false;
await apos.user.update(req, user);
});
it('changing a user\'s password should invalidate bearer tokens for that user', async function () {
// Log in
let response = await apos.http.post('/api/v1/@apostrophecms/login/login', {
body: {
username: 'HarryPutter',
password: 'VeryPasswordManySecure🐶'
}
});
assert(response.token);
let token = response.token;
// For verification: can't do this without an admin bearer token
await apos.http.get(
'/api/v1/@apostrophecms/user',
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
const req = apos.task.getReq();
const user = await apos.user.find(req, {
username: 'HarryPutter'
}).toObject();
assert(user);
user.password = 'AnotherLovelyPassword';
await apos.user.update(req, user);
let failed = false;
try {
await apos.http.get(
'/api/v1/@apostrophecms/user',
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
// Should NOT work
assert(false);
} catch (e) {
failed = true;
assert.strictEqual(e.status, 401);
}
assert(failed);
// Make sure we can come back from that
response = await apos.http.post('/api/v1/@apostrophecms/login/login', {
body: {
username: 'HarryPutter',
password: 'AnotherLovelyPassword'
}
});
assert(response.token);
token = response.token;
await apos.http.get(
'/api/v1/@apostrophecms/user',
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
});
it('api key should beat session when both are present', async function () {
const jar = apos.http.jar();
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'BobSmith',
password: 'bobsmith',
session: true
},
jar
}
);
const page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
assert(page.match(/Bob Smith/));
const page2 = await apos.http.get(
'/',
{
jar,
headers: {
Authorization: 'ApiKey adminApiKey'
}
}
);
assert(page2.match(/logged in/));
assert(!page2.match(/Bob Smith/));
assert(page2.match(/System Task/));
});
it('should validate POST /login/reset-request', async function () {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset-request',
{
body: {
session: true
},
jar
}
), {
status: 400
});
});
it('should hide sensitive exceptions POST /login/reset-request', async function () {
let log;
const orig = apos.util.error;
apos.util.error = (m) => {
log = m;
};
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
await apos.http.post(
'/api/v1/@apostrophecms/login/reset-request',
{
body: {
email: 'invalidUser',
session: true
},
jar
}
);
assert.match(log, /invalidUser/);
const user = apos.user.newInstance();
user.title = 'noEmail';
user.username = 'noEmail';
user.password = 'secret';
user.role = 'guest';
await apos.user.insert(apos.task.getReq(), user);
await apos.http.post(
'/api/v1/@apostrophecms/login/reset-request',
{
body: {
email: 'noEmail',
session: true
},
jar
}
);
assert.match(log, /noEmail/);
apos.util.error = orig;
});
it('should reset password POST /login/reset-request (request)', async function () {
let args;
const orig = apos.login.email;
apos.login.email = (req, ...a) => {
args = a;
};
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
let user = apos.user.newInstance();
user.title = 'resetUser';
user.email = 'resetUser@example.com';
user.username = 'resetUser';
user.password = 'secret';
user.role = 'guest';
user = await apos.user.insert(apos.task.getReq(), user);
resetUserId = user._id;
await apos.http.post(
'/api/v1/@apostrophecms/login/reset-request',
{
body: {
email: 'resetUser',
session: true
},
jar
}
);
{
assert(Array.isArray(args));
const [ template, data, opts ] = args;
assert(template);
assert.deepEqual(data.user._id, user._id);
assert(data.user.passwordResetAt);
assert.match(data.url, /\/login\?reset=/);
assert.match(data.url, /&email=/);
assert(data.site);
assert.equal(opts.to, user.email);
assert(opts.subject);
}
await apos.http.post(
'/api/v1/@apostrophecms/login/reset-request',
{
body: {
email: 'resetUser@example.com',
session: true
},
jar
}
);
{
assert(Array.isArray(args));
const [ template, data, opts ] = args;
assert(template);
assert.deepEqual(data.user._id, user._id);
assert(data.user.passwordResetAt);
assert.match(data.url, /\/login\?reset=/);
assert.match(data.url, /&email=/);
assert(data.site);
assert.equal(opts.to, user.email);
assert(opts.subject);
// Safe the token for the reset tests
const url = new URL(data.url);
resetToken = url.searchParams.get('reset');
}
apos.login.email = orig;
});
it('should reset password GET /login/reset (validate)', async function () {
const user = await apos.doc.db.findOne({ _id: resetUserId });
// Fail
await assert.rejects(() => apos.http.get(
'/api/v1/@apostrophecms/login/reset',
{
qs: {}
}
), {
status: 400
});
await assert.rejects(() => apos.http.get(
'/api/v1/@apostrophecms/login/reset',
{
qs: {
reset: 'invalid',
email: user.username
}
}
), {
status: 400
});
await assert.rejects(() => apos.http.get(
'/api/v1/@apostrophecms/login/reset',
{
qs: {
reset: resetToken,
email: 'invalid'
}
}
), {
status: 400
});
// Success
await apos.http.get(
'/api/v1/@apostrophecms/login/reset',
{
qs: {
reset: resetToken,
email: user.username
}
}
);
await apos.http.get(
'/api/v1/@apostrophecms/login/reset',
{
qs: {
reset: resetToken,
email: user.email
}
}
);
});
it('should reset password POST /login/reset (validate & reset)', async function () {
const jar = apos.http.jar();
const user = await apos.doc.db.findOne({ _id: resetUserId });
await apos.http.get(
'/',
{
jar
}
);
// Validate
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
session: true
},
jar
}
), {
status: 400
});
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
reset: 'invalid',
email: user.email,
password: 'new more secret',
session: true
},
jar
}
), {
status: 400
});
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
reset: resetToken,
email: 'invalid',
password: 'new more secret',
session: true
},
jar
}
), {
status: 400
});
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
reset: resetToken,
email: user.email,
password: '',
session: true
},
jar
}
), {
status: 400
});
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
// explicit check for boolean cheat!
reset: false,
email: user.email,
password: 'new more secret',
session: true
},
jar
}
), {
status: 400
});
// Reset
await apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
reset: resetToken,
email: user.email,
password: 'new more secret',
session: true
},
jar
}
);
// Can not reset anymore
await assert.rejects(() => apos.http.post(
'/api/v1/@apostrophecms/login/reset',
{
body: {
reset: resetToken,
email: user.email,
password: 'new even more secret',
session: true
},
jar
}
), {
status: 400
});
// Login with the new password
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
body: {
username: user.email,
password: 'new more secret',
session: true
},
jar
}
);
const page = await apos.http.get(
'/',
{
jar
}
);
assert(page.match(/logged in/));
});
it('should find user by reset data', async function () {
let user = apos.user.newInstance();
user.title = 'getResetUser';
user.email = 'getResetUser@example.com';
user.username = 'getResetUser';
user.password = 'secret';
user.role = 'guest';
user = await apos.user.insert(apos.task.getReq(), user);
// Find by email
{
const found = await apos.login.getPasswordResetUser(user.email);
assert.equal(found._id, user._id);
}
// Find by username
{
const found = await apos.login.getPasswordResetUser(user.username);
assert.equal(found._id, user._id);
}
// Fail with no token
await assert.rejects(
() => apos.login.getPasswordResetUser(user.username, ''),
{
message: 'invalid'
}
);
await assert.rejects(
() => apos.login.getPasswordResetUser(user.username, null),
{
message: 'invalid'
}
);
await assert.rejects(
() => apos.login.getPasswordResetUser(user.username, 'invalid'),
{
message: 'notfound'
}
);
user.passwordReset = 'secret';
user.passwordResetAt = new Date();
user = await apos.user.update(apos.task.getReq(), user);
// Find by email and validate token
{
const found = await apos.login.getPasswordResetUser(user.email, 'secret');
assert.equal(found._id, user._id);
}
// // Find by username and validate token
{
const found = await apos.login.getPasswordResetUser(user.username, 'secret');
assert.equal(found._id, user._id);
}
await assert.rejects(
() => apos.login.getPasswordResetUser(user.username, 'invalid'),
{
message: 'Incorrect passwordReset'
}
);
// Expired
user.passwordResetAt = new Date(0);
user = await apos.user.update(apos.task.getReq(), user);
await assert.rejects(
() => apos.login.getPasswordResetUser(user.username, 'invalid'),
{
message: 'notfound'
}
);
});
it('should return an error with code 404 at GET login/whoami when user is not logged in', async function () {
try {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
await apos.http.post('/api/v1/@apostrophecms/login/whoami',
{
method: 'POST',
body: {
session: true
},
jar
}
);
assert.fail('Expected error but got success');
} catch (err) {
assert.strictEqual(err.status, 404);
}
});
it('should return user data at POST login/whoami when user is logged in', async function () {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'AnotherLovelyPassword',
session: true
},
jar
}
);
const whoamiResponse = await apos.http.post('/api/v1/@apostrophecms/login/whoami', {
method: 'POST',
body: {
session: true
},
jar
});
assert.ok(whoamiResponse._id);
assert.strictEqual(whoamiResponse.username, 'HarryPutter');
assert.strictEqual(whoamiResponse.title, 'Extra Cool Putter');
assert.strictEqual(whoamiResponse.email, 'hputter@aol.com');
});
it('should return user data with additional whoamiFields if explicitly added to the login module options when user is logged in', async function () {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
apos.modules['@apostrophecms/login'].options.whoamiFields = [ 'role' ];
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'AnotherLovelyPassword',
session: true
},
jar
}
);
const whoamiResponse = await apos.http.post('/api/v1/@apostrophecms/login/whoami', {
method: 'POST',
body: {
session: true
},
jar
});
assert.strictEqual(whoamiResponse.role, 'admin');
});
it('should not return user data with additional whoamiFields if not explicitly added to the login module options when user is logged in', async function () {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
// Reset the whoamiFields to default (empty)
apos.modules['@apostrophecms/login'].options.whoamiFields = [];
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'HarryPutter',
password: 'AnotherLovelyPassword',
session: true
},
jar
}
);
const whoamiResponse = await apos.http.post('/api/v1/@apostrophecms/login/whoami', {
method: 'POST',
body: {
session: true
},
jar
});
assert.ok(!('role' in whoamiResponse));
});
describe('localLogin: false', function () {
let apos2;
this.timeout(20000);
before(async function () {
apos2 = await t.create({
root: module,
modules: {
'@apostrophecms/express': {
options: {
apiKeys: {
adminApiKey: {
role: 'admin'
}
}
}
},
'@apostrophecms/login': {
options: {
localLogin: false,
passwordReset: true,
environmentLabel: 'test'
}
}
}
});
});
after(function () {
return t.destroy(apos2);
});
it('should return user data at POST login/whoami when user is logged in', async function () {
const actual = apos2.modules['@apostrophecms/login']._routes.map(({ method, url }) => ({
method,
url
}));
const expected = [
{
method: 'post',
url: '/api/v1/@apostrophecms/login/logout'
},
{
method: 'post',
url: '/api/v1/@apostrophecms/login/whoami'
},
{
method: 'get',
url: '/api/v1/@apostrophecms/login/whoami'
}
];
assert.deepEqual(actual, expected);
});
});
});
describe('Case Sensitivity', function() {
describe('Case Sensitive', function() {
let apos;
this.timeout(20000);
before(async function () {
apos = await t.create({
root: module
});
});
beforeEach(async function() {
await apos.doc.db.deleteMany({
type: '@apostrophecms/user'
});
await apos.user.safe.deleteMany();
});
after(function () {
return t.destroy(apos);
});
it('should be case sensitive by default', async function() {
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
const req = apos.task.getReq();
const user = {
title: 'virus',
username: 'Virus',
password: 'password',
email: 'Virus@gmail.com',
role: 'admin'
};
const doc = await apos.user.insert(req, user);
let loginError = false;
try {
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'Virus',
password: 'password',
session: true
},
jar
}
);
} catch (err) {
loginError = true;
}
const actual = {
loginError,
username: doc.username,
email: doc.email
};
const expected = {
loginError: false,
username: 'Virus',
email: 'Virus@gmail.com'
};
assert.deepEqual(actual, expected);
});
it('should migrate usernames and emails to lowercase when running the caseInsensitiveTask method', async function() {
const logError = apos.login.logError;
const errorLogs = {};
apos.login.logError = (name, text, data) => {
errorLogs[name] = {
text,
data
};
};
const req = apos.task.getReq();
const users = [
{
title: 'ViRus',
username: 'ViRus',
password: 'password',
email: 'VirUs@gmail.com',
role: 'admin'
},
{
title: 'toto',
username: 'ToTo',
password: 'password',
email: 'toto@gmail.com',
role: 'admin'
},
{
title: 'tata',
username: 'tata',
password: 'password',
email: 'TaTa@gmail.com',
role: 'admin'
},
{
title: 'Conflict',
username: 'Conflict',
password: 'password',
email: 'conflict@gmail.com',
role: 'admin'
},
{
title: 'Conflict',
username: 'conflict',
password: 'password',
email: 'conflict2@gmail.com',
role: 'admin'
}
];
for (const user of users) {
await apos.user.insert(req, user);
}
const inserted = (await apos.user
.find(req, {})
.toArray())
.map(({ username, email }) => ({
username,
email
}));
await apos.task.invoke('@apostrophecms/login:case-insensitive');
const updated = (await apos.user
.find(req, {})
.toArray())
.map(({ username, email }) => ({
username,
email
}));
const actual = {
inserted,
updated,
errorLogs
};
const errorFailed = errorLogs['conflicting-usernames'].data.failed[0];
const expected = {
inserted: [
{
username: 'conflict',
email: 'conflict2@gmail.com'
},
{
username: 'Conflict',
email: 'conflict@gmail.com'
},
{
username: 'tata',
email: 'TaTa@gmail.com'
},
{
username: 'ToTo',
email: 'toto@gmail.com'
},
{
username: 'ViRus',
email: 'VirUs@gmail.com'
}
],
updated: [
{
username: 'conflict',
email: 'conflict2@gmail.com'
},
{
username: 'Conflict',
email: 'conflict@gmail.com'
},
{
username: 'tata',
email: 'tata@gmail.com'
},
{
username: 'toto',
email: 'toto@gmail.com'
},
{
username: 'virus',
email: 'virus@gmail.com'
}
],
errorLogs: {
'conflicting-usernames': {
text: 'Accounts with certain usernames and/or emails would be in conflict with other accounts if changed to lowercase. Please review the following usernames and emails and address them manually.',
data: {
failed: [
{
conflictingFields: {
username: 'conflict'
},
user: {
_id: errorFailed.user._id,
email: 'conflict@gmail.com',
username: 'Conflict'
}
}
]
}
}
}
};
assert.deepEqual(actual, expected);
apos.login.logError = logError;
});
});
describe('Case Insensitive', function() {
let apos;
this.timeout(20000);
before(async function () {
apos = await t.create({
root: module,
modules: {
'@apostrophecms/login': {
options: {
caseInsensitive: true
}
}
}
});
});
beforeEach(async function() {
await apos.doc.db.deleteMany({
type: '@apostrophecms/user'
});
await apos.user.safe.deleteMany();
});
after(function () {
return t.destroy(apos);
});
it('should normalize users insertion in docs and safe collections in lowecase', async function() {
const req = apos.task.getReq();
const users = [
{
title: 'ViRus',
username: 'ViRus',
password: 'password',
email: 'VirUs@gmail.com',
role: 'admin'
},
{
title: 'toto',
username: 'ToTo',
password: 'password',
email: 'toto@gmail.com',
role: 'admin'
},
{
title: 'tata',
username: 'tata',
password: 'password',
email: 'TaTa@gmail.com',
role: 'admin'
},
{
title: 'Conflict',
username: 'Conflict',
password: 'password',
email: 'conflict@gmail.com',
role: 'admin'
}
];
const userConflicting = {
title: 'Conflict',
username: 'conflict',
password: 'password',
email: 'conflict2@gmail.com',
role: 'admin'
};
for (const user of users) {
await apos.user.insert(req, user);
}
const inserted = await apos.doc.db
.find({ type: '@apostrophecms/user' })
.project({
_id: 0,
username: 1,
email: 1
})
.sort({ username: 1 })
.toArray();
const safe = (await apos.user.safe
.find({})
.project({
_id: 0,
username: 1
})
.sort({ username: 1 })
.toArray());
let conflictError = false;
try {
await apos.user.insert(req, userConflicting);
} catch (err) {
conflictError = true;
}
const actual = {
inserted,
safe,
conflictError
};
const expected = {
inserted: [
{
username: 'conflict',
email: 'conflict@gmail.com'
},
{
username: 'tata',
email: 'tata@gmail.com'
},
{
username: 'toto',
email: 'toto@gmail.com'
},
{
username: 'virus',
email: 'virus@gmail.com'
}
],
safe: [
{ username: 'conflict' },
{ username: 'tata' },
{ username: 'toto' },
{ username: 'virus' }
],
conflictError: true
};
assert.deepEqual(actual, expected);
});
it('should normalize login username / email in lowercase during connection', async function() {
const req = apos.task.getReq();
const jar = apos.http.jar();
await apos.http.get(
'/',
{
jar
}
);
const users = [
{
title: 'toto',
username: 'ToTo',
password: 'password',
email: 'toto@gmail.com',
role: 'admin'
},
{
title: 'tata',
username: 'tata',
password: 'password',
email: 'TaTa@gmail.com',
role: 'admin'
}
];
for (const user of users) {
await apos.user.insert(req, user);
}
let totoLoginError = false;
try {
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'ToTO',
password: 'password',
session: true
},
jar
}
);
} catch (err) {
totoLoginError = true;
}
let tataLoginError = false;
try {
await apos.http.post(
'/api/v1/@apostrophecms/login/login',
{
method: 'POST',
body: {
username: 'TATA@gmail.COm',
password: 'password',
session: true
},
jar
}
);
} catch (err) {
tataLoginError = true;
}
const actual = {
totoLoginError,
tataLoginError
};
const expected = {
totoLoginError: false,
tataLoginError: false
};
assert.deepEqual(actual, expected);
});
});
});