Skip to content

Commit

Permalink
Merge 842151d into 5a83664
Browse files Browse the repository at this point in the history
  • Loading branch information
phillipthelen committed Oct 2, 2018
2 parents 5a83664 + 842151d commit 32d3038
Show file tree
Hide file tree
Showing 35 changed files with 1,270 additions and 176 deletions.
90 changes: 90 additions & 0 deletions migrations/users/generate-usernames.js
@@ -0,0 +1,90 @@
let authorName = 'Sabe'; // in case script author needs to know when their ...
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done

/*
* Generate usernames for users who lack them
*/

import monk from 'monk';
import nconf from 'nconf';
import { generateUsername } from '../../website/server/libs/auth/utils';
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });

function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
let query = {
'auth.local.username': {$exists: false},
'auth.timestamps.loggedin': {$gt: new Date('2018-04-01')}, // Initial coverage for users active within last 6 months
};

if (lastId) {
query._id = {
$gt: lastId,
};
}

dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'auth',
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch((err) => {
console.log(err);
return exiting(1, `ERROR! ${ err}`);
});
}

let progressCount = 1000;
let count = 0;

function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}

let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];

return Promise.all(userPromises)
.then(() => {
processUsers(lastUser._id);
});
}

function updateUser (user) {
count++;

if (!user.auth.local.username) {
dbUsers.update({_id: user._id}, {$set: {'auth.local.username': generateUsername() }});
}
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
if (user._id === authorUuid) console.warn(`${authorName } processed`);
}

function displayData () {
console.warn(`\n${ count } users processed\n`);
return exiting(0);
}

function exiting (code, msg) {
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
console.log(msg);
}
}
process.exit(code);
}

module.exports = processUsers;
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -83,6 +83,7 @@
"rimraf": "^2.4.3",
"sass-loader": "^7.0.0",
"shelljs": "^0.8.2",
"short-uuid": "^3.0.0",
"smartbanner.js": "^1.9.1",
"stripe": "^5.9.0",
"superagent": "^3.8.3",
Expand Down
20 changes: 18 additions & 2 deletions test/api/v3/integration/user/PUT-user.test.js
Expand Up @@ -54,7 +54,7 @@ describe('PUT /user', () => {
});


it('profile.name cannot be an empty string or null', async () => {
it('validates profile.name', async () => {
await expect(user.put('/user', {
'profile.name': ' ', // string should be trimmed
})).to.eventually.be.rejected.and.eql({
Expand All @@ -76,7 +76,23 @@ describe('PUT /user', () => {
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'User validation failed',
message: t('invalidReqParams'),
});

await expect(user.put('/user', {
'profile.name': 'this is a very long display name that will not be allowed due to length',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('displaynameIssueLength'),
});

await expect(user.put('/user', {
'profile.name': 'TESTPLACEHOLDERSLURWORDHERE',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('displaynameIssueSlur'),
});
});
});
Expand Down
17 changes: 17 additions & 0 deletions test/api/v3/integration/user/auth/POST-register_local.test.js
Expand Up @@ -41,6 +41,23 @@ describe('POST /user/auth/local/register', () => {
expect(user.newUser).to.eql(true);
});

it('registers a new user and sets verifiedUsername to true', async () => {
let username = generateRandomUserName();
let email = `${username}@example.com`;
let password = 'password';

let user = await api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});

expect(user._id).to.exist;
expect(user.apiToken).to.exist;
expect(user.flags.verifiedUsername).to.eql(true);
});

xit('remove spaces from username', async () => {
// TODO can probably delete this test now
let username = ' usernamewithspaces ';
Expand Down
Expand Up @@ -39,15 +39,18 @@ describe('POST /user/auth/social', () => {
});

it('registers a new user', async () => {
let response = await api.post(endpoint, {
const response = await api.post(endpoint, {
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
network,
});

expect(response.apiToken).to.exist;
expect(response.id).to.exist;
expect(response.newUser).to.be.true;
expect(response.username).to.exist;

await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist;
});

it('logs an existing user in', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/api/v4/user/PUT-user.test.js
Expand Up @@ -76,7 +76,7 @@ describe('PUT /user', () => {
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'User validation failed',
message: t('invalidReqParams'),
});
});
});
Expand Down
89 changes: 89 additions & 0 deletions test/api/v4/user/auth/POST-user_verify_username.test.js
@@ -0,0 +1,89 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v4';

const ENDPOINT = '/user/auth/verify-username';

describe('POST /user/auth/verify-username', async () => {
let user;

beforeEach(async () => {
user = await generateUser();
});

it('successfully verifies username', async () => {
let newUsername = 'new-username';
let response = await user.post(ENDPOINT, {
username: newUsername,
});
expect(response).to.eql({ isUsable: true });
});

it('successfully verifies username with allowed characters', async () => {
let newUsername = 'new-username_123';
let response = await user.post(ENDPOINT, {
username: newUsername,
});
expect(response).to.eql({ isUsable: true });
});

context('errors', async () => {
it('errors if username is not provided', async () => {
await expect(user.post(ENDPOINT, {
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});

it('errors if username is a slur', async () => {
await expect(user.post(ENDPOINT, {
username: 'TESTPLACEHOLDERSLURWORDHERE',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueLength'), t('usernameIssueSlur')] });
});

it('errors if username contains a slur', async () => {
await expect(user.post(ENDPOINT, {
username: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueLength'), t('usernameIssueSlur')] });
await expect(user.post(ENDPOINT, {
username: 'something_TESTPLACEHOLDERSLURWORDHERE',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueLength'), t('usernameIssueSlur')] });
await expect(user.post(ENDPOINT, {
username: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueLength'), t('usernameIssueSlur')] });
});

it('errors if username is not allowed', async () => {
await expect(user.post(ENDPOINT, {
username: 'support',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueForbidden')] });
});

it('errors if username is not allowed regardless of casing', async () => {
await expect(user.post(ENDPOINT, {
username: 'SUppORT',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueForbidden')] });
});

it('errors if username has incorrect length', async () => {
await expect(user.post(ENDPOINT, {
username: 'thisisaverylongusernameover20characters',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueLength')] });
});

it('errors if username contains invalid characters', async () => {
await expect(user.post(ENDPOINT, {
username: 'Eichhörnchen',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueInvalidCharacters')] });
await expect(user.post(ENDPOINT, {
username: 'test.name',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueInvalidCharacters')] });
await expect(user.post(ENDPOINT, {
username: '🤬',
})).to.eventually.eql({ isUsable: false, issues: [t('usernameIssueInvalidCharacters')] });
});
});
});

0 comments on commit 32d3038

Please sign in to comment.