diff --git a/test/api/v3/integration/challenges/GET-challenges_challengeId_members.test.js b/test/api/v3/integration/challenges/GET-challenges_challengeId_members.test.js index 182de5aa30..a7d750fdb9 100644 --- a/test/api/v3/integration/challenges/GET-challenges_challengeId_members.test.js +++ b/test/api/v3/integration/challenges/GET-challenges_challengeId_members.test.js @@ -149,7 +149,10 @@ describe('GET /challenges/:challengeId/members', () => { let usersToGenerate = []; for (let i = 0; i < 3; i++) { - usersToGenerate.push(generateUser({challenges: [challenge._id]})); + usersToGenerate.push(generateUser({ + challenges: [challenge._id], + 'profile.name': `${i}profilename`, + })); } let generatedUsers = await Promise.all(usersToGenerate); let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name); diff --git a/test/api/v3/integration/user/auth/POST-register_local.test.js b/test/api/v3/integration/user/auth/POST-register_local.test.js index 046009d997..d916831047 100644 --- a/test/api/v3/integration/user/auth/POST-register_local.test.js +++ b/test/api/v3/integration/user/auth/POST-register_local.test.js @@ -6,10 +6,14 @@ import { getProperty, } from '../../../../../helpers/api-integration/v3'; import { ApiUser } from '../../../../../helpers/api-integration/api-classes'; -import { v4 as generateRandomUserName } from 'uuid'; +import { v4 as uuid } from 'uuid'; import { each } from 'lodash'; import { encrypt } from '../../../../../../website/server/libs/encryption'; +function generateRandomUserName () { + return (Date.now() + uuid()).substring(0, 20); +} + describe('POST /user/auth/local/register', () => { context('username and email are free', () => { let api; @@ -37,7 +41,8 @@ describe('POST /user/auth/local/register', () => { expect(user.newUser).to.eql(true); }); - it('remove spaces from username', async () => { + xit('remove spaces from username', async () => { + // TODO can probably delete this test now let username = ' usernamewithspaces '; let email = 'test@example.com'; let password = 'password'; diff --git a/test/helpers/api-integration/v3/object-generators.js b/test/helpers/api-integration/v3/object-generators.js index c2aa0fdeab..8322ae1098 100644 --- a/test/helpers/api-integration/v3/object-generators.js +++ b/test/helpers/api-integration/v3/object-generators.js @@ -15,7 +15,7 @@ import * as Tasks from '../../../../website/server/models/task'; // , you can do so by passing in the full path as a string: // { 'items.eggs.Wolf': 10 } export async function generateUser (update = {}) { - let username = generateUUID(); + let username = (Date.now() + generateUUID()).substring(0, 20); let password = 'password'; let email = `${username}@example.com`; diff --git a/website/common/locales/en/front.json b/website/common/locales/en/front.json index b01e184e36..849863d0ac 100644 --- a/website/common/locales/en/front.json +++ b/website/common/locales/en/front.json @@ -275,6 +275,8 @@ "emailTaken": "Email address is already used in an account.", "newEmailRequired": "Missing new email address.", "usernameTaken": "Login Name already taken.", + "usernameWrongLength": "Login Name must be between 1 and 20 characters long.", + "usernameBadCharacters": "Login Name must contain only letters a to z, numbers 0 to 9, hyphens, or underscores.", "passwordConfirmationMatch": "Password confirmation doesn't match password.", "invalidLoginCredentials": "Incorrect username and/or email and/or password.", "passwordResetPage": "Reset Password", diff --git a/website/server/controllers/api-v3/auth.js b/website/server/controllers/api-v3/auth.js index 4a52e90d01..910568ed4a 100644 --- a/website/server/controllers/api-v3/auth.js +++ b/website/server/controllers/api-v3/auth.js @@ -25,6 +25,8 @@ import { validatePasswordResetCodeAndFindUser, convertToBcrypt} from '../../libs const BASE_URL = nconf.get('BASE_URL'); const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL'); const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL'); +const USERNAME_LENGTH_MIN = 1; +const USERNAME_LENGTH_MAX = 20; let api = {}; @@ -78,11 +80,11 @@ function hasBackupAuth (user, networkToRemove) { /** * @api {post} /api/v3/user/auth/local/register Register - * @apiDescription Register a new user with email, username and password or attach local auth to a social user + * @apiDescription Register a new user with email, login name, and password or attach local auth to a social user * @apiName UserRegisterLocal * @apiGroup User * - * @apiParam (Body) {String} username Username of the new user + * @apiParam (Body) {String} username Login name of the new user. Must be 1-36 characters, containing only a-z, 0-9, hyphens (-), or underscores (_). * @apiParam (Body) {String} email Email address of the new user * @apiParam (Body) {String} password Password for the new user * @apiParam (Body) {String} confirmPassword Password confirmation @@ -101,7 +103,12 @@ api.registerLocal = { notEmpty: {errorMessage: res.t('missingEmail')}, isEmail: {errorMessage: res.t('notAnEmail')}, }, - username: {notEmpty: {errorMessage: res.t('missingUsername')}}, + username: { + notEmpty: {errorMessage: res.t('missingUsername')}, + isLength: {options: {min: USERNAME_LENGTH_MIN, max: USERNAME_LENGTH_MAX}, errorMessage: res.t('usernameWrongLength')}, + // TODO use the constants in the error message above + matches: {options: /^[-_a-zA-Z0-9]+$/, errorMessage: res.t('usernameBadCharacters')}, + }, password: { notEmpty: {errorMessage: res.t('missingPassword')}, equals: {options: [req.body.confirmPassword], errorMessage: res.t('passwordConfirmationMatch')},