2016-05-03 22:50:21 +00:00
|
|
|
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2016-11-15 21:46:23 +00:00
|
|
|
'use strict';
|
2015-04-20 18:44:07 +00:00
|
|
|
var loopback = require('../');
|
2015-12-31 23:41:18 +00:00
|
|
|
var lt = require('./helpers/loopback-testing-helper');
|
2015-04-20 18:44:07 +00:00
|
|
|
var path = require('path');
|
|
|
|
var SIMPLE_APP = path.join(__dirname, 'fixtures', 'user-integration-app');
|
|
|
|
var app = require(path.join(SIMPLE_APP, 'server/server.js'));
|
2016-11-22 14:30:04 +00:00
|
|
|
var expect = require('./helpers/expect');
|
2017-04-10 10:20:40 +00:00
|
|
|
const Promise = require('bluebird');
|
|
|
|
const waitForEvent = require('./helpers/wait-for-event');
|
2015-04-20 18:44:07 +00:00
|
|
|
|
|
|
|
describe('users - integration', function() {
|
|
|
|
lt.beforeEach.withApp(app);
|
|
|
|
|
|
|
|
before(function(done) {
|
|
|
|
app.models.User.destroyAll(function(err) {
|
|
|
|
if (err) return done(err);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2016-07-26 12:10:13 +00:00
|
|
|
app.models.Post.destroyAll(function(err) {
|
2015-04-20 18:44:07 +00:00
|
|
|
if (err) return done(err);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
app.models.blog.destroyAll(function(err) {
|
|
|
|
if (err) return done(err);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('base-user', function() {
|
2016-04-01 09:14:26 +00:00
|
|
|
var userId, accessToken;
|
2015-04-20 18:44:07 +00:00
|
|
|
|
|
|
|
it('should create a new user', function(done) {
|
2015-09-19 02:13:30 +00:00
|
|
|
this.post('/api/users')
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
2015-09-19 02:13:30 +00:00
|
|
|
if (err) return done(err);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2016-11-22 14:30:04 +00:00
|
|
|
expect(res.body.id).to.exist();
|
2015-04-20 18:44:07 +00:00
|
|
|
userId = res.body.id;
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should log into the user', function(done) {
|
|
|
|
var url = '/api/users/login';
|
|
|
|
|
|
|
|
this.post(url)
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2016-11-22 14:30:04 +00:00
|
|
|
expect(res.body.id).to.exist();
|
2015-04-20 18:44:07 +00:00
|
|
|
accessToken = res.body.id;
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create post for a given user', function(done) {
|
|
|
|
var url = '/api/users/' + userId + '/posts?access_token=' + accessToken;
|
|
|
|
this.post(url)
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({title: 'T1', content: 'C1'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
expect(res.body.title).to.be.eql('T1');
|
|
|
|
expect(res.body.content).to.be.eql('C1');
|
|
|
|
expect(res.body.userId).to.be.eql(userId);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// FIXME: [rfeng] The test is passing if run alone. But it fails with
|
|
|
|
// `npm test` as the loopback models are polluted by other tests
|
|
|
|
it('should prevent access tokens from being included', function(done) {
|
|
|
|
var url = '/api/posts?filter={"include":{"user":"accessTokens"}}';
|
|
|
|
this.get(url)
|
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
expect(res.body).to.have.property('length', 1);
|
|
|
|
var post = res.body[0];
|
|
|
|
expect(post.user).to.have.property('username', 'x');
|
|
|
|
expect(post.user).to.not.have.property('accessTokens');
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2017-01-13 10:03:06 +00:00
|
|
|
|
|
|
|
it('should preserve current session when invalidating tokens', function(done) {
|
|
|
|
var url = '/api/users/' + userId;
|
|
|
|
var self = this;
|
|
|
|
this.patch(url)
|
|
|
|
.send({email: 'new@example.com'})
|
|
|
|
.set('Authorization', accessToken)
|
|
|
|
.expect(200, function(err) {
|
|
|
|
if (err) return done(err);
|
|
|
|
self.get(url)
|
|
|
|
.set('Authorization', accessToken)
|
|
|
|
.expect(200, done);
|
|
|
|
});
|
|
|
|
});
|
2015-07-01 19:13:09 +00:00
|
|
|
|
|
|
|
it('returns 401 on logout with no access token', function(done) {
|
|
|
|
this.post('/api/users/logout')
|
|
|
|
.expect(401, done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns 401 on logout with invalid access token', function(done) {
|
|
|
|
this.post('/api/users/logout')
|
|
|
|
.set('Authorization', 'unknown-token')
|
|
|
|
.expect(401, done);
|
|
|
|
});
|
2017-03-22 14:33:32 +00:00
|
|
|
|
|
|
|
it('updates the user\'s password', function() {
|
|
|
|
const User = app.models.User;
|
|
|
|
const credentials = {email: 'change@example.com', password: 'pass'};
|
|
|
|
return User.create(credentials)
|
|
|
|
.then(u => {
|
|
|
|
this.user = u;
|
|
|
|
return User.login(credentials);
|
|
|
|
})
|
|
|
|
.then(token => {
|
|
|
|
return this.post('/api/users/change-password')
|
|
|
|
.set('Authorization', token.id)
|
|
|
|
.send({
|
|
|
|
oldPassword: credentials.password,
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(204);
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
return User.findById(this.user.id);
|
|
|
|
})
|
|
|
|
.then(user => {
|
|
|
|
return user.hasPassword('new password');
|
|
|
|
})
|
|
|
|
.then(isMatch => expect(isMatch, 'user has new password').to.be.true());
|
|
|
|
});
|
|
|
|
|
|
|
|
it('rejects unauthenticated change password request', function() {
|
|
|
|
return this.post('/api/users/change-password')
|
|
|
|
.send({
|
|
|
|
oldPassword: 'old password',
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(401);
|
|
|
|
});
|
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
it('uses change password options provided by the remoting context', function() {
|
2017-03-22 14:33:32 +00:00
|
|
|
const User = app.models.User;
|
|
|
|
const credentials = {email: 'inject@example.com', password: 'pass'};
|
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
let observedOptions;
|
2017-03-22 14:33:32 +00:00
|
|
|
User.observe('before save', (ctx, next) => {
|
2017-04-10 12:07:41 +00:00
|
|
|
observedOptions = ctx.options;
|
2017-03-22 14:33:32 +00:00
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
|
|
|
return User.create(credentials)
|
|
|
|
.then(u => User.login(credentials))
|
|
|
|
.then(token => {
|
|
|
|
return this.post('/api/users/change-password')
|
|
|
|
.set('Authorization', token.id)
|
|
|
|
.send({
|
|
|
|
oldPassword: credentials.password,
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(204);
|
|
|
|
})
|
|
|
|
.then(() => {
|
2017-04-10 12:07:41 +00:00
|
|
|
expect(observedOptions).to.have.property('accessToken');
|
2017-03-22 14:33:32 +00:00
|
|
|
});
|
|
|
|
});
|
2017-04-10 10:20:40 +00:00
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
it('resets the user\'s password', function() {
|
2017-04-10 10:20:40 +00:00
|
|
|
const User = app.models.User;
|
|
|
|
const credentials = {email: 'reset@example.com', password: 'pass'};
|
|
|
|
return User.create(credentials)
|
|
|
|
.then(u => {
|
|
|
|
this.user = u;
|
|
|
|
return triggerPasswordReset(credentials.email);
|
|
|
|
})
|
|
|
|
.then(info => {
|
|
|
|
return this.post('/api/users/reset-password')
|
|
|
|
.set('Authorization', info.accessToken.id)
|
|
|
|
.send({
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(204);
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
return User.findById(this.user.id);
|
|
|
|
})
|
|
|
|
.then(user => {
|
|
|
|
return user.hasPassword('new password');
|
|
|
|
})
|
|
|
|
.then(isMatch => expect(isMatch, 'user has new password').to.be.true());
|
|
|
|
});
|
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
it('rejects unauthenticated reset password requests', function() {
|
2017-04-10 10:20:40 +00:00
|
|
|
return this.post('/api/users/reset-password')
|
|
|
|
.send({
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(401);
|
|
|
|
});
|
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
it('uses password reset options provided by the remoting context', function() {
|
2017-04-10 10:20:40 +00:00
|
|
|
const User = app.models.User;
|
|
|
|
const credentials = {email: 'inject-reset@example.com', password: 'pass'};
|
|
|
|
|
2017-04-10 12:07:41 +00:00
|
|
|
let observedOptions;
|
2017-04-10 10:20:40 +00:00
|
|
|
User.observe('before save', (ctx, next) => {
|
2017-04-10 12:07:41 +00:00
|
|
|
observedOptions = ctx.options;
|
2017-04-10 10:20:40 +00:00
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
|
|
|
return User.create(credentials)
|
|
|
|
.then(u => triggerPasswordReset(credentials.email))
|
|
|
|
.then(info => {
|
|
|
|
return this.post('/api/users/reset-password')
|
|
|
|
.set('Authorization', info.accessToken.id)
|
|
|
|
.send({
|
|
|
|
newPassword: 'new password',
|
|
|
|
})
|
|
|
|
.expect(204);
|
|
|
|
})
|
|
|
|
.then(() => {
|
2017-04-10 12:07:41 +00:00
|
|
|
expect(observedOptions).to.have.property('accessToken');
|
2017-04-10 10:20:40 +00:00
|
|
|
});
|
|
|
|
});
|
2015-04-20 18:44:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('sub-user', function() {
|
2016-04-01 09:14:26 +00:00
|
|
|
var userId, accessToken;
|
2015-04-20 18:44:07 +00:00
|
|
|
|
|
|
|
it('should create a new user', function(done) {
|
|
|
|
var url = '/api/myUsers';
|
|
|
|
|
|
|
|
this.post(url)
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2016-11-22 14:30:04 +00:00
|
|
|
expect(res.body.id).to.exist();
|
2015-04-20 18:44:07 +00:00
|
|
|
userId = res.body.id;
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should log into the user', function(done) {
|
|
|
|
var url = '/api/myUsers/login';
|
|
|
|
|
|
|
|
this.post(url)
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2016-11-22 14:30:04 +00:00
|
|
|
expect(res.body.id).to.exist();
|
2015-04-20 18:44:07 +00:00
|
|
|
accessToken = res.body.id;
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create blog for a given user', function(done) {
|
|
|
|
var url = '/api/myUsers/' + userId + '/blogs?access_token=' + accessToken;
|
|
|
|
this.post(url)
|
2016-11-15 21:46:23 +00:00
|
|
|
.send({title: 'T1', content: 'C1'})
|
2015-04-20 18:44:07 +00:00
|
|
|
.expect(200, function(err, res) {
|
|
|
|
if (err) {
|
|
|
|
console.error(err);
|
|
|
|
return done(err);
|
|
|
|
}
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
expect(res.body.title).to.be.eql('T1');
|
|
|
|
expect(res.body.content).to.be.eql('C1');
|
|
|
|
expect(res.body.userId).to.be.eql(userId);
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should prevent access tokens from being included', function(done) {
|
|
|
|
var url = '/api/blogs?filter={"include":{"user":"accessTokens"}}';
|
|
|
|
this.get(url)
|
|
|
|
.expect(200, function(err, res) {
|
2016-05-05 04:09:06 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
expect(res.body).to.have.property('length', 1);
|
|
|
|
var blog = res.body[0];
|
|
|
|
expect(blog.user).to.have.property('username', 'x');
|
|
|
|
expect(blog.user).to.not.have.property('accessTokens');
|
2016-05-05 04:09:06 +00:00
|
|
|
|
2015-04-20 18:44:07 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2017-04-10 10:20:40 +00:00
|
|
|
|
|
|
|
function triggerPasswordReset(email) {
|
|
|
|
const User = app.models.User;
|
|
|
|
return Promise.all([
|
|
|
|
User.resetPassword({email: email}),
|
|
|
|
waitForEvent(app.models.User, 'resetPasswordRequest'),
|
|
|
|
])
|
|
|
|
.spread((reset, info) => info);
|
|
|
|
}
|
2015-04-20 18:44:07 +00:00
|
|
|
});
|