diff --git a/config/env/default.js b/config/env/default.js index 4ae53fafaa..8c7f2429ee 100644 --- a/config/env/default.js +++ b/config/env/default.js @@ -42,6 +42,9 @@ module.exports = { }, logo: 'modules/core/client/img/brand/logo.png', favicon: 'modules/core/client/img/brand/favicon.ico', + illegalUsernames: ['meanjs', 'administrator', 'password', 'admin', 'user', + 'unknown', 'anonymous', 'null', 'undefined', 'api' + ], uploads: { profileUpload: { dest: './modules/users/client/img/profile/uploads/', // Profile upload destination path diff --git a/config/env/development.js b/config/env/development.js index 93e2f7d743..da024eb886 100644 --- a/config/env/development.js +++ b/config/env/development.js @@ -75,7 +75,7 @@ module.exports = { options: { logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'user', + username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', provider: 'local', email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', firstName: 'User', @@ -84,7 +84,7 @@ module.exports = { roles: ['user'] }, seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin', + username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', provider: 'local', email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', firstName: 'Admin', diff --git a/config/env/production.js b/config/env/production.js index 333234c77f..f37f36b162 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -94,7 +94,7 @@ module.exports = { options: { logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'user', + username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', provider: 'local', email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', firstName: 'User', @@ -103,7 +103,7 @@ module.exports = { roles: ['user'] }, seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin', + username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', provider: 'local', email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', firstName: 'Admin', diff --git a/config/env/test.js b/config/env/test.js index c010177736..41c14ebf32 100644 --- a/config/env/test.js +++ b/config/env/test.js @@ -75,7 +75,7 @@ module.exports = { options: { logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'user', + username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', provider: 'local', email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', firstName: 'User', @@ -84,7 +84,7 @@ module.exports = { roles: ['user'] }, seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin', + username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', provider: 'local', email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', firstName: 'Admin', diff --git a/modules/core/tests/server/core.server.config.tests.js b/modules/core/tests/server/core.server.config.tests.js index 4e8a3882dc..b7212e0317 100644 --- a/modules/core/tests/server/core.server.config.tests.js +++ b/modules/core/tests/server/core.server.config.tests.js @@ -82,7 +82,7 @@ describe('Configuration Tests:', function () { }); it('should not be an admin user to begin with', function(done) { - User.find({ username: 'admin' }, function(err, users) { + User.find({ username: 'seedadmin' }, function(err, users) { should.not.exist(err); users.should.be.instanceof(Array).and.have.lengthOf(0); return done(); @@ -90,7 +90,7 @@ describe('Configuration Tests:', function () { }); it('should not be a "regular" user to begin with', function(done) { - User.find({ username: 'user' }, function(err, users) { + User.find({ username: 'seeduser' }, function(err, users) { should.not.exist(err); users.should.be.instanceof(Array).and.have.lengthOf(0); return done(); diff --git a/modules/users/client/controllers/authentication.client.controller.js b/modules/users/client/controllers/authentication.client.controller.js index 34ddeffd3f..23379ce384 100644 --- a/modules/users/client/controllers/authentication.client.controller.js +++ b/modules/users/client/controllers/authentication.client.controller.js @@ -15,6 +15,7 @@ vm.signup = signup; vm.signin = signin; vm.callOauthProvider = callOauthProvider; + vm.usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/; // Get an eventual error defined in the URL query string: if ($location.search().err) { diff --git a/modules/users/client/views/authentication/signup.client.view.html b/modules/users/client/views/authentication/signup.client.view.html index 2c593322d9..5d57b519a2 100644 --- a/modules/users/client/views/authentication/signup.client.view.html +++ b/modules/users/client/views/authentication/signup.client.view.html @@ -27,9 +27,10 @@

Or sign up using your email

- +

Username is required.

+

Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.

diff --git a/modules/users/server/models/user.server.model.js b/modules/users/server/models/user.server.model.js index 16c3546e78..1dc4fc247f 100644 --- a/modules/users/server/models/user.server.model.js +++ b/modules/users/server/models/user.server.model.js @@ -29,6 +29,24 @@ var validateLocalStrategyEmail = function (email) { return ((this.provider !== 'local' && !this.updated) || validator.isEmail(email, { require_tld: false })); }; +/** + * A Validation function for username + * - at least 3 characters + * - only a-z0-9_-. + * - contain at least one alphanumeric character + * - not in list of illegal usernames + * - no consecutive dots: "." ok, ".." nope + * - not begin or end with "." + */ + +var validateUsername = function(username) { + var usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/; + return ( + this.provider !== 'local' || + (username && usernameRegex.test(username) && config.illegalUsernames.indexOf(username) < 0) + ); +}; + /** * User Schema */ @@ -64,6 +82,7 @@ var UserSchema = new Schema({ type: String, unique: 'Username already exists', required: 'Please fill in a username', + validate: [validateUsername, 'Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'], lowercase: true, trim: true }, diff --git a/modules/users/tests/e2e/users.e2e.tests.js b/modules/users/tests/e2e/users.e2e.tests.js index 6c232bfa4f..02d27ede02 100644 --- a/modules/users/tests/e2e/users.e2e.tests.js +++ b/modules/users/tests/e2e/users.e2e.tests.js @@ -113,6 +113,116 @@ describe('Users E2E Tests:', function () { expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Email address is invalid.'); }); + it('Should report invalid username - ".login"', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys(user1.email); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('.login'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user1.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Email address error + expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'); + }); + + it('Should report invalid username - "login."', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys(user1.email); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('login.'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user1.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Email address error + expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'); + }); + + it('Should report invalid username - "log..in"', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys(user1.email); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('log..in'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user1.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Email address error + expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'); + }); + + it('Should report invalid username - "lo"', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys(user1.email); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('lo'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user1.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Email address error + expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'); + }); + + it('Should report invalid username - "log$in"', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys(user1.email); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('log$in'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user1.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Email address error + expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'); + }); + + it('Should signup username with . - "log.in"', function () { + browser.get('http://localhost:3001/authentication/signup'); + // Enter First Name + element(by.model('vm.credentials.firstName')).sendKeys(user2.firstName); + // Enter Last Name + element(by.model('vm.credentials.lastName')).sendKeys(user2.lastName); + // Enter Email + element(by.model('vm.credentials.email')).sendKeys('someemail@meanjs.com'); + // Enter Username + element(by.model('vm.credentials.username')).sendKeys('log.in'); + // Enter Password + element(by.model('vm.credentials.password')).sendKeys(user2.password); + // Click Submit button + element(by.css('button[type=submit]')).click(); + // Signup successful with username having . + expect(browser.getCurrentUrl()).toEqual('http://localhost:3001/'); + + signout(); + }); + it('Should report missing username', function () { browser.get('http://localhost:3001/authentication/signup'); // Enter First Name diff --git a/modules/users/tests/server/user.server.model.tests.js b/modules/users/tests/server/user.server.model.tests.js index 820389dba0..bac9e8b20a 100644 --- a/modules/users/tests/server/user.server.model.tests.js +++ b/modules/users/tests/server/user.server.model.tests.js @@ -5,7 +5,9 @@ */ var should = require('should'), mongoose = require('mongoose'), - User = mongoose.model('User'); + User = mongoose.model('User'), + path = require('path'), + config = require(path.resolve('./config/config')); /** * Globals @@ -643,6 +645,89 @@ describe('User Model Unit Tests:', function () { }); + describe('Username Validation', function() { + it('should show error to save username beginning with .', function(done) { + var _user = new User(user1); + + _user.username = '.login'; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should be able to show an error when try to save with not allowed username', function (done) { + var _user = new User(user1); + + _user.username = config.illegalUsernames[Math.floor(Math.random() * config.illegalUsernames.length)]; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should show error to save username end with .', function(done) { + var _user = new User(user1); + + _user.username = 'login.'; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should show error to save username with ..', function(done) { + var _user = new User(user1); + + _user.username = 'log..in'; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should show error to save username shorter than 3 character', function(done) { + var _user = new User(user1); + + _user.username = 'lo'; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should show error saving a username without at least one alphanumeric character', function(done) { + var _user = new User(user1); + + _user.username = '-_-'; + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should show error saving a username longer than 34 characters', function(done) { + var _user = new User(user1); + + _user.username = 'l'.repeat(35); + _user.save(function(err) { + should.exist(err); + done(); + }); + }); + + it('should save username with dot', function(done) { + var _user = new User(user1); + + _user.username = 'log.in'; + _user.save(function(err) { + should.not.exist(err); + done(); + }); + }); + + }); + after(function (done) { User.remove().exec(done); });