diff --git a/core/client/controllers/signup.js b/core/client/controllers/signup.js index 9c1e191cff..f8b983fcb3 100644 --- a/core/client/controllers/signup.js +++ b/core/client/controllers/signup.js @@ -5,6 +5,7 @@ var SignupController = Ember.ObjectController.extend(ValidationEngine, { name: null, email: null, password: null, + token: null, submitting: false, // ValidationEngine settings @@ -12,16 +13,25 @@ var SignupController = Ember.ObjectController.extend(ValidationEngine, { actions: { signup: function () { - var self = this; + var self = this, + data = self.getProperties('name', 'email', 'password', 'token'); self.notifications.closePassive(); this.toggleProperty('submitting'); this.validate({ format: false }).then(function () { ajax({ - url: self.get('ghostPaths').adminUrl('signup'), + url: self.get('ghostPaths').apiUrl('authentication', 'invitation'), 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 () { self.get('session').authenticate('ember-simple-auth-authenticator:oauth2-password-grant', { identification: self.get('email'), diff --git a/core/client/router.js b/core/client/router.js index 20beada645..7486b578cf 100644 --- a/core/client/router.js +++ b/core/client/router.js @@ -18,7 +18,7 @@ Router.map(function () { this.route('setup'); this.route('signin'); this.route('signout'); - this.route('signup'); + this.route('signup', { path: '/signup/:token' }); this.route('forgotten'); this.route('reset', { path: '/reset/:token' }); this.resource('posts', { path: '/' }, function () { diff --git a/core/client/routes/signup.js b/core/client/routes/signup.js index 84337a4099..1dd1807671 100644 --- a/core/client/routes/signup.js +++ b/core/client/routes/signup.js @@ -2,7 +2,19 @@ import styleBody from 'ghost/mixins/style-body'; import loadingIndicator from 'ghost/mixins/loading-indicator'; 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; diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index 7af370be88..d72bdfd272 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -91,6 +91,38 @@ authentication = { 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)); + }); + }); } }; diff --git a/core/server/api/users.js b/core/server/api/users.js index 6895c61822..855a7df90f 100644 --- a/core/server/api/users.js +++ b/core/server/api/users.js @@ -96,6 +96,7 @@ users = { * @param {{context}} options * @returns {Promise(User}} Newly created user */ + // TODO: remove and rename invite to add when setup is implemented add: function add(object, options) { options = options || {}; @@ -213,16 +214,12 @@ users = { * @param {User} object the user to create * @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) { // 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}}); }, - check: function check(object) { - return dataProvider.User.check(object); - }, - /** * ### Change Password * @param {password} object diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js index 96db709163..6f017d1cf4 100644 --- a/core/server/middleware/middleware.js +++ b/core/server/middleware/middleware.js @@ -50,8 +50,7 @@ var middleware = { if (res.isAdmin) { if (subPath.indexOf('/ghost/api/') === 0 - && path.indexOf('/ghost/api/v0.1/authentication/token') !== 0 - && path.indexOf('/ghost/api/v0.1/authentication/passwordreset/') !== 0) { + && path.indexOf('/ghost/api/v0.1/authentication/') !== 0) { return passport.authenticate('bearer', { session: false, failWithError: true }, function (err, user, info) { diff --git a/core/server/models/user.js b/core/server/models/user.js index ec827f567a..60cc24a3c1 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -216,7 +216,6 @@ User = ghostBookshelf.Model.extend({ if (!user || user.get('status') === 'invited') { return when.reject(new Error('NotFound')); } - if (user.get('status') !== 'locked') { return nodefn.call(bcrypt.compare, object.password, user.get('password')).then(function (matched) { if (!matched) { @@ -376,9 +375,7 @@ User = ghostBookshelf.Model.extend({ var foundUser = results[0], passwordHash = results[1]; - foundUser.save({password: passwordHash, status: 'active'}); - - return foundUser; + return foundUser.save({password: passwordHash, status: 'active'}); }); }, diff --git a/core/server/routes/api.js b/core/server/routes/api.js index 6eff8b6c84..2b5eb2abdd 100644 --- a/core/server/routes/api.js +++ b/core/server/routes/api.js @@ -56,6 +56,9 @@ apiRoutes = function (middleware) { // ## Authentication 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.post('/ghost/api/v0.1/authentication/invitation', api.http(api.authentication.acceptInvitation)); + router.post('/ghost/api/v0.1/authentication/token', middleware.addClientSecret, middleware.authenticateClient,