Merge pull request #3550 from halfdan/3542-forgotten-user

Properly show message when resetting password.
This commit is contained in:
Hannah Wolfe 2014-08-06 02:18:30 +01:00
commit 9fb34d6b27
10 changed files with 113 additions and 67 deletions

View File

@ -26,7 +26,7 @@ var ForgottenController = Ember.Controller.extend(ValidationEngine, {
}
}).then(function (resp) {
self.toggleProperty('submitting');
self.notifications.showSuccess('Please check your email for instructions.');
self.notifications.showSuccess('Please check your email for instructions.', {delayed: true});
self.transitionToRoute('signin');
}).catch(function (resp) {
self.toggleProperty('submitting');

View File

@ -4,6 +4,7 @@ var _ = require('lodash'),
mail = require('./mail'),
globalUtils = require('../utils'),
utils = require('./utils'),
users = require('./users'),
when = require('when'),
errors = require('../errors'),
config = require('../config'),
@ -41,7 +42,12 @@ authentication = {
return when.reject(new errors.BadRequestError('No email provided.'));
}
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
return users.read({ context: {internal: true}, email: email, status: 'active' }).then(function (foundUser) {
if (!foundUser) {
when.reject(new errors.NotFound('Invalid email address'));
}
return settings.read({context: {internal: true}, key: 'dbHash'});
}).then(function (response) {
var dbHash = response.settings[0].value;
return dataProvider.User.generateResetToken(email, expires, dbHash);
}).then(function (resetToken) {
@ -147,20 +153,14 @@ authentication = {
isSetup: function () {
return dataProvider.User.query(function (qb) {
qb.where('status', '=', 'active')
.orWhere('status', '=', 'warn-1')
.orWhere('status', '=', 'warn-2')
.orWhere('status', '=', 'warn-3')
.orWhere('status', '=', 'warn-4')
.orWhere('status', '=', 'locked');
qb.whereIn('status', ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked']);
}).fetch().then(function (users) {
if (users) {
return when.resolve({ setup: [{status: true}]});
} else {
return when.resolve({ setup: [{status: false}]});
}
});
if (users) {
return when.resolve({ setup: [{status: true}]});
} else {
return when.resolve({ setup: [{status: false}]});
}
});
},
setup: function (object) {
@ -184,7 +184,7 @@ authentication = {
status: 'active'
};
return dataProvider.User.findOne({role: 'Owner'});
return dataProvider.User.findOne({role: 'Owner', status: 'all'});
}).then(function (ownerUser) {
if (ownerUser) {
return dataProvider.User.setup(setupUser, _.extend(internal, {id: ownerUser.id}));

View File

@ -96,7 +96,7 @@ users = {
* @returns {Promise(User)} User
*/
read: function read(options) {
var attrs = ['id', 'slug', 'email'],
var attrs = ['id', 'slug', 'email', 'status'],
data = _.pick(options, attrs);
options = _.omit(options, attrs);
@ -154,7 +154,7 @@ users = {
roleId = parseInt(role.id || role, 10);
return dataProvider.User.findOne(
{id: options.context.user, include: 'roles'}
{id: options.context.user, status: 'all'}, {include: 'roles'}
).then(function (contextUser) {
var contextRoleId = contextUser.related('roles').toJSON()[0].id;
@ -240,7 +240,7 @@ users = {
// If sending the invitation failed, set status to invited-pending
return dataProvider.User.edit({status: 'invited-pending'}, {id: user.id}).then(function (user) {
return dataProvider.User.findOne({ id: user.id }, options).then(function (user) {
return dataProvider.User.findOne({ id: user.id, status: 'all' }, options).then(function (user) {
return { users: [user] };
});
});

View File

@ -10,6 +10,8 @@ var _ = require('lodash'),
Role = require('./role').Role,
tokenSecurity = {},
activeStates = ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'],
invitedStates = ['invited', 'invited-pending'],
User,
Users;
@ -109,7 +111,7 @@ User = ghostBookshelf.Model.extend({
// whitelists for the `options` hash argument on methods, by method name.
// these are the only options that can be passed to Bookshelf / Knex.
validOptions = {
findOne: ['withRelated'],
findOne: ['withRelated', 'status'],
findAll: ['withRelated'],
setup: ['id'],
edit: ['withRelated', 'id'],
@ -196,9 +198,9 @@ User = ghostBookshelf.Model.extend({
}
if (options.status === 'active') {
userCollection.query().whereIn('status', ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked']);
userCollection.query().whereIn('status', activeStates);
} else if (options.status === 'invited') {
userCollection.query().whereIn('status', ['invited', 'invited-pending']);
userCollection.query().whereIn('status', invitedStates);
} else if (options.status !== 'all') {
options.where.status = options.status;
}
@ -320,6 +322,16 @@ User = ghostBookshelf.Model.extend({
* **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
*/
findOne: function (data, options) {
var query,
status;
data = _.extend({
status: 'active'
}, data || {});
status = data.status;
delete data.status;
options = options || {};
options.withRelated = _.union([ 'roles' ], options.include);
@ -333,11 +345,24 @@ User = ghostBookshelf.Model.extend({
delete data.role;
}
if (data.status === 'all') {
delete data.status;
// We pass include to forge so that toJSON has access
query = this.forge(data, {include: options.include});
data = this.filterData(data);
if (status === 'active') {
query.query('whereIn', 'status', activeStates);
} else if (status === 'invited') {
query.query('whereIn', 'status', invitedStates);
} else if (status !== 'all') {
query.query('where', { 'status': options.status});
}
return ghostBookshelf.Model.findOne.call(this, data, options);
options = this.filterOptions(options, 'findOne');
delete options.include;
return query.fetch(options);
},
/**
@ -379,7 +404,8 @@ User = ghostBookshelf.Model.extend({
return user.roles().updatePivot({role_id: roleId});
}
}).then(function () {
return self.findOne(user, options);
options.status = 'all';
return self.findOne({id: user.id}, options);
});
}
return user;
@ -446,7 +472,7 @@ User = ghostBookshelf.Model.extend({
return userData.roles().attach(roles, options);
}).then(function () {
// find and return the added user
return self.findOne({id: userData.id}, options);
return self.findOne({id: userData.id, status: 'all'}, options);
});
},

View File

@ -4,7 +4,7 @@ var _ = require('lodash'),
var effective = {
user: function (id) {
return Models.User.findOne({id: id}, { include: ['permissions', 'roles', 'roles.permissions'] })
return Models.User.findOne({id: id, status: 'all'}, { include: ['permissions', 'roles', 'roles.permissions'] })
.then(function (foundUser) {
var seenPerms = {},
rolePerms = _.map(foundUser.related('roles').models, function (role) {

View File

@ -21,7 +21,7 @@ describe('Authentication API', function () {
describe('Not completed', function () {
// TODO: stub settings
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:init'));
beforeEach(testUtils.setup('roles', 'owner:pre', 'settings', 'perms:setting', 'perms:init'));
it('should report that setup has not been completed', function (done) {
AuthAPI.isSetup().then(function (result) {

View File

@ -38,7 +38,7 @@ describe('Users API', function () {
});
describe('Browse', function () {
function checkBrowseResponse(response, count) {
function checkBrowseResponse(response, count) {
should.exist(response);
testUtils.API.checkResponse(response, 'users');
should.exist(response.users);
@ -47,60 +47,63 @@ describe('Users API', function () {
testUtils.API.checkResponse(response.users[1], 'user', ['roles']);
testUtils.API.checkResponse(response.users[2], 'user', ['roles']);
testUtils.API.checkResponse(response.users[3], 'user', ['roles']);
}
}
it('Owner can browse', function (done) {
it('Owner can browse', function (done) {
UserAPI.browse(context.owner).then(function (response) {
checkBrowseResponse(response, 5);
checkBrowseResponse(response, 7);
done();
}).catch(done);
});
});
it('Admin can browse', function (done) {
it('Admin can browse', function (done) {
UserAPI.browse(context.admin).then(function (response) {
checkBrowseResponse(response, 5);
checkBrowseResponse(response, 7);
done();
}).catch(done);
});
});
it('Editor can browse', function (done) {
it('Editor can browse', function (done) {
UserAPI.browse(context.editor).then(function (response) {
checkBrowseResponse(response, 5);
checkBrowseResponse(response, 7);
done();
}).catch(done);
});
});
it('Author can browse active', function (done) {
it('Author can browse active', function (done) {
UserAPI.browse(context.author).then(function (response) {
checkBrowseResponse(response, 5);
checkBrowseResponse(response, 7);
done();
}).catch(done);
});
});
it('No-auth CANNOT browse', function (done) {
it('No-auth CANNOT browse', function (done) {
UserAPI.browse().then(function () {
done(new Error('Browse users is not denied without authentication.'));
}, function () {
done();
}).catch(done);
});
});
it('Can browse invited/invited-pending (admin)', function (done) {
UserAPI.browse(_.extend(testUtils.context.admin, { status: 'invited' })).then(function (response) {
should.exist(response);
testUtils.API.checkResponse(response, 'users');
should.exist(response.users);
response.users.should.have.length(1);
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
response.users[0].status.should.equal('invited-pending');
testUtils.fixtures.createInvitedUsers().then(function () {
UserAPI.browse(_.extend(testUtils.context.admin, { status: 'invited' })).then(function (response) {
should.exist(response);
testUtils.API.checkResponse(response, 'users');
should.exist(response.users);
response.users.should.have.length(3);
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
response.users[0].status.should.equal('invited-pending');
done();
}).catch(done);
done();
}).catch(done);
});
});
it('Author can browse', function (done) {
UserAPI.browse(context.author).then(function (response) {
checkBrowseResponse(response, 5);
checkBrowseResponse(response, 7);
done();
}).catch(done);
});

View File

@ -179,7 +179,7 @@ describe('User Model', function run() {
results.meta.pagination.page.should.equal(1);
results.meta.pagination.limit.should.equal(15);
results.meta.pagination.pages.should.equal(1);
results.users.length.should.equal(3);
results.users.length.should.equal(4);
done();
}).catch(done);
@ -193,7 +193,7 @@ describe('User Model', function run() {
results.meta.pagination.limit.should.equal(15);
results.meta.pagination.pages.should.equal(1);
results.meta.pagination.total.should.equal(2);
results.users.length.should.equal(1);
results.users.length.should.equal(2);
return UserModel.findPage({role: 'Owner'});
}).then(function (results) {

View File

@ -75,36 +75,31 @@ DataGenerator.Content = {
name: 'Joe Bloggs',
slug: 'joe-blogs',
email: 'jbloggs@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
status: 'active'
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
name: 'Smith Wellingsworth',
slug: 'smith-wellingsworth',
email: 'swellingsworth@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
status: 'invited-pending'
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
name: 'Jimothy Bogendath',
slug: 'jimothy-bogendath',
email: 'jbOgendAth@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
status: 'warn-1'
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
name: 'Slimer McEctoplasm',
slug: 'slimer-mcectoplasm',
email: 'smcectoplasm@example.com',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
status: 'warn-2'
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
},
{
name: 'Ivan Email',
slug: 'ivan-email',
email: 'info@ghost.org',
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6',
status: 'inactive'
password: '$2a$10$.pZeeBE0gHXd0PTnbT/ph.GEKgd0Wd3q2pWna3ynTGBkPKnGIKZL6'
}
],

View File

@ -225,6 +225,28 @@ fixtures = {
});
},
createInvitedUsers: function createInvitedUser() {
var knex = config.database.knex,
// grab 3 more users
extraUsers = DataGenerator.Content.users.slice(2, 5);
extraUsers = _.map(extraUsers, function (user) {
return DataGenerator.forKnex.createUser(_.extend({}, user, {
email: 'inv' + user.email,
slug: 'inv' + user.slug,
status: 'invited-pending'
}));
});
return knex('users').insert(extraUsers).then(function () {
return knex('roles_users').insert([
{ user_id: 8, role_id: 1},
{ user_id: 9, role_id: 2},
{ user_id: 10, role_id: 3}
]);
});
},
insertOne: function insertOne(obj, fn) {
var knex = config.database.knex;
return knex(obj)