diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts
index f6d360e09..a92f9c231 100644
--- a/client/src/app/+admin/users/shared/user.service.ts
+++ b/client/src/app/+admin/users/shared/user.service.ts
@@ -14,9 +14,10 @@ export class UserService {
private restExtractor: RestExtractor
) {}
- addUser(username: string, password: string) {
+ addUser(username: string, password: string, email: string) {
const body = {
username,
+ email,
password
};
diff --git a/client/src/app/+admin/users/user-add/user-add.component.html b/client/src/app/+admin/users/user-add/user-add.component.html
index 9b76c7c1b..105760f48 100644
--- a/client/src/app/+admin/users/user-add/user-add.component.html
+++ b/client/src/app/+admin/users/user-add/user-add.component.html
@@ -14,6 +14,17 @@
+
+
{
this.notificationsService.success('Success', `User ${username} created.`);
this.router.navigate([ '/admin/users/list' ]);
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index c27b96e28..69ae4353d 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -40,6 +40,9 @@ export class UserListComponent {
username: {
title: 'Username'
},
+ email: {
+ title: 'Email'
+ },
role: {
title: 'Role',
sort: false
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index 9f2661e12..0c8e18a2f 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -26,6 +26,6 @@
diff --git a/client/src/app/shared/forms/form-validators/email.validator.ts b/client/src/app/shared/forms/form-validators/email.validator.ts
new file mode 100644
index 000000000..6a2c3bdca
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/email.validator.ts
@@ -0,0 +1,13 @@
+import { FormControl } from '@angular/forms';
+
+export function validateEmail(c: FormControl) {
+ // Thanks to http://emailregex.com/
+ /* tslint:disable */
+ const EMAIL_REGEXP = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+
+ return EMAIL_REGEXP.test(c.value) ? null : {
+ email: {
+ valid: false
+ }
+ };
+}
diff --git a/client/src/app/shared/forms/form-validators/host.validator.ts b/client/src/app/shared/forms/form-validators/host.validator.ts
index ea3e43cb1..ec417e079 100644
--- a/client/src/app/shared/forms/form-validators/host.validator.ts
+++ b/client/src/app/shared/forms/form-validators/host.validator.ts
@@ -2,7 +2,7 @@ import { FormControl } from '@angular/forms';
export function validateHost(c: FormControl) {
// Thanks to http://stackoverflow.com/a/106223
- let HOST_REGEXP = new RegExp(
+ const HOST_REGEXP = new RegExp(
'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
);
diff --git a/client/src/app/shared/forms/form-validators/index.ts b/client/src/app/shared/forms/form-validators/index.ts
index ab7c2df31..669411a54 100644
--- a/client/src/app/shared/forms/form-validators/index.ts
+++ b/client/src/app/shared/forms/form-validators/index.ts
@@ -1,3 +1,4 @@
+export * from './email.validator';
export * from './host.validator';
export * from './user';
export * from './video-abuse';
diff --git a/client/src/app/shared/forms/form-validators/user.ts b/client/src/app/shared/forms/form-validators/user.ts
index 5b11ff265..0ad0e2a4b 100644
--- a/client/src/app/shared/forms/form-validators/user.ts
+++ b/client/src/app/shared/forms/form-validators/user.ts
@@ -1,5 +1,7 @@
import { Validators } from '@angular/forms';
+import { validateEmail } from './email.validator';
+
export const USER_USERNAME = {
VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ],
MESSAGES: {
@@ -8,6 +10,13 @@ export const USER_USERNAME = {
'maxlength': 'Username cannot be more than 20 characters long.'
}
};
+export const USER_EMAIL = {
+ VALIDATORS: [ Validators.required, validateEmail ],
+ MESSAGES: {
+ 'required': 'Email is required.',
+ 'email': 'Email must be valid.',
+ }
+};
export const USER_PASSWORD = {
VALIDATORS: [ Validators.required, Validators.minLength(6) ],
MESSAGES: {
diff --git a/server/controllers/api/users.js b/server/controllers/api/users.js
index 6cd0e84f7..324c99b4c 100644
--- a/server/controllers/api/users.js
+++ b/server/controllers/api/users.js
@@ -61,6 +61,7 @@ function createUser (req, res, next) {
const user = db.User.build({
username: req.body.username,
password: req.body.password,
+ email: req.body.email,
role: constants.USER_ROLES.USER
})
diff --git a/server/initializers/installer.js b/server/initializers/installer.js
index fb63b81ac..837a987dd 100644
--- a/server/initializers/installer.js
+++ b/server/initializers/installer.js
@@ -96,6 +96,7 @@ function createOAuthAdminIfNotExist (callback) {
const username = 'root'
const role = constants.USER_ROLES.ADMIN
+ const email = constants.CONFIG.ADMIN.EMAIL
const createOptions = {}
let password = ''
@@ -115,6 +116,7 @@ function createOAuthAdminIfNotExist (callback) {
const userData = {
username,
+ email,
password,
role
}
diff --git a/server/middlewares/validators/users.js b/server/middlewares/validators/users.js
index 0629550bc..3089370ff 100644
--- a/server/middlewares/validators/users.js
+++ b/server/middlewares/validators/users.js
@@ -13,11 +13,12 @@ const validatorsUsers = {
function usersAdd (req, res, next) {
req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
req.checkBody('password', 'Should have a valid password').isUserPasswordValid()
+ req.checkBody('email', 'Should have a valid email').isEmail()
logger.debug('Checking usersAdd parameters', { parameters: req.body })
checkErrors(req, res, function () {
- db.User.loadByUsername(req.body.username, function (err, user) {
+ db.User.loadByUsernameOrEmail(req.body.username, req.body.email, function (err, user) {
if (err) {
logger.error('Error in usersAdd request validator.', { error: err })
return res.sendStatus(500)
diff --git a/server/models/author.js b/server/models/author.js
index f036193c8..34b013097 100644
--- a/server/models/author.js
+++ b/server/models/author.js
@@ -84,7 +84,9 @@ function findOrCreateAuthor (name, podId, userId, transaction, callback) {
if (transaction) query.transaction = transaction
this.findOrCreate(query).asCallback(function (err, result) {
+ if (err) return callback(err)
+
// [ instance, wasCreated ]
- return callback(err, result[0])
+ return callback(null, result[0])
})
}
diff --git a/server/models/pod.js b/server/models/pod.js
index 575ebbc61..79afb737a 100644
--- a/server/models/pod.js
+++ b/server/models/pod.js
@@ -35,7 +35,10 @@ module.exports = function (sequelize, DataTypes) {
},
email: {
type: DataTypes.STRING(400),
- allowNull: false
+ allowNull: false,
+ validate: {
+ isEmail: true
+ }
}
},
{
diff --git a/server/models/user.js b/server/models/user.js
index 6cb9eec3f..35a98dd6b 100644
--- a/server/models/user.js
+++ b/server/models/user.js
@@ -32,6 +32,13 @@ module.exports = function (sequelize, DataTypes) {
}
}
},
+ email: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ isEmail: true
+ }
+ },
role: {
type: DataTypes.ENUM(values(constants.USER_ROLES)),
allowNull: false
@@ -42,6 +49,10 @@ module.exports = function (sequelize, DataTypes) {
{
fields: [ 'username' ],
unique: true
+ },
+ {
+ fields: [ 'email' ],
+ unique: true
}
],
classMethods: {
@@ -52,7 +63,8 @@ module.exports = function (sequelize, DataTypes) {
list,
listForApi,
loadById,
- loadByUsername
+ loadByUsername,
+ loadByUsernameOrEmail
},
instanceMethods: {
isPasswordMatch,
@@ -88,6 +100,7 @@ function toFormatedJSON () {
return {
id: this.id,
username: this.username,
+ email: this.email,
role: this.role,
createdAt: this.createdAt
}
@@ -151,3 +164,13 @@ function loadByUsername (username, callback) {
return this.findOne(query).asCallback(callback)
}
+
+function loadByUsernameOrEmail (username, email, callback) {
+ const query = {
+ where: {
+ $or: [ { username }, { email } ]
+ }
+ }
+
+ return this.findOne(query).asCallback(callback)
+}
diff --git a/server/tests/api/check-params/pods.js b/server/tests/api/check-params/pods.js
index 8d52b69b1..22cbdb30f 100644
--- a/server/tests/api/check-params/pods.js
+++ b/server/tests/api/check-params/pods.js
@@ -39,7 +39,7 @@ describe('Test pods API validators', function () {
], done)
})
- describe('When making friends', function () {
+ describe('When managing friends', function () {
let userAccessToken = null
before(function (done) {
@@ -156,13 +156,32 @@ describe('Test pods API validators', function () {
it('Should fail without public key', function (done) {
const data = {
+ email: 'testexample.com',
host: 'coucou.com'
}
requestsUtils.makePostBodyRequest(server.url, path, null, data, done)
})
+ it('Should fail without an email', function (done) {
+ const data = {
+ host: 'coucou.com',
+ publicKey: 'mysuperpublickey'
+ }
+ requestsUtils.makePostBodyRequest(server.url, path, null, data, done)
+ })
+
+ it('Should fail without an invalid email', function (done) {
+ const data = {
+ host: 'coucou.com',
+ email: 'testexample.com',
+ publicKey: 'mysuperpublickey'
+ }
+ requestsUtils.makePostBodyRequest(server.url, path, null, data, done)
+ })
+
it('Should fail without an host', function (done) {
const data = {
+ email: 'testexample.com',
publicKey: 'mysuperpublickey'
}
requestsUtils.makePostBodyRequest(server.url, path, null, data, done)
@@ -171,6 +190,7 @@ describe('Test pods API validators', function () {
it('Should fail with an incorrect host', function (done) {
const data = {
host: 'http://coucou.com',
+ email: 'testexample.com',
publicKey: 'mysuperpublickey'
}
requestsUtils.makePostBodyRequest(server.url, path, null, data, function () {
@@ -185,6 +205,7 @@ describe('Test pods API validators', function () {
it('Should succeed with the correct parameters', function (done) {
const data = {
host: 'coucou.com',
+ email: 'test@example.com',
publicKey: 'mysuperpublickey'
}
requestsUtils.makePostBodyRequest(server.url, path, null, data, done, 200)
@@ -193,6 +214,7 @@ describe('Test pods API validators', function () {
it('Should fail with a host that already exists', function (done) {
const data = {
host: 'coucou.com',
+ email: 'test@example.com',
publicKey: 'mysuperpublickey'
}
requestsUtils.makePostBodyRequest(server.url, path, null, data, done, 409)
diff --git a/server/tests/api/check-params/users.js b/server/tests/api/check-params/users.js
index c1fcf34a4..debf63cf6 100644
--- a/server/tests/api/check-params/users.js
+++ b/server/tests/api/check-params/users.js
@@ -92,6 +92,7 @@ describe('Test users API validators', function () {
it('Should fail with a too small username', function (done) {
const data = {
username: 'ji',
+ email: 'test@example.com',
password: 'mysuperpassword'
}
@@ -101,6 +102,7 @@ describe('Test users API validators', function () {
it('Should fail with a too long username', function (done) {
const data = {
username: 'mysuperusernamewhichisverylong',
+ email: 'test@example.com',
password: 'mysuperpassword'
}
@@ -110,6 +112,26 @@ describe('Test users API validators', function () {
it('Should fail with an incorrect username', function (done) {
const data = {
username: 'my username',
+ email: 'test@example.com',
+ password: 'mysuperpassword'
+ }
+
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done)
+ })
+
+ it('Should fail with a missing email', function (done) {
+ const data = {
+ username: 'ji',
+ password: 'mysuperpassword'
+ }
+
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done)
+ })
+
+ it('Should fail with an invalid email', function (done) {
+ const data = {
+ username: 'mysuperusernamewhichisverylong',
+ email: 'testexample.com',
password: 'mysuperpassword'
}
@@ -119,6 +141,7 @@ describe('Test users API validators', function () {
it('Should fail with a too small password', function (done) {
const data = {
username: 'myusername',
+ email: 'test@example.com',
password: 'bla'
}
@@ -128,6 +151,7 @@ describe('Test users API validators', function () {
it('Should fail with a too long password', function (done) {
const data = {
username: 'myusername',
+ email: 'test@example.com',
password: 'my super long password which is very very very very very very very very very very very very very very' +
'very very very very very very very very very very very very very very very veryv very very very very' +
'very very very very very very very very very very very very very very very very very very very very long'
@@ -139,6 +163,7 @@ describe('Test users API validators', function () {
it('Should fail with an non authenticated user', function (done) {
const data = {
username: 'myusername',
+ email: 'test@example.com',
password: 'my super password'
}
@@ -148,6 +173,17 @@ describe('Test users API validators', function () {
it('Should fail if we add a user with the same username', function (done) {
const data = {
username: 'user1',
+ email: 'test@example.com',
+ password: 'my super password'
+ }
+
+ requestsUtils.makePostBodyRequest(server.url, path, server.accessToken, data, done, 409)
+ })
+
+ it('Should fail if we add a user with the same email', function (done) {
+ const data = {
+ username: 'myusername',
+ email: 'user1@example.com',
password: 'my super password'
}
@@ -157,6 +193,7 @@ describe('Test users API validators', function () {
it('Should succeed with the correct params', function (done) {
const data = {
username: 'user2',
+ email: 'test@example.com',
password: 'my super password'
}
@@ -166,6 +203,7 @@ describe('Test users API validators', function () {
it('Should fail with a non admin user', function (done) {
server.user = {
username: 'user1',
+ email: 'test@example.com',
password: 'my super password'
}
@@ -176,6 +214,7 @@ describe('Test users API validators', function () {
const data = {
username: 'user3',
+ email: 'test@example.com',
password: 'my super password'
}
diff --git a/server/tests/api/users.js b/server/tests/api/users.js
index e6d937eb0..df075f48a 100644
--- a/server/tests/api/users.js
+++ b/server/tests/api/users.js
@@ -186,6 +186,7 @@ describe('Test users', function () {
const user = res.body
expect(user.username).to.equal('user_1')
+ expect(user.email).to.equal('user_1@example.com')
expect(user.id).to.exist
done()
@@ -216,9 +217,11 @@ describe('Test users', function () {
const user = users[0]
expect(user.username).to.equal('user_1')
+ expect(user.email).to.equal('user_1@example.com')
const rootUser = users[1]
expect(rootUser.username).to.equal('root')
+ expect(rootUser.email).to.equal('admin1@example.com')
userId = user.id
done()
@@ -238,6 +241,7 @@ describe('Test users', function () {
const user = users[0]
expect(user.username).to.equal('root')
+ expect(user.email).to.equal('admin1@example.com')
done()
})
@@ -256,6 +260,7 @@ describe('Test users', function () {
const user = users[0]
expect(user.username).to.equal('user_1')
+ expect(user.email).to.equal('user_1@example.com')
done()
})
@@ -274,6 +279,7 @@ describe('Test users', function () {
const user = users[0]
expect(user.username).to.equal('user_1')
+ expect(user.email).to.equal('user_1@example.com')
done()
})
@@ -291,7 +297,9 @@ describe('Test users', function () {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal('root')
+ expect(users[0].email).to.equal('admin1@example.com')
expect(users[1].username).to.equal('user_1')
+ expect(users[1].email).to.equal('user_1@example.com')
done()
})
diff --git a/server/tests/utils/users.js b/server/tests/utils/users.js
index 2bf9c6e3e..a2c010f64 100644
--- a/server/tests/utils/users.js
+++ b/server/tests/utils/users.js
@@ -20,12 +20,17 @@ function createUser (url, accessToken, username, password, specialStatus, end) {
}
const path = '/api/v1/users'
+ const body = {
+ username,
+ password,
+ email: username + '@example.com'
+ }
request(url)
.post(path)
.set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + accessToken)
- .send({ username: username, password: password })
+ .send(body)
.expect(specialStatus)
.end(end)
}