Skip to content
This repository was archived by the owner on Aug 30, 2021. It is now read-only.

Commit fb9d9d9

Browse files
sujeethkmleanos
authored andcommitted
feat(user): add strict validations for username (#1574)
Idea proposed by @sparshy #1204 Suggestions, rules and tests from Trustroots @simison Added validations on user server model Added client side validations Added relevant tests on user server tests Added relevant tests on user e2e tests Fixes #1204
1 parent 0e2ea65 commit fb9d9d9

File tree

10 files changed

+229
-10
lines changed

10 files changed

+229
-10
lines changed

config/env/default.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ module.exports = {
4242
},
4343
logo: 'modules/core/client/img/brand/logo.png',
4444
favicon: 'modules/core/client/img/brand/favicon.ico',
45+
illegalUsernames: ['meanjs', 'administrator', 'password', 'admin', 'user',
46+
'unknown', 'anonymous', 'null', 'undefined', 'api'
47+
],
4548
uploads: {
4649
profileUpload: {
4750
dest: './modules/users/client/img/profile/uploads/', // Profile upload destination path

config/env/development.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module.exports = {
7575
options: {
7676
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
7777
seedUser: {
78-
username: process.env.MONGO_SEED_USER_USERNAME || 'user',
78+
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
7979
provider: 'local',
8080
email: process.env.MONGO_SEED_USER_EMAIL || '[email protected]',
8181
firstName: 'User',
@@ -84,7 +84,7 @@ module.exports = {
8484
roles: ['user']
8585
},
8686
seedAdmin: {
87-
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin',
87+
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
8888
provider: 'local',
8989
email: process.env.MONGO_SEED_ADMIN_EMAIL || '[email protected]',
9090
firstName: 'Admin',

config/env/production.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ module.exports = {
9494
options: {
9595
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
9696
seedUser: {
97-
username: process.env.MONGO_SEED_USER_USERNAME || 'user',
97+
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
9898
provider: 'local',
9999
email: process.env.MONGO_SEED_USER_EMAIL || '[email protected]',
100100
firstName: 'User',
@@ -103,7 +103,7 @@ module.exports = {
103103
roles: ['user']
104104
},
105105
seedAdmin: {
106-
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin',
106+
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
107107
provider: 'local',
108108
email: process.env.MONGO_SEED_ADMIN_EMAIL || '[email protected]',
109109
firstName: 'Admin',

config/env/test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module.exports = {
7575
options: {
7676
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
7777
seedUser: {
78-
username: process.env.MONGO_SEED_USER_USERNAME || 'user',
78+
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
7979
provider: 'local',
8080
email: process.env.MONGO_SEED_USER_EMAIL || '[email protected]',
8181
firstName: 'User',
@@ -84,7 +84,7 @@ module.exports = {
8484
roles: ['user']
8585
},
8686
seedAdmin: {
87-
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'admin',
87+
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
8888
provider: 'local',
8989
email: process.env.MONGO_SEED_ADMIN_EMAIL || '[email protected]',
9090
firstName: 'Admin',

modules/core/tests/server/core.server.config.tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ describe('Configuration Tests:', function () {
8282
});
8383

8484
it('should not be an admin user to begin with', function(done) {
85-
User.find({ username: 'admin' }, function(err, users) {
85+
User.find({ username: 'seedadmin' }, function(err, users) {
8686
should.not.exist(err);
8787
users.should.be.instanceof(Array).and.have.lengthOf(0);
8888
return done();
8989
});
9090
});
9191

9292
it('should not be a "regular" user to begin with', function(done) {
93-
User.find({ username: 'user' }, function(err, users) {
93+
User.find({ username: 'seeduser' }, function(err, users) {
9494
should.not.exist(err);
9595
users.should.be.instanceof(Array).and.have.lengthOf(0);
9696
return done();

modules/users/client/controllers/authentication.client.controller.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
vm.signup = signup;
1616
vm.signin = signin;
1717
vm.callOauthProvider = callOauthProvider;
18+
vm.usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/;
1819

1920
// Get an eventual error defined in the URL query string:
2021
if ($location.search().err) {

modules/users/client/views/authentication/signup.client.view.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ <h3 class="col-xs-12 text-center">Or sign up using your email</h3>
2727
</div>
2828
<div class="form-group" show-errors>
2929
<label for="username">Username</label>
30-
<input type="text" id="username" name="username" class="form-control" ng-model="vm.credentials.username" placeholder="Username" lowercase required>
30+
<input type="text" id="username" name="username" class="form-control" ng-model="vm.credentials.username" ng-pattern="vm.usernameRegex" placeholder="Username" lowercase required>
3131
<div ng-messages="vm.userForm.username.$error" role="alert">
3232
<p class="help-block error-text" ng-message="required">Username is required.</p>
33+
<p class="help-block error-text" ng-message="pattern">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.</p>
3334
</div>
3435
</div>
3536
<div class="form-group" show-errors>

modules/users/server/models/user.server.model.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ var validateLocalStrategyEmail = function (email) {
2929
return ((this.provider !== 'local' && !this.updated) || validator.isEmail(email, { require_tld: false }));
3030
};
3131

32+
/**
33+
* A Validation function for username
34+
* - at least 3 characters
35+
* - only a-z0-9_-.
36+
* - contain at least one alphanumeric character
37+
* - not in list of illegal usernames
38+
* - no consecutive dots: "." ok, ".." nope
39+
* - not begin or end with "."
40+
*/
41+
42+
var validateUsername = function(username) {
43+
var usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/;
44+
return (
45+
this.provider !== 'local' ||
46+
(username && usernameRegex.test(username) && config.illegalUsernames.indexOf(username) < 0)
47+
);
48+
};
49+
3250
/**
3351
* User Schema
3452
*/
@@ -64,6 +82,7 @@ var UserSchema = new Schema({
6482
type: String,
6583
unique: 'Username already exists',
6684
required: 'Please fill in a username',
85+
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.'],
6786
lowercase: true,
6887
trim: true
6988
},

modules/users/tests/e2e/users.e2e.tests.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,116 @@ describe('Users E2E Tests:', function () {
113113
expect(element.all(by.css('.error-text')).get(0).getText()).toBe('Email address is invalid.');
114114
});
115115

116+
it('Should report invalid username - ".login"', function () {
117+
browser.get('http://localhost:3001/authentication/signup');
118+
// Enter First Name
119+
element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName);
120+
// Enter Last Name
121+
element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName);
122+
// Enter Email
123+
element(by.model('vm.credentials.email')).sendKeys(user1.email);
124+
// Enter Username
125+
element(by.model('vm.credentials.username')).sendKeys('.login');
126+
// Enter Password
127+
element(by.model('vm.credentials.password')).sendKeys(user1.password);
128+
// Click Submit button
129+
element(by.css('button[type=submit]')).click();
130+
// Email address error
131+
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.');
132+
});
133+
134+
it('Should report invalid username - "login."', function () {
135+
browser.get('http://localhost:3001/authentication/signup');
136+
// Enter First Name
137+
element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName);
138+
// Enter Last Name
139+
element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName);
140+
// Enter Email
141+
element(by.model('vm.credentials.email')).sendKeys(user1.email);
142+
// Enter Username
143+
element(by.model('vm.credentials.username')).sendKeys('login.');
144+
// Enter Password
145+
element(by.model('vm.credentials.password')).sendKeys(user1.password);
146+
// Click Submit button
147+
element(by.css('button[type=submit]')).click();
148+
// Email address error
149+
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.');
150+
});
151+
152+
it('Should report invalid username - "log..in"', function () {
153+
browser.get('http://localhost:3001/authentication/signup');
154+
// Enter First Name
155+
element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName);
156+
// Enter Last Name
157+
element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName);
158+
// Enter Email
159+
element(by.model('vm.credentials.email')).sendKeys(user1.email);
160+
// Enter Username
161+
element(by.model('vm.credentials.username')).sendKeys('log..in');
162+
// Enter Password
163+
element(by.model('vm.credentials.password')).sendKeys(user1.password);
164+
// Click Submit button
165+
element(by.css('button[type=submit]')).click();
166+
// Email address error
167+
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.');
168+
});
169+
170+
it('Should report invalid username - "lo"', function () {
171+
browser.get('http://localhost:3001/authentication/signup');
172+
// Enter First Name
173+
element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName);
174+
// Enter Last Name
175+
element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName);
176+
// Enter Email
177+
element(by.model('vm.credentials.email')).sendKeys(user1.email);
178+
// Enter Username
179+
element(by.model('vm.credentials.username')).sendKeys('lo');
180+
// Enter Password
181+
element(by.model('vm.credentials.password')).sendKeys(user1.password);
182+
// Click Submit button
183+
element(by.css('button[type=submit]')).click();
184+
// Email address error
185+
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.');
186+
});
187+
188+
it('Should report invalid username - "log$in"', function () {
189+
browser.get('http://localhost:3001/authentication/signup');
190+
// Enter First Name
191+
element(by.model('vm.credentials.firstName')).sendKeys(user1.firstName);
192+
// Enter Last Name
193+
element(by.model('vm.credentials.lastName')).sendKeys(user1.lastName);
194+
// Enter Email
195+
element(by.model('vm.credentials.email')).sendKeys(user1.email);
196+
// Enter Username
197+
element(by.model('vm.credentials.username')).sendKeys('log$in');
198+
// Enter Password
199+
element(by.model('vm.credentials.password')).sendKeys(user1.password);
200+
// Click Submit button
201+
element(by.css('button[type=submit]')).click();
202+
// Email address error
203+
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.');
204+
});
205+
206+
it('Should signup username with . - "log.in"', function () {
207+
browser.get('http://localhost:3001/authentication/signup');
208+
// Enter First Name
209+
element(by.model('vm.credentials.firstName')).sendKeys(user2.firstName);
210+
// Enter Last Name
211+
element(by.model('vm.credentials.lastName')).sendKeys(user2.lastName);
212+
// Enter Email
213+
element(by.model('vm.credentials.email')).sendKeys('[email protected]');
214+
// Enter Username
215+
element(by.model('vm.credentials.username')).sendKeys('log.in');
216+
// Enter Password
217+
element(by.model('vm.credentials.password')).sendKeys(user2.password);
218+
// Click Submit button
219+
element(by.css('button[type=submit]')).click();
220+
// Signup successful with username having .
221+
expect(browser.getCurrentUrl()).toEqual('http://localhost:3001/');
222+
223+
signout();
224+
});
225+
116226
it('Should report missing username', function () {
117227
browser.get('http://localhost:3001/authentication/signup');
118228
// Enter First Name

modules/users/tests/server/user.server.model.tests.js

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
*/
66
var should = require('should'),
77
mongoose = require('mongoose'),
8-
User = mongoose.model('User');
8+
User = mongoose.model('User'),
9+
path = require('path'),
10+
config = require(path.resolve('./config/config'));
911

1012
/**
1113
* Globals
@@ -643,6 +645,89 @@ describe('User Model Unit Tests:', function () {
643645

644646
});
645647

648+
describe('Username Validation', function() {
649+
it('should show error to save username beginning with .', function(done) {
650+
var _user = new User(user1);
651+
652+
_user.username = '.login';
653+
_user.save(function(err) {
654+
should.exist(err);
655+
done();
656+
});
657+
});
658+
659+
it('should be able to show an error when try to save with not allowed username', function (done) {
660+
var _user = new User(user1);
661+
662+
_user.username = config.illegalUsernames[Math.floor(Math.random() * config.illegalUsernames.length)];
663+
_user.save(function(err) {
664+
should.exist(err);
665+
done();
666+
});
667+
});
668+
669+
it('should show error to save username end with .', function(done) {
670+
var _user = new User(user1);
671+
672+
_user.username = 'login.';
673+
_user.save(function(err) {
674+
should.exist(err);
675+
done();
676+
});
677+
});
678+
679+
it('should show error to save username with ..', function(done) {
680+
var _user = new User(user1);
681+
682+
_user.username = 'log..in';
683+
_user.save(function(err) {
684+
should.exist(err);
685+
done();
686+
});
687+
});
688+
689+
it('should show error to save username shorter than 3 character', function(done) {
690+
var _user = new User(user1);
691+
692+
_user.username = 'lo';
693+
_user.save(function(err) {
694+
should.exist(err);
695+
done();
696+
});
697+
});
698+
699+
it('should show error saving a username without at least one alphanumeric character', function(done) {
700+
var _user = new User(user1);
701+
702+
_user.username = '-_-';
703+
_user.save(function(err) {
704+
should.exist(err);
705+
done();
706+
});
707+
});
708+
709+
it('should show error saving a username longer than 34 characters', function(done) {
710+
var _user = new User(user1);
711+
712+
_user.username = 'l'.repeat(35);
713+
_user.save(function(err) {
714+
should.exist(err);
715+
done();
716+
});
717+
});
718+
719+
it('should save username with dot', function(done) {
720+
var _user = new User(user1);
721+
722+
_user.username = 'log.in';
723+
_user.save(function(err) {
724+
should.not.exist(err);
725+
done();
726+
});
727+
});
728+
729+
});
730+
646731
after(function (done) {
647732
User.remove().exec(done);
648733
});

0 commit comments

Comments
 (0)