diff --git a/errors.js b/errors.js new file mode 100644 index 000000000..7e6bbaa63 --- /dev/null +++ b/errors.js @@ -0,0 +1,40 @@ +// ErrPasswordTooShort is returned when the password length is too short. +const ErrPasswordTooShort = new Error('password must be at least 8 characters'); +ErrPasswordTooShort.translation_key = 'PASSWORD_LENGTH'; +ErrPasswordTooShort.status = 400; + +const ErrMissingEmail = new Error('email is required'); +ErrMissingEmail.translation_key = 'EMAIL_REQUIRED'; +ErrMissingEmail.status = 400; + +const ErrMissingPassword = new Error('password is required'); +ErrMissingPassword.translation_key = 'PASSWORD_REQUIRED'; +ErrMissingPassword.status = 400; + +const ErrEmailTaken = new Error('Email address already in use'); +ErrEmailTaken.translation_key = 'EMAIL_IN_USE'; +ErrEmailTaken.status = 400; + +const ErrSpecialChars = new Error('No special characters are allowed in a display name'); +ErrSpecialChars.translation_key = 'NO_SPECIAL_CHARACTERS'; +ErrSpecialChars.status = 400; + +const ErrMissingDisplay = new Error('A display name is required to create a user'); +ErrMissingDisplay.translation_key = 'DISPLAY_NAME_REQUIRED'; +ErrMissingDisplay.status = 400; + +// ErrContainsProfanity is returned in the event that the middleware detects +// profanity/wordlisted words in the payload. +const ErrContainsProfanity = new Error('Suspected profanity. If you think this in error, please let us know!'); +ErrContainsProfanity.translation_key = 'PROFANITY_ERROR'; +ErrContainsProfanity.status = 400; + +module.exports = { + ErrPasswordTooShort, + ErrMissingEmail, + ErrMissingPassword, + ErrEmailTaken, + ErrSpecialChars, + ErrMissingDisplay, + ErrContainsProfanity +}; diff --git a/models/user.js b/models/user.js index 859d3dcdb..a25bca092 100644 --- a/models/user.js +++ b/models/user.js @@ -6,6 +6,7 @@ const jwt = require('jsonwebtoken'); const Action = require('./action'); const Comment = require('./comment'); const Wordlist = require('../services/wordlist'); +const errors = require('../errors'); const EMAIL_CONFIRM_JWT_SUBJECT = 'email_confirm'; const PASSWORD_RESET_JWT_SUBJECT = 'password_reset'; @@ -319,17 +320,12 @@ const isValidDisplayName = (displayName) => { const onlyLettersNumbersUnderscore = /^[a-z0-9_]+$/; if (!displayName) { - const ErrMissingDisplay = new Error('A display name is required to create a user'); - ErrMissingDisplay.translation_key = 'DISPLAY_NAME_REQUIRED'; - ErrMissingDisplay.status = 400; - return Promise.reject(ErrMissingDisplay); + return Promise.reject(errors.ErrMissingDisplay); } if (!onlyLettersNumbersUnderscore.test(displayName)) { - const ErrSpecialChars = new Error('No special characters are allowed in a display name'); - ErrSpecialChars.translation_key = 'NO_SPECIAL_CHARACTERS'; - ErrSpecialChars.status = 400; - return Promise.reject(ErrSpecialChars); + + return Promise.reject(errors.ErrSpecialChars); } // check for profanity @@ -346,24 +342,20 @@ const isValidDisplayName = (displayName) => { UserService.createLocalUser = (email, password, displayName) => { if (!email) { - const ErrMissingEmail = new Error('EMAIL_REQUIRED'); - ErrMissingEmail.status = 400; - return Promise.reject(ErrMissingEmail); + + return Promise.reject(errors.ErrMissingEmail); } email = email.toLowerCase().trim(); displayName = displayName.toLowerCase().trim(); if (!password) { - const ErrMissingPassword = new Error('PASSWORD_REQUIRED'); - ErrMissingPassword.status = 400; - return Promise.reject(ErrMissingPassword); + + return Promise.reject(errors.ErrMissingPassword); } if (password.length < 8) { - const ErrPasswordTooShort = new Error('PASSWORD_LENGTH'); - ErrPasswordTooShort.status = 400; - return Promise.reject(ErrPasswordTooShort); + return Promise.reject(errors.ErrPasswordTooShort); } return isValidDisplayName(displayName) @@ -389,9 +381,7 @@ UserService.createLocalUser = (email, password, displayName) => { user.save((err) => { if (err) { if (err.code === 11000) { - const ErrEmailTaken = new Error('EMAIL_IN_USE'); - ErrEmailTaken.status = 400; - return reject(ErrEmailTaken); + return reject(errors.ErrEmailTaken); } return reject(err); } diff --git a/routes/api/account/index.js b/routes/api/account/index.js index 645b9c652..6a853e79d 100644 --- a/routes/api/account/index.js +++ b/routes/api/account/index.js @@ -139,3 +139,4 @@ router.put('/settings', authorization.needed(), (req, res, next) => { }); module.exports = router; +module.exports.ErrPasswordTooShort = ErrPasswordTooShort; diff --git a/services/wordlist.js b/services/wordlist.js index 553d4f7d8..a84397cda 100644 --- a/services/wordlist.js +++ b/services/wordlist.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const natural = require('natural'); const tokenizer = new natural.WordTokenizer(); const Setting = require('../models/setting'); +const errors = require('../errors'); /** * The root wordlist object. @@ -143,7 +144,7 @@ class Wordlist { if (this.match(this.lists.banned, phrase)) { debug(`the field "${field}" contained a phrase "${phrase}" which contained a banned word/phrase`); - errors.banned = ErrContainsProfanity; + errors.banned = errors.ErrContainsProfanity; // Stop looping through the fields now, we discovered the worst possible // situation (a banned word). @@ -154,7 +155,7 @@ class Wordlist { if (this.match(this.lists.suspect, phrase)) { debug(`the field "${field}" contained a phrase "${phrase}" which contained a suspected word/phrase`); - errors.suspect = ErrContainsProfanity; + errors.suspect = errors.ErrContainsProfanity; // Continue looping through the fields now, we discovered a possible bad // word (suspect). @@ -179,7 +180,7 @@ class Wordlist { }); if (hasBadWords) { - throw ErrContainsProfanity; + throw errors.ErrContainsProfanity; } else { return Promise.resolve(displayName); } @@ -215,11 +216,6 @@ class Wordlist { } } -// ErrContainsProfanity is returned in the event that the middleware detects -// profanity/wordlisted words in the payload. -const ErrContainsProfanity = new Error('Suspected profanity. If you think this in error, please let us know!'); -ErrContainsProfanity.translation_key = 'PROFANITY_ERROR'; -ErrContainsProfanity.status = 400; + module.exports = Wordlist; -module.exports.ErrContainsProfanity = ErrContainsProfanity; diff --git a/tests/services/wordlist.js b/tests/services/wordlist.js index 4edfef0c7..5edc3a88a 100644 --- a/tests/services/wordlist.js +++ b/tests/services/wordlist.js @@ -1,5 +1,5 @@ const expect = require('chai').expect; - +const errors = require('../../errors'); const Wordlist = require('../../services/wordlist'); describe('wordlist: services', () => { @@ -67,7 +67,7 @@ describe('wordlist: services', () => { content: 'how to do really bad things?' }, 'content'); - expect(errors).to.have.property('banned', Wordlist.ErrContainsProfanity); + expect(errors).to.have.property('banned', errors.ErrContainsProfanity); }); it('does not match on bodies not containing bad words', () => {