0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Allow user to accept invitation

closes #3081
- added route `/ghost/api/v0.1/authentication/invitation`
- added accept invitation
- added signup with token
- removed check() from users api
- fixed promise in resetPassword()
This commit is contained in:
Sebastian Gierlinger 2014-07-03 17:06:07 +02:00
parent f114f4f2f6
commit 84cfd529ed
8 changed files with 66 additions and 16 deletions

View file

@ -5,6 +5,7 @@ var SignupController = Ember.ObjectController.extend(ValidationEngine, {
name: null, name: null,
email: null, email: null,
password: null, password: null,
token: null,
submitting: false, submitting: false,
// ValidationEngine settings // ValidationEngine settings
@ -12,16 +13,25 @@ var SignupController = Ember.ObjectController.extend(ValidationEngine, {
actions: { actions: {
signup: function () { signup: function () {
var self = this; var self = this,
data = self.getProperties('name', 'email', 'password', 'token');
self.notifications.closePassive(); self.notifications.closePassive();
this.toggleProperty('submitting'); this.toggleProperty('submitting');
this.validate({ format: false }).then(function () { this.validate({ format: false }).then(function () {
ajax({ ajax({
url: self.get('ghostPaths').adminUrl('signup'), url: self.get('ghostPaths').apiUrl('authentication', 'invitation'),
type: 'POST', type: 'POST',
data: self.getProperties('name', 'email', 'password') dataType: 'json',
data: {
invitation: [{
name: data.name,
email: data.email,
password: data.password,
token: data.token
}]
}
}).then(function () { }).then(function () {
self.get('session').authenticate('ember-simple-auth-authenticator:oauth2-password-grant', { self.get('session').authenticate('ember-simple-auth-authenticator:oauth2-password-grant', {
identification: self.get('email'), identification: self.get('email'),

View file

@ -18,7 +18,7 @@ Router.map(function () {
this.route('setup'); this.route('setup');
this.route('signin'); this.route('signin');
this.route('signout'); this.route('signout');
this.route('signup'); this.route('signup', { path: '/signup/:token' });
this.route('forgotten'); this.route('forgotten');
this.route('reset', { path: '/reset/:token' }); this.route('reset', { path: '/reset/:token' });
this.resource('posts', { path: '/' }, function () { this.resource('posts', { path: '/' }, function () {

View file

@ -2,7 +2,19 @@ import styleBody from 'ghost/mixins/style-body';
import loadingIndicator from 'ghost/mixins/loading-indicator'; import loadingIndicator from 'ghost/mixins/loading-indicator';
var SignupRoute = Ember.Route.extend(styleBody, loadingIndicator, { var SignupRoute = Ember.Route.extend(styleBody, loadingIndicator, {
classNames: ['ghost-signup'] classNames: ['ghost-signup'],
beforeModel: function () {
if (this.get('session').isAuthenticated) {
this.transitionTo(Ember.SimpleAuth.routeAfterAuthentication);
}
},
setupController: function (controller, params) {
var tokenText = atob(params.token),
email = tokenText.split('|')[1];
controller.token = params.token;
controller.email = email;
controller.name = email.substring(0, email.indexOf('@'));
}
}); });
export default SignupRoute; export default SignupRoute;

View file

@ -91,6 +91,38 @@ authentication = {
return when.reject(new errors.UnauthorizedError(error.message)); return when.reject(new errors.UnauthorizedError(error.message));
}); });
}); });
},
/**
* ### Accept Invitation
* @param {User} object the user to create
* @returns {Promise(User}} Newly created user
*/
acceptInvitation: function acceptInvitation(object) {
var resetToken,
newPassword,
ne2Password,
name,
email;
return utils.checkObject(object, 'invitation').then(function (checkedInvitation) {
resetToken = checkedInvitation.invitation[0].token;
newPassword = checkedInvitation.invitation[0].password;
ne2Password = checkedInvitation.invitation[0].password;
email = checkedInvitation.invitation[0].email;
name = checkedInvitation.invitation[0].name;
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
var dbHash = response.settings[0].value;
return dataProvider.User.resetPassword(resetToken, newPassword, ne2Password, dbHash);
}).then(function (user) {
return dataProvider.User.edit({name: name, email: email}, {id: user.id});
}).then(function () {
return when.resolve({invitation: [{message: 'Invitation accepted.'}]});
}).otherwise(function (error) {
return when.reject(new errors.UnauthorizedError(error.message));
});
});
} }
}; };

View file

@ -96,6 +96,7 @@ users = {
* @param {{context}} options * @param {{context}} options
* @returns {Promise(User}} Newly created user * @returns {Promise(User}} Newly created user
*/ */
// TODO: remove and rename invite to add when setup is implemented
add: function add(object, options) { add: function add(object, options) {
options = options || {}; options = options || {};
@ -213,16 +214,12 @@ users = {
* @param {User} object the user to create * @param {User} object the user to create
* @returns {Promise(User}} Newly created user * @returns {Promise(User}} Newly created user
*/ */
// TODO: create a proper API end point and use JSON API format // TODO: remove when setup is implemented
register: function register(object) { register: function register(object) {
// TODO: if we want to prevent users from being created with the signup form this is the right place to do it // TODO: if we want to prevent users from being created with the signup form this is the right place to do it
return users.add(object, {context: {internal: true}}); return users.add(object, {context: {internal: true}});
}, },
check: function check(object) {
return dataProvider.User.check(object);
},
/** /**
* ### Change Password * ### Change Password
* @param {password} object * @param {password} object

View file

@ -50,8 +50,7 @@ var middleware = {
if (res.isAdmin) { if (res.isAdmin) {
if (subPath.indexOf('/ghost/api/') === 0 if (subPath.indexOf('/ghost/api/') === 0
&& path.indexOf('/ghost/api/v0.1/authentication/token') !== 0 && path.indexOf('/ghost/api/v0.1/authentication/') !== 0) {
&& path.indexOf('/ghost/api/v0.1/authentication/passwordreset/') !== 0) {
return passport.authenticate('bearer', { session: false, failWithError: true }, return passport.authenticate('bearer', { session: false, failWithError: true },
function (err, user, info) { function (err, user, info) {

View file

@ -216,7 +216,6 @@ User = ghostBookshelf.Model.extend({
if (!user || user.get('status') === 'invited') { if (!user || user.get('status') === 'invited') {
return when.reject(new Error('NotFound')); return when.reject(new Error('NotFound'));
} }
if (user.get('status') !== 'locked') { if (user.get('status') !== 'locked') {
return nodefn.call(bcrypt.compare, object.password, user.get('password')).then(function (matched) { return nodefn.call(bcrypt.compare, object.password, user.get('password')).then(function (matched) {
if (!matched) { if (!matched) {
@ -376,9 +375,7 @@ User = ghostBookshelf.Model.extend({
var foundUser = results[0], var foundUser = results[0],
passwordHash = results[1]; passwordHash = results[1];
foundUser.save({password: passwordHash, status: 'active'}); return foundUser.save({password: passwordHash, status: 'active'});
return foundUser;
}); });
}, },

View file

@ -56,6 +56,9 @@ apiRoutes = function (middleware) {
// ## Authentication // ## Authentication
router.post('/ghost/api/v0.1/authentication/passwordreset', api.http(api.authentication.generateResetToken)); router.post('/ghost/api/v0.1/authentication/passwordreset', api.http(api.authentication.generateResetToken));
router.put('/ghost/api/v0.1/authentication/passwordreset', api.http(api.authentication.resetPassword)); router.put('/ghost/api/v0.1/authentication/passwordreset', api.http(api.authentication.resetPassword));
router.post('/ghost/api/v0.1/authentication/invitation', api.http(api.authentication.acceptInvitation));
router.post('/ghost/api/v0.1/authentication/token', router.post('/ghost/api/v0.1/authentication/token',
middleware.addClientSecret, middleware.addClientSecret,
middleware.authenticateClient, middleware.authenticateClient,