0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -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,
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'),

View file

@ -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 () {

View file

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

View file

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

View file

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

View file

@ -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) {

View file

@ -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'});
});
},

View file

@ -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,