From 3cd2b28bc26bcb89b1a42eed2713c91975859882 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 16:48:59 -0700 Subject: [PATCH 01/11] Inital rename of displayName -> username --- bin/cli-setup | 10 ++-- bin/cli-users | 18 +++---- .../src/components/ActionButton.js | 2 +- client/coral-admin/src/components/Comment.js | 2 +- client/coral-admin/src/components/User.js | 2 +- .../src/containers/Community/Community.js | 2 +- .../src/containers/Community/Table.js | 2 +- .../components/Steps/CreateYourAccount.js | 4 +- client/coral-admin/src/reducers/install.js | 4 +- client/coral-admin/src/translations.json | 2 +- client/coral-embed-stream/src/Embed.js | 2 +- client/coral-embed-stream/src/Stream.js | 2 +- client/coral-framework/actions/auth.js | 4 +- client/coral-framework/actions/user.js | 4 +- .../components/SuspendedAccount.js | 18 +++---- .../graphql/fragments/commentView.graphql | 2 +- client/coral-framework/helpers/error.js | 2 +- client/coral-framework/helpers/validate.js | 2 +- client/coral-framework/reducers/auth.js | 2 +- client/coral-framework/reducers/user.js | 2 +- client/coral-framework/translations.json | 14 +++--- .../RileysAwesomeCommentBox.js | 2 +- client/coral-plugin-stream/Stream.js | 2 +- .../components/SettingsHeader.js | 2 +- .../components/CreateDisplayNameDialog.js | 10 ++-- .../coral-sign-in/components/SignUpContent.js | 12 ++--- client/coral-sign-in/components/UserBox.js | 2 +- .../containers/ChangeDisplayNameContainer.js | 2 +- .../containers/SignInContainer.js | 2 +- client/coral-sign-in/translations.js | 10 ++-- docs/swagger.yaml | 6 +-- errors.js | 4 +- graph/typeDefs.graphql | 4 +- models/user.js | 16 ++++-- routes/api/account/index.js | 4 +- routes/api/auth/index.js | 2 +- routes/api/setup/index.js | 4 +- routes/api/users/index.js | 4 +- services/passport.js | 2 +- services/setup.js | 10 ++-- services/users.js | 50 +++++++++---------- services/wordlist.js | 8 +-- test/e2e/pages/embedStreamPage.js | 4 +- test/e2e/tests/EmbedStreamTests.js | 4 +- test/e2e/tests/Visitor/SignUpTest.js | 2 +- test/routes/api/account/index.js | 12 ++--- test/routes/api/auth/index.js | 4 +- test/routes/api/comments/index.js | 12 ++--- test/routes/api/queue/index.js | 8 +-- test/services/comments.js | 4 +- test/services/users.js | 23 ++++----- views/auth-callback.ejs | 2 +- 52 files changed, 171 insertions(+), 164 deletions(-) diff --git a/bin/cli-setup b/bin/cli-setup index 185b04f01..7985bf220 100755 --- a/bin/cli-setup +++ b/bin/cli-setup @@ -116,11 +116,11 @@ const performSetup = () => { return inquirer.prompt([ { type: 'input', - name: 'displayName', - message: 'Display Name', - filter: (displayName) => { + name: 'username', + message: 'Username', + filter: (username) => { return UsersService - .isValidDisplayName(displayName, false) + .isValidDisplayName(username, false) .catch((err) => { throw err.message; }); @@ -174,7 +174,7 @@ const performSetup = () => { settings: settings.toObject(), user: { email: user.email, - displayName: user.displayName, + username: user.username, password: user.password } }); diff --git a/bin/cli-users b/bin/cli-users index 01f46ad2e..473c07d9c 100755 --- a/bin/cli-users +++ b/bin/cli-users @@ -32,7 +32,7 @@ function getUserCreateAnswers(options) { email: options.email, password: options.password, confirmPassword: options.password, - displayName: options.name, + username: options.name, roles: [] }; @@ -75,11 +75,11 @@ function getUserCreateAnswers(options) { } }, { - name: 'displayName', - message: 'Display Name', - filter: (displayName) => { + name: 'username', + message: 'Username', + filter: (username) => { return UsersService - .isValidDisplayName(displayName) + .isValidDisplayName(username) .catch((err) => { throw err.message; }); @@ -108,7 +108,7 @@ function createUser(options) { }) .then((answers) => { return UsersService - .createLocalUser(answers.email.trim(), answers.password.trim(), answers.displayName.trim()) + .createLocalUser(answers.email.trim(), answers.password.trim(), answers.username.trim()) .then((user) => { console.log(`Created user ${user.id}.`); @@ -209,7 +209,7 @@ function updateUser(userID, options) { 'id': userID }, { $set: { - displayName: options.name + username: options.name } }); @@ -238,7 +238,7 @@ function listUsers() { let table = new Table({ head: [ 'ID', - 'Display Name', + 'Username', 'Profiles', 'Roles', 'Status', @@ -249,7 +249,7 @@ function listUsers() { users.forEach((user) => { table.push([ user.id, - user.displayName, + user.username, user.profiles.map((p) => p.provider).join(', '), user.roles.join(', '), user.status, diff --git a/client/coral-admin/src/components/ActionButton.js b/client/coral-admin/src/components/ActionButton.js index 61de1c669..14b7fe95d 100644 --- a/client/coral-admin/src/components/ActionButton.js +++ b/client/coral-admin/src/components/ActionButton.js @@ -18,7 +18,7 @@ const ActionButton = ({option, type, comment = {}, user, menuOptionsMap, onClick className={`ban ${styles.banButton}`} cStyle='darkGrey' disabled={banned ? 'disabled' : ''} - onClick={() => onClickShowBanDialog(user.id, user.displayName, comment.id) + onClick={() => onClickShowBanDialog(user.id, user.username, comment.id) } raised > diff --git a/client/coral-admin/src/components/Comment.js b/client/coral-admin/src/components/Comment.js index 74b0361a2..db5cfe5bf 100644 --- a/client/coral-admin/src/components/Comment.js +++ b/client/coral-admin/src/components/Comment.js @@ -23,7 +23,7 @@ const Comment = props => {
  • - {author.displayName || lang.t('comment.anon')} + {author.username || lang.t('comment.anon')} {timeago().format(comment.createdAt || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))} {comment.flagged ?

    {lang.t('comment.flagged')}

    : null}
    diff --git a/client/coral-admin/src/components/User.js b/client/coral-admin/src/components/User.js index 54de0375e..6b08ed76a 100644 --- a/client/coral-admin/src/components/User.js +++ b/client/coral-admin/src/components/User.js @@ -18,7 +18,7 @@ const User = props => {
  • - {user.displayName} + {user.username}
    diff --git a/client/coral-admin/src/containers/Community/Community.js b/client/coral-admin/src/containers/Community/Community.js index d63dd1e96..bad5654cf 100644 --- a/client/coral-admin/src/containers/Community/Community.js +++ b/client/coral-admin/src/containers/Community/Community.js @@ -13,7 +13,7 @@ const lang = new I18n(translations); const tableHeaders = [ { title: lang.t('community.username_and_email'), - field: 'displayName' + field: 'username' }, { title: lang.t('community.account_creation_date'), diff --git a/client/coral-admin/src/containers/Community/Table.js b/client/coral-admin/src/containers/Community/Table.js index 254550e74..480ce72bf 100644 --- a/client/coral-admin/src/containers/Community/Table.js +++ b/client/coral-admin/src/containers/Community/Table.js @@ -44,7 +44,7 @@ class Table extends Component { {commenters.map((row, i)=> ( - {row.displayName} + {row.username} {row.profiles.map(({id}) => id)} diff --git a/client/coral-admin/src/containers/Install/components/Steps/CreateYourAccount.js b/client/coral-admin/src/containers/Install/components/Steps/CreateYourAccount.js index 91c664c37..90deff640 100644 --- a/client/coral-admin/src/containers/Install/components/Steps/CreateYourAccount.js +++ b/client/coral-admin/src/containers/Install/components/Steps/CreateYourAccount.js @@ -21,12 +21,12 @@ const InitialStep = props => { ({ }); }, clearNotification: () => dispatch(clearNotification()), - editName: (displayName) => dispatch(editName(displayName)), + editName: (username) => dispatch(editName(username)), showSignInDialog: (offset) => dispatch(showSignInDialog(offset)), logout: () => dispatch(logout()), dispatch: d => dispatch(d) diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index 65743e6c7..8f84bb3bf 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -10,7 +10,7 @@ class Stream extends React.Component { asset: PropTypes.object.isRequired, comments: PropTypes.array.isRequired, currentUser: PropTypes.shape({ - displayName: PropTypes.string, + username: PropTypes.string, id: PropTypes.string }) } diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index bda46ae42..a2206fc09 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -15,11 +15,11 @@ export const hideCreateDisplayNameDialog = () => ({type: actions.HIDE_CREATEDISP const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS}); const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error}); -export const updateDisplayName = ({displayName}) => ({type: actions.UPDATE_DISPLAYNAME, displayName}); +export const updateDisplayName = ({username}) => ({type: actions.UPDATE_DISPLAYNAME, username}); export const createDisplayName = (userId, formData) => dispatch => { dispatch(createDisplayNameRequest()); - coralApi('/account/displayname', {method: 'PUT', body: formData}) + coralApi('/account/username', {method: 'PUT', body: formData}) .then(() => { dispatch(createDisplayNameSuccess()); dispatch(hideCreateDisplayNameDialog()); diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index 42d2e0398..bf2a77402 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -5,8 +5,8 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import translations from './../translations'; const lang = new I18n(translations); -export const editName = (displayName) => (dispatch) => { - return coralApi('/account/displayname', {method: 'PUT', body: {displayName}}) +export const editName = (username) => (dispatch) => { + return coralApi('/account/username', {method: 'PUT', body: {username}}) .then(() => { dispatch(addNotification('success', lang.t('successNameUpdate'))); }); diff --git a/client/coral-framework/components/SuspendedAccount.js b/client/coral-framework/components/SuspendedAccount.js index 4c221aa81..00adc9952 100644 --- a/client/coral-framework/components/SuspendedAccount.js +++ b/client/coral-framework/components/SuspendedAccount.js @@ -14,16 +14,16 @@ class SuspendedAccount extends Component { } state = { - displayName: '', + username: '', alert: '' } onSubmitClick = (e) => { const {editName} = this.props; - const {displayName} = this.state; + const {username} = this.state; e.preventDefault(); - if (validate.displayName(displayName)) { - editName(displayName) + if (validate.username(username)) { + editName(username) .then(() => location.reload()) .catch((error) => { this.setState({alert: lang.t(`error.${error.message}`)}); @@ -36,7 +36,7 @@ class SuspendedAccount extends Component { render () { const {canEditName} = this.props; - const {displayName, alert} = this.state; + const {username, alert} = this.state; return
    { @@ -51,7 +51,7 @@ class SuspendedAccount extends Component { {alert}
    diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 03c083070..3a3404174 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -21,13 +21,13 @@ class SignUpContent extends React.Component { showErrors: PropTypes.bool, errors: PropTypes.shape({ email: PropTypes.string, - displayName: PropTypes.string, + username: PropTypes.string, password: PropTypes.string, confirmPassword: PropTypes.string, }), formData: PropTypes.shape({ email: PropTypes.string, - displayName: PropTypes.string, + username: PropTypes.string, password: PropTypes.string, confirmPassword: PropTypes.string }) @@ -89,12 +89,12 @@ class SignUpContent extends React.Component { onChange={handleChange} /> (
    {lang.t('signIn.loggedInAs')} - changeTab(1)}>{user.displayName}. {lang.t('signIn.notYou')} + changeTab(1)}>{user.username}. {lang.t('signIn.notYou')} {lang.t('signIn.logout')}
    ); diff --git a/client/coral-sign-in/containers/ChangeDisplayNameContainer.js b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js index bc7fe8267..625985b63 100644 --- a/client/coral-sign-in/containers/ChangeDisplayNameContainer.js +++ b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js @@ -21,7 +21,7 @@ import { class ChangeDisplayNameContainer extends Component { initialState = { formData: { - displayName: '', + username: '', }, errors: {}, showErrors: false diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js index c92fe024d..fd9762ee3 100644 --- a/client/coral-sign-in/containers/SignInContainer.js +++ b/client/coral-sign-in/containers/SignInContainer.js @@ -29,7 +29,7 @@ class SignInContainer extends Component { initialState = { formData: { email: '', - displayName: '', + username: '', password: '', confirmPassword: '' }, diff --git a/client/coral-sign-in/translations.js b/client/coral-sign-in/translations.js index e8014014c..36ce3fce4 100644 --- a/client/coral-sign-in/translations.js +++ b/client/coral-sign-in/translations.js @@ -19,7 +19,7 @@ export default { register: 'Register', signUp: 'Sign Up', confirmPassword: 'Confirm Password', - displayName: 'Display Name', + username: 'Username', alreadyHaveAnAccount: 'Already have an account?', recoverPassword: 'Recover password', emailInUse: 'Email address already in use', @@ -32,10 +32,10 @@ export default { 'createdisplay': { writeyourusername: 'Write your username', yourusername: 'Your username is publicly visible on all comments you post. A username is needed before you can post your first comment.', - displayName: 'Display Name', + username: 'Username', save: 'Save', requiredField: 'Required field', - errorCreate: 'Error when changing display name', + errorCreate: 'Error when changing username', checkTheForm: 'Invalid Form. Please, check the fields', specialCharacters: 'Display names can contain letters, numbers and _ only' }, @@ -60,7 +60,7 @@ export default { register: 'Regístrate', signUp: 'Registro', confirmPassword: 'Confirmar Contraseña', - displayName: 'Nombre', + username: 'Nombre', alreadyHaveAnAccount: 'Ya tienes una cuenta?', recoverPassword: 'Recuperar contraseña', emailInUse: 'Este email se encuentra en uso', @@ -73,7 +73,7 @@ export default { 'createdisplay': { writeyourusername: 'Escribe tu nombre', yourusername: 'Tu nombre es visible publicamente en todos los comentarios que publiques. Es necesario tener un nombre de usuario antes de poder publicar tu primer comentario.', - displayName: 'Nombre a mostrar', + username: 'Nombre a mostrar', save: 'Guardar', requiredField: 'Campo necesario', errorCreate: 'Hubo un error al cambiar el nombre de usuario', diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6358f8f06..10e8f34f6 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -515,7 +515,7 @@ paths: - name: value in: query type: string - description: A term to search users' displayNames and email addresses. + description: A term to search users' usernames and email addresses. - name: sort in: query type: string @@ -576,7 +576,7 @@ paths: format: email password: type: string - displayName: + username: type: string responses: 201: @@ -899,7 +899,7 @@ definitions: id: type: string description: The uuid.v4 id of the user. - displayName: + username: type: string description: The name appearing next to the user's comments. disabled: diff --git a/errors.js b/errors.js index 7ee932c96..662459cdf 100644 --- a/errors.js +++ b/errors.js @@ -59,12 +59,12 @@ const ErrDisplayTaken = new APIError('Display name already in use', { status: 400 }); -const ErrSpecialChars = new APIError('No special characters are allowed in a display name', { +const ErrSpecialChars = new APIError('No special characters are allowed in a username', { translation_key: 'NO_SPECIAL_CHARACTERS', status: 400 }); -const ErrMissingDisplay = new APIError('A display name is required to create a user', { +const ErrMissingDisplay = new APIError('A username is required to create a user', { translation_key: 'DISPLAY_NAME_REQUIRED', status: 400 }); diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 8369b93c6..9426d128a 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -47,8 +47,8 @@ enum USER_ROLES { type User { id: ID! - # display name of a user. - displayName: String! + # username of a user. + username: String! # actions against a specific user. actions: [ActionSummary] diff --git a/models/user.js b/models/user.js index 06dc3c7d1..b142be5a0 100644 --- a/models/user.js +++ b/models/user.js @@ -12,7 +12,7 @@ const USER_STATUS = [ 'ACTIVE', 'BANNED', 'PENDING', - 'APPROVED' // Indicates that the users' displayname has been approved + 'APPROVED' // Indicates that the users' username has been approved ]; // ProfileSchema is the mongoose schema defined as the representation of a @@ -61,10 +61,8 @@ const UserSchema = new mongoose.Schema({ // This is sourced from the social provider or set manually during user setup // and simply provides a name to display for the given user. - displayName: { + username: { type: String, - unique: true, - lowercase: true, required: true }, @@ -128,6 +126,16 @@ UserSchema.index({ background: false }); +UserSchema.index({ + 'username': 1 +}, { + unique: true, + collation: { + locale: 'en_US', + strength: 2 + } +}); + /** * Returns true if the user has all the roles specified. */ diff --git a/routes/api/account/index.js b/routes/api/account/index.js index d383e9f36..3de9d41c3 100644 --- a/routes/api/account/index.js +++ b/routes/api/account/index.js @@ -115,9 +115,9 @@ router.put('/password/reset', (req, res, next) => { }); }); -router.put('/displayname', authorization.needed(), (req, res, next) => { +router.put('/username', authorization.needed(), (req, res, next) => { UsersService - .editName(req.user.id, req.body.displayName) + .editName(req.user.id, req.body.username) .then(() => { res.status(204).end(); }) diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index e3acf2c77..dcce15933 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -70,7 +70,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => { return res.render('auth-callback', {err: JSON.stringify(errors.ErrNotAuthorized), data: null}); } - // Authorize the user to edit their displayName. + // Authorize the user to edit their username. UsersService.toggleNameEdit(user.id, true) .then(() => { diff --git a/routes/api/setup/index.js b/routes/api/setup/index.js index 60f7d7fd2..e325769cd 100644 --- a/routes/api/setup/index.js +++ b/routes/api/setup/index.js @@ -32,11 +32,11 @@ router.post('/', (req, res, next) => { const { settings, - user: {email, password, displayName} + user: {email, password, username} } = req.body; SetupService - .setup({settings, user: {email, password, displayName}}) + .setup({settings, user: {email, password, username}}) .then(() => { // We're setup! diff --git a/routes/api/users/index.js b/routes/api/users/index.js index 1ce429cb4..4182beb49 100644 --- a/routes/api/users/index.js +++ b/routes/api/users/index.js @@ -120,11 +120,11 @@ const SendEmailConfirmation = (app, userID, email, referer) => UsersService // create a local user. router.post('/', (req, res, next) => { - const {email, password, displayName} = req.body; + const {email, password, username} = req.body; const redirectUri = req.header('X-Pym-Url') || req.header('Referer'); UsersService - .createLocalUser(email, password, displayName) + .createLocalUser(email, password, username) .then((user) => { // Send an email confirmation. The Front end will know about the diff --git a/services/passport.js b/services/passport.js index dcb6904ec..b56834cc7 100644 --- a/services/passport.js +++ b/services/passport.js @@ -102,7 +102,7 @@ if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && clientID: process.env.TALK_FACEBOOK_APP_ID, clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`, - profileFields: ['id', 'displayName', 'picture.type(large)'] + profileFields: ['id', 'username', 'picture.type(large)'] }, (accessToken, refreshToken, profile, done) => { UsersService .findOrCreateExternalUser(profile) diff --git a/services/setup.js b/services/setup.js index 74ef1d431..2bfbc66a0 100644 --- a/services/setup.js +++ b/services/setup.js @@ -45,7 +45,7 @@ module.exports = class SetupService { /** * This verifies that the current input for the setup is valid. */ - static validate({settings, user: {email, displayName, password}}) { + static validate({settings, user: {email, username, password}}) { // Verify the email address of the user. if (!email) { @@ -57,7 +57,7 @@ module.exports = class SetupService { // Verify other properties of the user. return Promise.all([ - UsersService.isValidDisplayName(displayName, false), + UsersService.isValidDisplayName(username, false), UsersService.isValidPassword(password), settingsModel.validate() ]); @@ -66,11 +66,11 @@ module.exports = class SetupService { /** * This will perform the setup. */ - static setup({settings, user: {email, password, displayName}}) { + static setup({settings, user: {email, password, username}}) { // Validate the settings first. return SetupService - .validate({settings, user: {email, password, displayName}}) + .validate({settings, user: {email, password, username}}) .then(() => { return SettingsService.update(settings); }) @@ -80,7 +80,7 @@ module.exports = class SetupService { // Create the user. return UsersService - .createLocalUser(email, password, displayName) + .createLocalUser(email, password, username) // Grant them administrative privileges and confirm the email account. .then((user) => { diff --git a/services/users.js b/services/users.js index 408d5c618..22903daef 100644 --- a/services/users.js +++ b/services/users.js @@ -122,7 +122,7 @@ module.exports = class UsersService { // The user was not found, lets create them! user = new UserModel({ - displayName: profile.displayName, + username: profile.username, roles: [], profiles: [ { @@ -164,24 +164,24 @@ module.exports = class UsersService { static createLocalUsers(users) { return Promise.all(users.map((user) => { return UsersService - .createLocalUser(user.email, user.password, user.displayName); + .createLocalUser(user.email, user.password, user.username); })); } /** - * Check the requested displayname for naughty words (currently in English) and special chars - * @param {String} displayName word to be checked for profanity + * Check the requested username for blocked words and special chars + * @param {String} username word to be checked for profanity * @param {Boolean} checkAgainstWordlist enables cheching against the wordlist - * @return {Promise} rejected if the machine's sensibilites are offended + * @return {Promise} */ - static isValidDisplayName(displayName, checkAgainstWordlist = true) { + static isValidDisplayName(username, checkAgainstWordlist = true) { const onlyLettersNumbersUnderscore = /^[A-Za-z0-9_]+$/; - if (!displayName) { + if (!username) { return Promise.reject(errors.ErrMissingDisplay); } - if (!onlyLettersNumbersUnderscore.test(displayName)) { + if (!onlyLettersNumbersUnderscore.test(username)) { return Promise.reject(errors.ErrSpecialChars); } @@ -189,11 +189,11 @@ module.exports = class UsersService { if (checkAgainstWordlist) { // check for profanity - return Wordlist.displayNameCheck(displayName); + return Wordlist.usernameCheck(username); } // No errors found! - return Promise.resolve(displayName); + return Promise.resolve(username); } /** @@ -215,23 +215,23 @@ module.exports = class UsersService { * Creates the local user with a given email, password, and name. * @param {String} email email of the new user * @param {String} password plaintext password of the new user - * @param {String} displayName name of the display user + * @param {String} username name of the display user * @param {Function} done callback */ - static createLocalUser(email, password, displayName) { + static createLocalUser(email, password, username) { if (!email) { return Promise.reject(errors.ErrMissingEmail); } email = email.toLowerCase().trim(); - displayName = displayName.toLowerCase().trim(); + username = username.trim(); return Promise.all([ - UsersService.isValidDisplayName(displayName), + UsersService.isValidDisplayName(username), UsersService.isValidPassword(password) ]) - .then(() => { // displayName is valid + .then(() => { // username is valid return new Promise((resolve, reject) => { bcrypt.hash(password, SALT_ROUNDS, (err, hashedPassword) => { if (err) { @@ -239,7 +239,7 @@ module.exports = class UsersService { } let user = new UserModel({ - displayName: displayName, + username, password: hashedPassword, roles: [], profiles: [ @@ -253,7 +253,7 @@ module.exports = class UsersService { user.save((err) => { if (err) { if (err.code === 11000) { - if (err.message.match('displayName')) { + if (err.message.match('username')) { return reject(errors.ErrDisplayTaken); } return reject(errors.ErrEmailTaken); @@ -397,7 +397,7 @@ module.exports = class UsersService { static findPublicByIdArray(ids) { return UserModel.find({ id: {$in: ids} - }, 'id displayName'); + }, 'id username'); } /** @@ -473,7 +473,7 @@ module.exports = class UsersService { /** * Finds a user using a value which gets compared using a prefix match against - * the user's email address and/or their display name. + * the user's email address and/or their username. * @param {String} value value to search by * @return {Promise} */ @@ -481,9 +481,9 @@ module.exports = class UsersService { return UserModel.find({ $or: [ - // Search by a prefix match on the displayName. + // Search by a prefix match on the username. { - 'displayName': { + 'username': { $regex: new RegExp(`^${value}`), $options: 'i' } @@ -667,18 +667,18 @@ module.exports = class UsersService { } /** - * Updates the user's displayName. + * Updates the user's username. * @param {String} id the id of the user to be enabled. - * @param {String} displayName The new displayname for the user. + * @param {String} username The new username for the user. * @return {Promise} */ - static editName(id, displayName) { + static editName(id, username) { return UserModel.update({ id, canEditName: true }, { $set: { - displayName: displayName.toLowerCase(), + username: username, canEditName: false, status: 'PENDING' } diff --git a/services/wordlist.js b/services/wordlist.js index dde032f97..8dc16fba2 100644 --- a/services/wordlist.js +++ b/services/wordlist.js @@ -201,22 +201,22 @@ class Wordlist { /** * check potential username for banned words, special characters */ - static displayNameCheck(displayName) { + static usernameCheck(username) { const wl = new Wordlist(); return wl.load() .then(() => { - displayName = displayName.replace(/_/g, ''); + username = username.replace(/_/g, ''); // test each word, and fail if we find a match const hasBadWords = wl.lists.banned.some(phrase => { - return displayName.indexOf(phrase.join('')) !== -1; + return username.indexOf(phrase.join('')) !== -1; }); if (hasBadWords) { throw Errors.ErrContainsProfanity; } else { - return Promise.resolve(displayName); + return Promise.resolve(username); } }); } diff --git a/test/e2e/pages/embedStreamPage.js b/test/e2e/pages/embedStreamPage.js index 1dd467b10..28a9929ea 100644 --- a/test/e2e/pages/embedStreamPage.js +++ b/test/e2e/pages/embedStreamPage.js @@ -19,7 +19,7 @@ const embedStreamCommands = { .setValue('@signInDialogEmail', user.email) .setValue('@signInDialogPassword', user.pass) .setValue('@signUpDialogConfirmPassword', user.pass) - .setValue('@signUpDialogDisplayName', user.displayName) + .setValue('@signUpDialogDisplayName', user.username) .waitForElementVisible('@signUpButton') .click('@signUpButton') .waitForElementVisible('@signInViewTrigger') @@ -96,7 +96,7 @@ module.exports = { selector: '#signInDialog #confirmPassword' }, signUpDialogDisplayName: { - selector: '#signInDialog #displayName' + selector: '#signInDialog #username' }, logInButton: { selector: '#coralLogInButton' diff --git a/test/e2e/tests/EmbedStreamTests.js b/test/e2e/tests/EmbedStreamTests.js index 5591d6b8d..78d13ccfa 100644 --- a/test/e2e/tests/EmbedStreamTests.js +++ b/test/e2e/tests/EmbedStreamTests.js @@ -37,7 +37,7 @@ module.exports = { .click('#coralRegister') .waitForElementVisible('#email', 1000) .setValue('#email', mockUser.email) - .setValue('#displayName', mockUser.name) + .setValue('#username', mockUser.name) .setValue('#password', mockUser.pw) .setValue('#confirmPassword', mockUser.pw) .click('#coralSignUpButton') @@ -125,7 +125,7 @@ module.exports = { // Add a mock user .then(() => mocks.users([{ - displayName: 'Baby Blue', + username: 'Baby Blue', email: 'whale@tale.sea', password: 'krill' }])) diff --git a/test/e2e/tests/Visitor/SignUpTest.js b/test/e2e/tests/Visitor/SignUpTest.js index d19ca1d8c..253b41659 100644 --- a/test/e2e/tests/Visitor/SignUpTest.js +++ b/test/e2e/tests/Visitor/SignUpTest.js @@ -13,7 +13,7 @@ module.exports = { embedStreamPage .signUp({ email: `visitor_${Date.now()}@test.com`, - displayName: `visitor${Date.now()}`, + username: `visitor${Date.now()}`, pass: 'testtest' }); }, diff --git a/test/routes/api/account/index.js b/test/routes/api/account/index.js index 5b9acfa9d..3a4493279 100644 --- a/test/routes/api/account/index.js +++ b/test/routes/api/account/index.js @@ -13,7 +13,7 @@ chai.use(require('chai-http')); const UsersService = require('../../../../services/users'); -describe('/api/v1/account/displayname', () => { +describe('/api/v1/account/username', () => { let mockUser; beforeEach(() => SettingsService.init(settings).then(() => { @@ -29,9 +29,9 @@ describe('/api/v1/account/displayname', () => { .post(`/api/v1/users/${mockUser.id}/username-enable`) .set(passport.inject({id: '456', roles: ['ADMIN']})) .then(() => chai.request(app) - .put('/api/v1/account/displayname') + .put('/api/v1/account/username') .set(passport.inject({id: mockUser.id, roles: []})) - .send({displayName: 'MojoJojo'})) + .send({username: 'MojoJojo'})) .then((res) => { expect(res).to.have.status(204); }); @@ -42,9 +42,9 @@ describe('/api/v1/account/displayname', () => { .post(`/api/v1/users/${mockUser.id}/username-enable`) .set(passport.inject({id: '456', roles: ['ADMIN']})) .then(() => chai.request(app) - .put('/api/v1/account/displayname') + .put('/api/v1/account/username') .set(passport.inject({id: 'wrongid', roles: []})) - .send({displayName: 'MojoJojo'})) + .send({username: 'MojoJojo'})) .then(() => { done(new Error('Exected Error')); }) @@ -56,7 +56,7 @@ describe('/api/v1/account/displayname', () => { it('it should return an error when the user tries to edit their username if canEditName is disabled', (done) => { chai.request(app) - .put('/api/v1/account/displayname') + .put('/api/v1/account/username') .set(passport.inject({id: mockUser.id, roles: []})) .send({username: 'MojoJojo'}) .then(() => { diff --git a/test/routes/api/auth/index.js b/test/routes/api/auth/index.js index 9f3937a91..38b867b7d 100644 --- a/test/routes/api/auth/index.js +++ b/test/routes/api/auth/index.js @@ -45,7 +45,7 @@ describe('/api/v1/auth/local', () => { expect(res2).to.have.status(200); expect(res2).to.be.json; expect(res2.body).to.have.property('user'); - expect(res2.body.user).to.have.property('displayName', 'maria'); + expect(res2.body.user).to.have.property('username', 'Maria'); }); }); @@ -91,7 +91,7 @@ describe('/api/v1/auth/local', () => { expect(res).to.have.status(200); expect(res).to.be.json; expect(res.body).to.have.property('user'); - expect(res.body.user).to.have.property('displayName', 'maria'); + expect(res.body.user).to.have.property('username', 'Maria'); }); }); }); diff --git a/test/routes/api/comments/index.js b/test/routes/api/comments/index.js index c305f9f6b..698c5870e 100644 --- a/test/routes/api/comments/index.js +++ b/test/routes/api/comments/index.js @@ -49,11 +49,11 @@ describe('/api/v1/comments', () => { }]; const users = [{ - displayName: 'Ana', + username: 'Ana', email: 'ana@gmail.com', password: '123456789' }, { - displayName: 'Maria', + username: 'Maria', email: 'maria@gmail.com', password: '123456789' }]; @@ -183,11 +183,11 @@ describe('/api/v1/comments/:comment_id', () => { }]; const users = [{ - displayName: 'Ana', + username: 'Ana', email: 'ana@gmail.com', password: '123456789' }, { - displayName: 'Maria', + username: 'Maria', email: 'maria@gmail.com', password: '123456789' }]; @@ -296,11 +296,11 @@ describe('/api/v1/comments/:comment_id/actions', () => { }]; const users = [{ - displayName: 'Ana', + username: 'Ana', email: 'ana@gmail.com', password: '123456789' }, { - displayName: 'Maria', + username: 'Maria', email: 'maria@gmail.com', password: '123456789' }]; diff --git a/test/routes/api/queue/index.js b/test/routes/api/queue/index.js index 9f89a961e..bca68f355 100644 --- a/test/routes/api/queue/index.js +++ b/test/routes/api/queue/index.js @@ -45,11 +45,11 @@ describe('/api/v1/queue', () => { }]; const users = [{ - displayName: 'Ana', + username: 'Ana', email: 'ana@gmail.com', password: '123456789' }, { - displayName: 'Maria', + username: 'Maria', email: 'maria@gmail.com', password: '123456789' }]; @@ -103,7 +103,7 @@ describe('/api/v1/queue', () => { expect(res).to.have.status(200); expect(res.body.comments).to.have.length(1); expect(res.body.comments[0]).to.have.property('body'); - expect(res.body.users[0]).to.have.property('displayName'); + expect(res.body.users[0]).to.have.property('username'); expect(res.body.actions[0]).to.have.property('action_type'); }); }); @@ -115,7 +115,7 @@ describe('/api/v1/queue', () => { .end(function(err, res){ expect(err).to.be.null; expect(res).to.have.status(200); - expect(res.body.users[0]).to.have.property('displayName'); + expect(res.body.users[0]).to.have.property('username'); expect(res.body.actions[0]).to.have.property('action_type'); done(); }); diff --git a/test/services/comments.js b/test/services/comments.js index fc2105cb1..0de4e9d4b 100644 --- a/test/services/comments.js +++ b/test/services/comments.js @@ -69,11 +69,11 @@ describe('services.CommentsService', () => { const users = [{ email: 'stampi@gmail.com', - displayName: 'Stampi', + username: 'Stampi', password: '1Coral!!' }, { email: 'sockmonster@gmail.com', - displayName: 'Sockmonster', + username: 'Sockmonster', password: '2Coral!!' }]; diff --git a/test/services/users.js b/test/services/users.js index 5774d45ae..51ba01403 100644 --- a/test/services/users.js +++ b/test/services/users.js @@ -12,15 +12,15 @@ describe('services.UsersService', () => { return SettingsService.init(settings).then(() => { return UsersService.createLocalUsers([{ email: 'stampi@gmail.com', - displayName: 'Stampi', + username: 'Stampi', password: '1Coral!-' }, { email: 'sockmonster@gmail.com', - displayName: 'Sockmonster', + username: 'Sockmonster', password: '2Coral!2' }, { email: 'marvel@gmail.com', - displayName: 'Marvel', + username: 'Marvel', password: '3Coral!3' }]).then((users) => { mockUsers = users; @@ -33,7 +33,7 @@ describe('services.UsersService', () => { return UsersService .findById(mockUsers[0].id) .then((user) => { - expect(user).to.have.property('displayName', 'stampi'); + expect(user).to.have.property('username', 'Stampi'); }); }); }); @@ -53,11 +53,11 @@ describe('services.UsersService', () => { return UsersService.findPublicByIdArray(ids).then((result) => { expect(result).to.have.length(3); const sorted = result.sort((a, b) => { - if(a.displayName < b.displayName) {return -1;} - if(a.displayName > b.displayName) {return 1;} + if(a.username < b.username) {return -1;} + if(a.username > b.username) {return 1;} return 0; }); - expect(sorted[0]).to.have.property('displayName', 'marvel'); + expect(sorted[0]).to.have.property('username', 'Marvel'); }); }); }); @@ -68,8 +68,7 @@ describe('services.UsersService', () => { return UsersService .findLocalUser(mockUsers[0].profiles[0].id, '1Coral!-') .then((user) => { - expect(user).to.have.property('displayName') - .and.to.equal(mockUsers[0].displayName.toLowerCase()); + expect(user).to.have.property('username', mockUsers[0].username); }); }); @@ -84,10 +83,10 @@ describe('services.UsersService', () => { }); describe('#createLocalUser', () => { - it('should not create a user with duplicate display name', () => { + it('should not create a user with duplicate username', () => { return UsersService.createLocalUsers([{ email: 'otrostampi@gmail.com', - displayName: 'StampiTheSecond', + username: 'StampiTheSecond', password: '1Coralito!' }]) .then((user) => { @@ -227,7 +226,7 @@ describe('services.UsersService', () => { .then(() => UsersService.editName(mockUsers[0].id, 'Jojo')) .then(() => UsersService.findById(mockUsers[0].id)) .then((user) => { - expect(user).to.have.property('displayName', 'jojo'); + expect(user).to.have.property('username', 'Jojo'); expect(user).to.have.property('canEditName', false); }); }); diff --git a/views/auth-callback.ejs b/views/auth-callback.ejs index d38d759ee..66b9cfede 100644 --- a/views/auth-callback.ejs +++ b/views/auth-callback.ejs @@ -3,7 +3,7 @@ From 7db3da5fdd5d7104962db92ddcbed137e1b4e5c9 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 17:03:41 -0700 Subject: [PATCH 02/11] Added fix for facebook --- services/passport.js | 5 ++++- services/users.js | 18 ++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/services/passport.js b/services/passport.js index b56834cc7..d265d9812 100644 --- a/services/passport.js +++ b/services/passport.js @@ -102,7 +102,10 @@ if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && clientID: process.env.TALK_FACEBOOK_APP_ID, clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`, - profileFields: ['id', 'username', 'picture.type(large)'] + + // TODO: remove displayName reference when we have steps in the FE to handle + // the username create flow. + profileFields: ['id', 'displayName', 'picture.type(large)'] }, (accessToken, refreshToken, profile, done) => { UsersService .findOrCreateExternalUser(profile) diff --git a/services/users.js b/services/users.js index 22903daef..97cf3e125 100644 --- a/services/users.js +++ b/services/users.js @@ -105,13 +105,13 @@ module.exports = class UsersService { * @param {Object} profile - User social/external profile * @param {Function} done [description] */ - static findOrCreateExternalUser(profile) { + static findOrCreateExternalUser({id, provider, displayName}) { return UserModel .findOne({ profiles: { $elemMatch: { - id: profile.id, - provider: profile.provider + id, + provider } } }) @@ -122,14 +122,12 @@ module.exports = class UsersService { // The user was not found, lets create them! user = new UserModel({ - username: profile.username, + + // TODO: remove displayName reference when we have steps in the FE to handle + // the username create flow. + username: displayName.replace(/ /g, '_').replace(/[^a-zA-Z_]/g, ''), roles: [], - profiles: [ - { - id: profile.id, - provider: profile.provider - } - ] + profiles: [{id, provider}] }); return user.save(); From 229ddb11f20631ce1503ec080407f27273dec467 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 17:37:39 -0700 Subject: [PATCH 03/11] Adjusted circle --- INSTALL.md | 2 +- circle.yml | 109 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 008c4cba3..33b634de8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,7 @@ There are some runtime requirements for running Talk from source: - [Node](https://nodejs.org/) v7 or later -- [MongoDB](https://www.mongodb.com/) v3.2 or later +- [MongoDB](https://www.mongodb.com/) v3.4 or later - [Redis](https://redis.io/) v3.2 or later - [Yarn](https://yarnpkg.com/) v0.19.1 or later diff --git a/circle.yml b/circle.yml index bae929df7..a467e8bba 100644 --- a/circle.yml +++ b/circle.yml @@ -1,45 +1,74 @@ -machine: - node: - version: 7 - services: - - docker - - redis - environment: - PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" - NODE_ENV: "test" +version: 2 -dependencies: - override: - - yarn - cache_directories: - - ~/.cache/yarn - post: - # Build the static assets - - yarn build - # Lint the project here, before tests are ran. - - yarn lint +containerInfo: + - image: node:7 + - image: mongo:3.4 + - image: redis:3.2 -database: - post: - # Initialize the settings in the database, this will create indicies for the - # database. - - ./bin/cli setup --defaults - - sleep 2 +stages: + build: + workDir: ~/talk + environment: + - "PATH=$PATH:~/talk/node_modules/.bin" + - "NODE_ENV=test" + steps: + # Get the code + - type: checkout -test: - override: - # Run the tests using the junit reporter. - - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test - # Run the e2e test suite - - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e + # Restore the yarn cache + - type: cache-restore + key: yarn-cache -deployment: - release: - tag: /v[0-9]+(\.[0-9]+)*/ - commands: - - bash ./scripts/deploy.sh + # Install dependencies + - type: shell + shell: /bin/bash + command: | + # install yarn + npm install -g yarn - latest: - branch: master - commands: - - bash ./scripts/deploy.sh + # install dependencies + yarn + + # Save the yarn cache + - type: cache-save + key: yarn-cache + paths: + - ~/.cache/yarn + + # Build the dependencies + - type: shell + shell: /bin/bash + command: | + # build static dependencies + yarn build + + # lint the project + yarn lint + + - type: shell + shell: /bin/bash + command: | + # Initialize the settings in the database, this will create indicies + # for the database. + ./bin/cli setup --defaults + + # Ugly fix to wait until database indicies are created. + sleep 2 + + # Run the tests using the junit reporter. + MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test + + # Run the e2e test suite + E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e + +# TODO: fix +# deployment: +# release: +# tag: /v[0-9]+(\.[0-9]+)*/ +# commands: +# - bash ./scripts/deploy.sh +# +# latest: +# branch: master +# commands: +# - bash ./scripts/deploy.sh From 832d1f67635ef1525004f4503667eefeb196a552 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 18:02:25 -0700 Subject: [PATCH 04/11] Revert "Adjusted circle" This reverts commit 229ddb11f20631ce1503ec080407f27273dec467. --- INSTALL.md | 2 +- circle.yml | 109 ++++++++++++++++++++--------------------------------- 2 files changed, 41 insertions(+), 70 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 33b634de8..008c4cba3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,7 @@ There are some runtime requirements for running Talk from source: - [Node](https://nodejs.org/) v7 or later -- [MongoDB](https://www.mongodb.com/) v3.4 or later +- [MongoDB](https://www.mongodb.com/) v3.2 or later - [Redis](https://redis.io/) v3.2 or later - [Yarn](https://yarnpkg.com/) v0.19.1 or later diff --git a/circle.yml b/circle.yml index a467e8bba..bae929df7 100644 --- a/circle.yml +++ b/circle.yml @@ -1,74 +1,45 @@ -version: 2 +machine: + node: + version: 7 + services: + - docker + - redis + environment: + PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" + NODE_ENV: "test" -containerInfo: - - image: node:7 - - image: mongo:3.4 - - image: redis:3.2 +dependencies: + override: + - yarn + cache_directories: + - ~/.cache/yarn + post: + # Build the static assets + - yarn build + # Lint the project here, before tests are ran. + - yarn lint -stages: - build: - workDir: ~/talk - environment: - - "PATH=$PATH:~/talk/node_modules/.bin" - - "NODE_ENV=test" - steps: - # Get the code - - type: checkout +database: + post: + # Initialize the settings in the database, this will create indicies for the + # database. + - ./bin/cli setup --defaults + - sleep 2 - # Restore the yarn cache - - type: cache-restore - key: yarn-cache +test: + override: + # Run the tests using the junit reporter. + - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test + # Run the e2e test suite + - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e - # Install dependencies - - type: shell - shell: /bin/bash - command: | - # install yarn - npm install -g yarn +deployment: + release: + tag: /v[0-9]+(\.[0-9]+)*/ + commands: + - bash ./scripts/deploy.sh - # install dependencies - yarn - - # Save the yarn cache - - type: cache-save - key: yarn-cache - paths: - - ~/.cache/yarn - - # Build the dependencies - - type: shell - shell: /bin/bash - command: | - # build static dependencies - yarn build - - # lint the project - yarn lint - - - type: shell - shell: /bin/bash - command: | - # Initialize the settings in the database, this will create indicies - # for the database. - ./bin/cli setup --defaults - - # Ugly fix to wait until database indicies are created. - sleep 2 - - # Run the tests using the junit reporter. - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test - - # Run the e2e test suite - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e - -# TODO: fix -# deployment: -# release: -# tag: /v[0-9]+(\.[0-9]+)*/ -# commands: -# - bash ./scripts/deploy.sh -# -# latest: -# branch: master -# commands: -# - bash ./scripts/deploy.sh + latest: + branch: master + commands: + - bash ./scripts/deploy.sh From c202042cdaaebb56331ea48e000c94299e33dc52 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 18:04:31 -0700 Subject: [PATCH 05/11] Revert "Revert "Adjusted circle"" This reverts commit 832d1f67635ef1525004f4503667eefeb196a552. --- INSTALL.md | 2 +- circle.yml | 109 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 008c4cba3..33b634de8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,7 @@ There are some runtime requirements for running Talk from source: - [Node](https://nodejs.org/) v7 or later -- [MongoDB](https://www.mongodb.com/) v3.2 or later +- [MongoDB](https://www.mongodb.com/) v3.4 or later - [Redis](https://redis.io/) v3.2 or later - [Yarn](https://yarnpkg.com/) v0.19.1 or later diff --git a/circle.yml b/circle.yml index bae929df7..a467e8bba 100644 --- a/circle.yml +++ b/circle.yml @@ -1,45 +1,74 @@ -machine: - node: - version: 7 - services: - - docker - - redis - environment: - PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" - NODE_ENV: "test" +version: 2 -dependencies: - override: - - yarn - cache_directories: - - ~/.cache/yarn - post: - # Build the static assets - - yarn build - # Lint the project here, before tests are ran. - - yarn lint +containerInfo: + - image: node:7 + - image: mongo:3.4 + - image: redis:3.2 -database: - post: - # Initialize the settings in the database, this will create indicies for the - # database. - - ./bin/cli setup --defaults - - sleep 2 +stages: + build: + workDir: ~/talk + environment: + - "PATH=$PATH:~/talk/node_modules/.bin" + - "NODE_ENV=test" + steps: + # Get the code + - type: checkout -test: - override: - # Run the tests using the junit reporter. - - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test - # Run the e2e test suite - - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e + # Restore the yarn cache + - type: cache-restore + key: yarn-cache -deployment: - release: - tag: /v[0-9]+(\.[0-9]+)*/ - commands: - - bash ./scripts/deploy.sh + # Install dependencies + - type: shell + shell: /bin/bash + command: | + # install yarn + npm install -g yarn - latest: - branch: master - commands: - - bash ./scripts/deploy.sh + # install dependencies + yarn + + # Save the yarn cache + - type: cache-save + key: yarn-cache + paths: + - ~/.cache/yarn + + # Build the dependencies + - type: shell + shell: /bin/bash + command: | + # build static dependencies + yarn build + + # lint the project + yarn lint + + - type: shell + shell: /bin/bash + command: | + # Initialize the settings in the database, this will create indicies + # for the database. + ./bin/cli setup --defaults + + # Ugly fix to wait until database indicies are created. + sleep 2 + + # Run the tests using the junit reporter. + MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test + + # Run the e2e test suite + E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e + +# TODO: fix +# deployment: +# release: +# tag: /v[0-9]+(\.[0-9]+)*/ +# commands: +# - bash ./scripts/deploy.sh +# +# latest: +# branch: master +# commands: +# - bash ./scripts/deploy.sh From fded4f0a917ab636755a5ea091e23844d718facf Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 9 Feb 2017 18:05:47 -0700 Subject: [PATCH 06/11] Added new machine type --- circle.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/circle.yml b/circle.yml index a467e8bba..90da5084e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,5 +1,7 @@ version: 2 +executorType: machine + containerInfo: - image: node:7 - image: mongo:3.4 From 184e63e4e3218232e38942dab7d86d5216a6ba92 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 10 Feb 2017 16:28:09 -0700 Subject: [PATCH 07/11] Updated circle based on feedback --- circle.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 90da5084e..653b0e7d4 100644 --- a/circle.yml +++ b/circle.yml @@ -11,9 +11,14 @@ stages: build: workDir: ~/talk environment: - - "PATH=$PATH:~/talk/node_modules/.bin" - - "NODE_ENV=test" + - NODE_ENV: test steps: + - type: shell + shell: /bin/bash + command: | + # add in path rewrite + echo "PATH=$PATH:~/talk/node_modules/.bin" >> ~/.bashrc + # Get the code - type: checkout @@ -23,7 +28,7 @@ stages: # Install dependencies - type: shell - shell: /bin/bash + shell: /bin/bash -l command: | # install yarn npm install -g yarn @@ -39,7 +44,7 @@ stages: # Build the dependencies - type: shell - shell: /bin/bash + shell: /bin/bash -l command: | # build static dependencies yarn build @@ -48,7 +53,7 @@ stages: yarn lint - type: shell - shell: /bin/bash + shell: /bin/bash -l command: | # Initialize the settings in the database, this will create indicies # for the database. From af49b0f6fb68f7a44bc0abf5e9f32533471b53e3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 10 Feb 2017 16:29:29 -0700 Subject: [PATCH 08/11] Fixed checkout order --- circle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 653b0e7d4..c3788b9a1 100644 --- a/circle.yml +++ b/circle.yml @@ -13,15 +13,15 @@ stages: environment: - NODE_ENV: test steps: + # Get the code + - type: checkout + - type: shell shell: /bin/bash command: | # add in path rewrite echo "PATH=$PATH:~/talk/node_modules/.bin" >> ~/.bashrc - # Get the code - - type: checkout - # Restore the yarn cache - type: cache-restore key: yarn-cache From a2855a734303def336f0cba14278685a00b3a341 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 13 Feb 2017 10:03:55 -0700 Subject: [PATCH 09/11] Removed unused shell --- circle.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/circle.yml b/circle.yml index c3788b9a1..3c18185a1 100644 --- a/circle.yml +++ b/circle.yml @@ -16,12 +16,6 @@ stages: # Get the code - type: checkout - - type: shell - shell: /bin/bash - command: | - # add in path rewrite - echo "PATH=$PATH:~/talk/node_modules/.bin" >> ~/.bashrc - # Restore the yarn cache - type: cache-restore key: yarn-cache From 4e73d7fc234242d7a5d0bf9fdd12d47535b67d5b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 13 Feb 2017 10:23:11 -0700 Subject: [PATCH 10/11] Switched from Circle 2.0 -> 1.0, added set -e --- circle.yml | 118 +++++++++++++++++++--------------------------- scripts/pree2e.sh | 3 ++ 2 files changed, 51 insertions(+), 70 deletions(-) diff --git a/circle.yml b/circle.yml index 3c18185a1..1b62faa4e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,75 +1,53 @@ -version: 2 +machine: + node: + version: 7 + services: + - docker + - redis + environment: + PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" + NODE_ENV: "test" -executorType: machine +dependencies: + override: + # Upgrade the database version to 3.4. + - sudo apt-get purge mongodb-org* + - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 + - echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list + - sudo apt-get update + - sudo apt-get install -y mongodb-org + - sudo service mongod restart + # Install node dependencies. + - yarn + cache_directories: + - ~/.cache/yarn + post: + # Build the static assets. + - yarn build + # Lint the project here, before tests are ran. + - yarn lint -containerInfo: - - image: node:7 - - image: mongo:3.4 - - image: redis:3.2 +database: + post: + # Initialize the settings in the database, this will create indicies for the + # database. + - ./bin/cli setup --defaults + - sleep 2 -stages: - build: - workDir: ~/talk - environment: - - NODE_ENV: test - steps: - # Get the code - - type: checkout +test: + override: + # Run the tests using the junit reporter. + - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test + # Run the e2e test suite. + - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e - # Restore the yarn cache - - type: cache-restore - key: yarn-cache +deployment: + release: + tag: /v[0-9]+(\.[0-9]+)*/ + commands: + - bash ./scripts/deploy.sh - # Install dependencies - - type: shell - shell: /bin/bash -l - command: | - # install yarn - npm install -g yarn - - # install dependencies - yarn - - # Save the yarn cache - - type: cache-save - key: yarn-cache - paths: - - ~/.cache/yarn - - # Build the dependencies - - type: shell - shell: /bin/bash -l - command: | - # build static dependencies - yarn build - - # lint the project - yarn lint - - - type: shell - shell: /bin/bash -l - command: | - # Initialize the settings in the database, this will create indicies - # for the database. - ./bin/cli setup --defaults - - # Ugly fix to wait until database indicies are created. - sleep 2 - - # Run the tests using the junit reporter. - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test - - # Run the e2e test suite - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e - -# TODO: fix -# deployment: -# release: -# tag: /v[0-9]+(\.[0-9]+)*/ -# commands: -# - bash ./scripts/deploy.sh -# -# latest: -# branch: master -# commands: -# - bash ./scripts/deploy.sh + latest: + branch: master + commands: + - bash ./scripts/deploy.sh diff --git a/scripts/pree2e.sh b/scripts/pree2e.sh index 9ed5b7b89..985ec4cc8 100755 --- a/scripts/pree2e.sh +++ b/scripts/pree2e.sh @@ -1,5 +1,8 @@ #!/bin/bash +# fail the e2e if any of these fail +set -e + # install selenium selenium-standalone install --config=./selenium.config.js From b30a5d21849a0108a3246c713da654c158edf8c2 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 13 Feb 2017 10:49:47 -0700 Subject: [PATCH 11/11] Removed 3.4 dep --- circle.yml | 16 +++++++++------- models/user.js | 19 +++++++++---------- services/users.js | 16 ++++++++++++---- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/circle.yml b/circle.yml index 1b62faa4e..77e61f102 100644 --- a/circle.yml +++ b/circle.yml @@ -10,13 +10,15 @@ machine: dependencies: override: - # Upgrade the database version to 3.4. - - sudo apt-get purge mongodb-org* - - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 - - echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list - - sudo apt-get update - - sudo apt-get install -y mongodb-org - - sudo service mongod restart + # TODO: use the following to add in support for MongoDB 3.4. + # # Upgrade the database version to 3.4. + # - sudo apt-get purge mongodb-org* + # - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 + # - echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list + # - sudo apt-get update + # - sudo apt-get install -y mongodb-org + # - sudo service mongod restart + # Install node dependencies. - yarn cache_directories: diff --git a/models/user.js b/models/user.js index b142be5a0..43a34a565 100644 --- a/models/user.js +++ b/models/user.js @@ -66,6 +66,15 @@ const UserSchema = new mongoose.Schema({ required: true }, + // TODO: find a way that we can instead utilize MongoDB 3.4's collation + // options to build the index in a case insenstive manner: + // https://docs.mongodb.com/manual/reference/collation/ + lowercaseUsername: { + type: String, + required: true, + unique: true + }, + // This is true when the user account is disabled, no action should be // acknowledged when they are disabled. Logins are also prevented. disabled: Boolean, @@ -126,16 +135,6 @@ UserSchema.index({ background: false }); -UserSchema.index({ - 'username': 1 -}, { - unique: true, - collation: { - locale: 'en_US', - strength: 2 - } -}); - /** * Returns true if the user has all the roles specified. */ diff --git a/services/users.js b/services/users.js index 97cf3e125..1b8045c84 100644 --- a/services/users.js +++ b/services/users.js @@ -99,6 +99,10 @@ module.exports = class UsersService { .then(() => dstUser.save()); } + static castDisplayName(displayName) { + return displayName.replace(/ /g, '_').replace(/[^a-zA-Z_]/g, ''); + } + /** * Finds a user given a social profile and if the user does not exist, creates * them. @@ -120,12 +124,14 @@ module.exports = class UsersService { return user; } + // TODO: remove displayName reference when we have steps in the FE to handle + // the username create flow. + let username = UsersService.castDisplayName(displayName); + // The user was not found, lets create them! user = new UserModel({ - - // TODO: remove displayName reference when we have steps in the FE to handle - // the username create flow. - username: displayName.replace(/ /g, '_').replace(/[^a-zA-Z_]/g, ''), + username, + lowercaseUsername: username.toLowerCase(), roles: [], profiles: [{id, provider}] }); @@ -238,6 +244,7 @@ module.exports = class UsersService { let user = new UserModel({ username, + lowercaseUsername: username.toLowerCase(), password: hashedPassword, roles: [], profiles: [ @@ -677,6 +684,7 @@ module.exports = class UsersService { }, { $set: { username: username, + lowercaseUsername: username.toLowerCase(), canEditName: false, status: 'PENDING' }