Add email to users

This commit is contained in:
Chocobozzz 2017-02-18 09:29:59 +01:00
parent 5d67f289df
commit ad4a8a1cca
19 changed files with 164 additions and 12 deletions

View File

@ -14,9 +14,10 @@ export class UserService {
private restExtractor: RestExtractor private restExtractor: RestExtractor
) {} ) {}
addUser(username: string, password: string) { addUser(username: string, password: string, email: string) {
const body = { const body = {
username, username,
email,
password password
}; };

View File

@ -14,6 +14,17 @@
</div> </div>
</div> </div>
<div class="form-group">
<label for="email">Email</label>
<input
type="text" class="form-control" id="email" placeholder="Email"
formControlName="email"
>
<div *ngIf="formErrors.email" class="alert alert-danger">
{{ formErrors.email }}
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label for="password">Password</label>
<input <input

View File

@ -5,7 +5,12 @@ import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications'; import { NotificationsService } from 'angular2-notifications';
import { UserService } from '../shared'; import { UserService } from '../shared';
import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared'; import {
FormReactive,
USER_USERNAME,
USER_EMAIL,
USER_PASSWORD
} from '../../../shared';
@Component({ @Component({
selector: 'my-user-add', selector: 'my-user-add',
@ -17,10 +22,12 @@ export class UserAddComponent extends FormReactive implements OnInit {
form: FormGroup; form: FormGroup;
formErrors = { formErrors = {
'username': '', 'username': '',
'email': '',
'password': '' 'password': ''
}; };
validationMessages = { validationMessages = {
'username': USER_USERNAME.MESSAGES, 'username': USER_USERNAME.MESSAGES,
'email': USER_EMAIL.MESSAGES,
'password': USER_PASSWORD.MESSAGES, 'password': USER_PASSWORD.MESSAGES,
}; };
@ -36,6 +43,7 @@ export class UserAddComponent extends FormReactive implements OnInit {
buildForm() { buildForm() {
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
username: [ '', USER_USERNAME.VALIDATORS ], username: [ '', USER_USERNAME.VALIDATORS ],
email: [ '', USER_EMAIL.VALIDATORS ],
password: [ '', USER_PASSWORD.VALIDATORS ], password: [ '', USER_PASSWORD.VALIDATORS ],
}); });
@ -49,9 +57,9 @@ export class UserAddComponent extends FormReactive implements OnInit {
addUser() { addUser() {
this.error = null; this.error = null;
const { username, password } = this.form.value; const { username, password, email } = this.form.value;
this.userService.addUser(username, password).subscribe( this.userService.addUser(username, password, email).subscribe(
() => { () => {
this.notificationsService.success('Success', `User ${username} created.`); this.notificationsService.success('Success', `User ${username} created.`);
this.router.navigate([ '/admin/users/list' ]); this.router.navigate([ '/admin/users/list' ]);

View File

@ -40,6 +40,9 @@ export class UserListComponent {
username: { username: {
title: 'Username' title: 'Username'
}, },
email: {
title: 'Email'
},
role: { role: {
title: 'Role', title: 'Role',
sort: false sort: false

View File

@ -26,6 +26,6 @@
<my-confirm></my-confirm> <my-confirm></my-confirm>
<footer> <footer>
PeerTube, CopyLeft 2015-2016 PeerTube, CopyLeft 2015-2017
</footer> </footer>
</div> </div>

View File

@ -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
}
};
}

View File

@ -2,7 +2,7 @@ import { FormControl } from '@angular/forms';
export function validateHost(c: FormControl) { export function validateHost(c: FormControl) {
// Thanks to http://stackoverflow.com/a/106223 // 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])$' '^(([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])$'
); );

View File

@ -1,3 +1,4 @@
export * from './email.validator';
export * from './host.validator'; export * from './host.validator';
export * from './user'; export * from './user';
export * from './video-abuse'; export * from './video-abuse';

View File

@ -1,5 +1,7 @@
import { Validators } from '@angular/forms'; import { Validators } from '@angular/forms';
import { validateEmail } from './email.validator';
export const USER_USERNAME = { export const USER_USERNAME = {
VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ], VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ],
MESSAGES: { MESSAGES: {
@ -8,6 +10,13 @@ export const USER_USERNAME = {
'maxlength': 'Username cannot be more than 20 characters long.' '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 = { export const USER_PASSWORD = {
VALIDATORS: [ Validators.required, Validators.minLength(6) ], VALIDATORS: [ Validators.required, Validators.minLength(6) ],
MESSAGES: { MESSAGES: {

View File

@ -61,6 +61,7 @@ function createUser (req, res, next) {
const user = db.User.build({ const user = db.User.build({
username: req.body.username, username: req.body.username,
password: req.body.password, password: req.body.password,
email: req.body.email,
role: constants.USER_ROLES.USER role: constants.USER_ROLES.USER
}) })

View File

@ -96,6 +96,7 @@ function createOAuthAdminIfNotExist (callback) {
const username = 'root' const username = 'root'
const role = constants.USER_ROLES.ADMIN const role = constants.USER_ROLES.ADMIN
const email = constants.CONFIG.ADMIN.EMAIL
const createOptions = {} const createOptions = {}
let password = '' let password = ''
@ -115,6 +116,7 @@ function createOAuthAdminIfNotExist (callback) {
const userData = { const userData = {
username, username,
email,
password, password,
role role
} }

View File

@ -13,11 +13,12 @@ const validatorsUsers = {
function usersAdd (req, res, next) { function usersAdd (req, res, next) {
req.checkBody('username', 'Should have a valid username').isUserUsernameValid() req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
req.checkBody('password', 'Should have a valid password').isUserPasswordValid() 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 }) logger.debug('Checking usersAdd parameters', { parameters: req.body })
checkErrors(req, res, function () { 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) { if (err) {
logger.error('Error in usersAdd request validator.', { error: err }) logger.error('Error in usersAdd request validator.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)

View File

@ -84,7 +84,9 @@ function findOrCreateAuthor (name, podId, userId, transaction, callback) {
if (transaction) query.transaction = transaction if (transaction) query.transaction = transaction
this.findOrCreate(query).asCallback(function (err, result) { this.findOrCreate(query).asCallback(function (err, result) {
if (err) return callback(err)
// [ instance, wasCreated ] // [ instance, wasCreated ]
return callback(err, result[0]) return callback(null, result[0])
}) })
} }

View File

@ -35,7 +35,10 @@ module.exports = function (sequelize, DataTypes) {
}, },
email: { email: {
type: DataTypes.STRING(400), type: DataTypes.STRING(400),
allowNull: false allowNull: false,
validate: {
isEmail: true
}
} }
}, },
{ {

View File

@ -32,6 +32,13 @@ module.exports = function (sequelize, DataTypes) {
} }
} }
}, },
email: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isEmail: true
}
},
role: { role: {
type: DataTypes.ENUM(values(constants.USER_ROLES)), type: DataTypes.ENUM(values(constants.USER_ROLES)),
allowNull: false allowNull: false
@ -42,6 +49,10 @@ module.exports = function (sequelize, DataTypes) {
{ {
fields: [ 'username' ], fields: [ 'username' ],
unique: true unique: true
},
{
fields: [ 'email' ],
unique: true
} }
], ],
classMethods: { classMethods: {
@ -52,7 +63,8 @@ module.exports = function (sequelize, DataTypes) {
list, list,
listForApi, listForApi,
loadById, loadById,
loadByUsername loadByUsername,
loadByUsernameOrEmail
}, },
instanceMethods: { instanceMethods: {
isPasswordMatch, isPasswordMatch,
@ -88,6 +100,7 @@ function toFormatedJSON () {
return { return {
id: this.id, id: this.id,
username: this.username, username: this.username,
email: this.email,
role: this.role, role: this.role,
createdAt: this.createdAt createdAt: this.createdAt
} }
@ -151,3 +164,13 @@ function loadByUsername (username, callback) {
return this.findOne(query).asCallback(callback) return this.findOne(query).asCallback(callback)
} }
function loadByUsernameOrEmail (username, email, callback) {
const query = {
where: {
$or: [ { username }, { email } ]
}
}
return this.findOne(query).asCallback(callback)
}

View File

@ -39,7 +39,7 @@ describe('Test pods API validators', function () {
], done) ], done)
}) })
describe('When making friends', function () { describe('When managing friends', function () {
let userAccessToken = null let userAccessToken = null
before(function (done) { before(function (done) {
@ -156,13 +156,32 @@ describe('Test pods API validators', function () {
it('Should fail without public key', function (done) { it('Should fail without public key', function (done) {
const data = { const data = {
email: 'testexample.com',
host: 'coucou.com' host: 'coucou.com'
} }
requestsUtils.makePostBodyRequest(server.url, path, null, data, done) 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) { it('Should fail without an host', function (done) {
const data = { const data = {
email: 'testexample.com',
publicKey: 'mysuperpublickey' publicKey: 'mysuperpublickey'
} }
requestsUtils.makePostBodyRequest(server.url, path, null, data, done) 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) { it('Should fail with an incorrect host', function (done) {
const data = { const data = {
host: 'http://coucou.com', host: 'http://coucou.com',
email: 'testexample.com',
publicKey: 'mysuperpublickey' publicKey: 'mysuperpublickey'
} }
requestsUtils.makePostBodyRequest(server.url, path, null, data, function () { 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) { it('Should succeed with the correct parameters', function (done) {
const data = { const data = {
host: 'coucou.com', host: 'coucou.com',
email: 'test@example.com',
publicKey: 'mysuperpublickey' publicKey: 'mysuperpublickey'
} }
requestsUtils.makePostBodyRequest(server.url, path, null, data, done, 200) 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) { it('Should fail with a host that already exists', function (done) {
const data = { const data = {
host: 'coucou.com', host: 'coucou.com',
email: 'test@example.com',
publicKey: 'mysuperpublickey' publicKey: 'mysuperpublickey'
} }
requestsUtils.makePostBodyRequest(server.url, path, null, data, done, 409) requestsUtils.makePostBodyRequest(server.url, path, null, data, done, 409)

View File

@ -92,6 +92,7 @@ describe('Test users API validators', function () {
it('Should fail with a too small username', function (done) { it('Should fail with a too small username', function (done) {
const data = { const data = {
username: 'ji', username: 'ji',
email: 'test@example.com',
password: 'mysuperpassword' password: 'mysuperpassword'
} }
@ -101,6 +102,7 @@ describe('Test users API validators', function () {
it('Should fail with a too long username', function (done) { it('Should fail with a too long username', function (done) {
const data = { const data = {
username: 'mysuperusernamewhichisverylong', username: 'mysuperusernamewhichisverylong',
email: 'test@example.com',
password: 'mysuperpassword' password: 'mysuperpassword'
} }
@ -110,6 +112,26 @@ describe('Test users API validators', function () {
it('Should fail with an incorrect username', function (done) { it('Should fail with an incorrect username', function (done) {
const data = { const data = {
username: 'my username', 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' password: 'mysuperpassword'
} }
@ -119,6 +141,7 @@ describe('Test users API validators', function () {
it('Should fail with a too small password', function (done) { it('Should fail with a too small password', function (done) {
const data = { const data = {
username: 'myusername', username: 'myusername',
email: 'test@example.com',
password: 'bla' password: 'bla'
} }
@ -128,6 +151,7 @@ describe('Test users API validators', function () {
it('Should fail with a too long password', function (done) { it('Should fail with a too long password', function (done) {
const data = { const data = {
username: 'myusername', 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' + 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 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' '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) { it('Should fail with an non authenticated user', function (done) {
const data = { const data = {
username: 'myusername', username: 'myusername',
email: 'test@example.com',
password: 'my super password' 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) { it('Should fail if we add a user with the same username', function (done) {
const data = { const data = {
username: 'user1', 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' password: 'my super password'
} }
@ -157,6 +193,7 @@ describe('Test users API validators', function () {
it('Should succeed with the correct params', function (done) { it('Should succeed with the correct params', function (done) {
const data = { const data = {
username: 'user2', username: 'user2',
email: 'test@example.com',
password: 'my super password' password: 'my super password'
} }
@ -166,6 +203,7 @@ describe('Test users API validators', function () {
it('Should fail with a non admin user', function (done) { it('Should fail with a non admin user', function (done) {
server.user = { server.user = {
username: 'user1', username: 'user1',
email: 'test@example.com',
password: 'my super password' password: 'my super password'
} }
@ -176,6 +214,7 @@ describe('Test users API validators', function () {
const data = { const data = {
username: 'user3', username: 'user3',
email: 'test@example.com',
password: 'my super password' password: 'my super password'
} }

View File

@ -186,6 +186,7 @@ describe('Test users', function () {
const user = res.body const user = res.body
expect(user.username).to.equal('user_1') expect(user.username).to.equal('user_1')
expect(user.email).to.equal('user_1@example.com')
expect(user.id).to.exist expect(user.id).to.exist
done() done()
@ -216,9 +217,11 @@ describe('Test users', function () {
const user = users[0] const user = users[0]
expect(user.username).to.equal('user_1') expect(user.username).to.equal('user_1')
expect(user.email).to.equal('user_1@example.com')
const rootUser = users[1] const rootUser = users[1]
expect(rootUser.username).to.equal('root') expect(rootUser.username).to.equal('root')
expect(rootUser.email).to.equal('admin1@example.com')
userId = user.id userId = user.id
done() done()
@ -238,6 +241,7 @@ describe('Test users', function () {
const user = users[0] const user = users[0]
expect(user.username).to.equal('root') expect(user.username).to.equal('root')
expect(user.email).to.equal('admin1@example.com')
done() done()
}) })
@ -256,6 +260,7 @@ describe('Test users', function () {
const user = users[0] const user = users[0]
expect(user.username).to.equal('user_1') expect(user.username).to.equal('user_1')
expect(user.email).to.equal('user_1@example.com')
done() done()
}) })
@ -274,6 +279,7 @@ describe('Test users', function () {
const user = users[0] const user = users[0]
expect(user.username).to.equal('user_1') expect(user.username).to.equal('user_1')
expect(user.email).to.equal('user_1@example.com')
done() done()
}) })
@ -291,7 +297,9 @@ describe('Test users', function () {
expect(users.length).to.equal(2) expect(users.length).to.equal(2)
expect(users[0].username).to.equal('root') 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].username).to.equal('user_1')
expect(users[1].email).to.equal('user_1@example.com')
done() done()
}) })

View File

@ -20,12 +20,17 @@ function createUser (url, accessToken, username, password, specialStatus, end) {
} }
const path = '/api/v1/users' const path = '/api/v1/users'
const body = {
username,
password,
email: username + '@example.com'
}
request(url) request(url)
.post(path) .post(path)
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + accessToken) .set('Authorization', 'Bearer ' + accessToken)
.send({ username: username, password: password }) .send(body)
.expect(specialStatus) .expect(specialStatus)
.end(end) .end(end)
} }