Skip to content

Commit

Permalink
Move setup to API
Browse files Browse the repository at this point in the history
closes TryGhost#3136
- moved setup to authentication API
- added `POST /ghost/api/v0.1/authentication/setup` to execute the
setup process
- added `GET /ghost/api/v0.1/authentication/setup` to check if blog is
already set up (needed for TryGhost#3145)
- removed unused methods from api/users.js
  • Loading branch information
sebgie committed Jul 11, 2014
1 parent 2658e8e commit 8c2258d
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 173 deletions.
14 changes: 11 additions & 3 deletions core/client/controllers/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@ var SetupController = Ember.ObjectController.extend(ValidationEngine, {

actions: {
setup: function () {
var self = this;
var self = this,
data = self.getProperties('blogTitle', 'name', 'email', 'password');

self.notifications.closePassive();

this.toggleProperty('submitting');
this.validate({ format: false }).then(function () {
ajax({
url: self.get('ghostPaths').adminUrl('setup'),
url: self.get('ghostPaths').apiUrl('authentication', 'setup'),
type: 'POST',
data: self.getProperties('blogTitle', 'name', 'email', 'password')
data: {
setup: [{
name: data.name,
email: data.email,
password: data.password,
blogTitle: data.blogTitle
}]
}
}).then(function () {
self.get('session').authenticate('ember-simple-auth-authenticator:oauth2-password-grant', {
identification: self.get('email'),
Expand Down
81 changes: 80 additions & 1 deletion core/server/api/authentication.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var dataProvider = require('../models'),
var _ = require('lodash'),
dataProvider = require('../models'),
settings = require('./settings'),
mail = require('./mail'),
utils = require('./utils'),
Expand Down Expand Up @@ -123,6 +124,84 @@ authentication = {
return when.reject(new errors.UnauthorizedError(error.message));
});
});
},

isSetup: function () {
return dataProvider.User.findOne({status: 'active'}).then(function (user) {
if (user) {
return when.resolve({ setup: [{status: true}]});
} else {
return when.resolve({ setup: [{status: false}]});
}
});
},

setup: function (object) {
var setupUser;

return utils.checkObject(object, 'setup').then(function (checkedSetupData) {
setupUser = {
name: checkedSetupData.setup[0].name,
email: checkedSetupData.setup[0].email,
password: checkedSetupData.setup[0].password,
blogTitle: checkedSetupData.setup[0].blogTitle,
status: 'active'
};
return dataProvider.User.findAll();
}).then(function (users) {
if (users.length > 0) {
return dataProvider.User.setup(setupUser, {id: 1});
} else {
// TODO: needs to pass owner role when role endpoint is finished!
return dataProvider.User.add(setupUser);
}
}).then(function (user) {
var userSettings = [];

userSettings.push({key: 'email', value: setupUser.email});

// Handles the additional values set by the setup screen.
if (!_.isEmpty(setupUser.blogTitle)) {
userSettings.push({key: 'title', value: setupUser.blogTitle});
userSettings.push({key: 'description', value: 'Thoughts, stories and ideas by ' + setupUser.name});
}
setupUser = user.toJSON();
return settings.edit({settings: userSettings}, {context: {user: 1}});
}).then(function () {
var message = {
to: setupUser.email,
subject: 'Your New Ghost Blog',
html: '<p><strong>Hello!</strong></p>' +
'<p>Good news! You\'ve successfully created a brand new Ghost blog over on ' + config().url + '</p>' +
'<p>You can log in to your admin account with the following details:</p>' +
'<p> Email Address: ' + setupUser.email + '<br>' +
'Password: The password you chose when you signed up</p>' +
'<p>Keep this email somewhere safe for future reference, and have fun!</p>' +
'<p>xoxo</p>' +
'<p>Team Ghost<br>' +
'<a href="https://ghost.org">https://ghost.org</a></p>'
},
payload = {
mail: [{
message: message,
options: {}
}]
};

return mail.send(payload).otherwise(function (error) {
errors.logError(
error.message,
"Unable to send welcome email, your blog will continue to function.",
"Please see http://docs.ghost.org/mail/ for instructions on configuring email."
);
});
}).then(function () {
return when.resolve({ users: [setupUser]});
}).otherwise(function (error) {
console.log('error');
console.log(error);
return when.reject(new errors.UnauthorizedError(error.message));
});
}
};

Expand Down
100 changes: 3 additions & 97 deletions core/server/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,37 +110,6 @@ users = {
});
},

/**
* ### Add
* @param {User} object the user to create
* @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 || {};

return canThis(options.context).add.user().then(function () {
return utils.checkObject(object, docName).then(function (checkedUserData) {
// if the user is created by users.register(), use id: 1 as the creator for now
if (options.include) {
options.include = prepareInclude(options.include);
}

if (options.context.internal) {
options.context.user = 1;
}

return dataProvider.User.add(checkedUserData.users[0], options);
}).then(function (result) {
if (result) {
return { users: [result.toJSON()]};
}
});
}, function () {
return when.reject(new errors.NoPermissionError('You do not have permission to add a users.'));
});
},
/**
* ### Destroy
* @param {{id, context}} options
Expand All @@ -159,11 +128,12 @@ users = {
},

/**
* ### Invite user
* ### Add user
* The newly added user is invited to join the blog via email.
* @param {User} object the user to create
* @returns {Promise(User}} Newly created user
*/
invite: function invite(object, options) {
add: function add(object, options) {
var newUser,
user;

Expand Down Expand Up @@ -236,31 +206,6 @@ users = {
});
},

/**
* ### Register
* Allow to register a user using the API without beeing authenticated in. Needed to set up the first user.
* @param {User} object the user to create
* @returns {Promise(User}} Newly created user
*/
// TODO: update when setup is moved
register: function register(object) {
var newUser;

return utils.checkObject(object, docName).then(function (checkedUserData) {
newUser = checkedUserData.users[0];
return dataProvider.User.findAll();
}).then(function (users) {
if (users.length > 0) {
return dataProvider.User.setup(newUser, {id: 1});
} else {
// TODO: needs to pass owner role when role endpoint is finished!
return dataProvider.User.add(newUser);
}
}).then(function (user) {
return { users: [user.toJSON()]};
});
},

/**
* ### Change Password
* @param {password} object
Expand All @@ -284,45 +229,6 @@ users = {
});
},

// TODO: remove when old admin is removed, functionality lives now in api/authentication
generateResetToken: function generateResetToken(email) {
var expires = Date.now() + ONE_DAY;
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
var dbHash = response.settings[0].value;
return dataProvider.User.generateResetToken(email, expires, dbHash);
});
},

// TODO: remove when old admin is removed -> not needed anymore
validateToken: function validateToken(token) {
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
var dbHash = response.settings[0].value;
return dataProvider.User.validateToken(token, dbHash);
});
},

// TODO: remove when old admin is removed, functionality lives now in api/authentication
resetPassword: function resetPassword(token, newPassword, ne2Password) {
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
var dbHash = response.settings[0].value;
return dataProvider.User.resetPassword(token, newPassword, ne2Password, dbHash);
});
},

// TODO: move to authentication endpoint with issue #3136
doesUserExist: function doesUserExist() {
return dataProvider.User.findAll().then(function (users) {
if (users.length > 0) {
var activeUsers = _.remove(users.toJSON(), function (user) {
return user.status !== 'inactive';
});
if (activeUsers.length > 0) {
return true;
}
}
return false;
});
}
};

module.exports = users;
66 changes: 0 additions & 66 deletions core/server/controllers/admin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
var config = require('../config'),
_ = require('lodash'),
path = require('path'),
when = require('when'),
api = require('../api'),
errors = require('../errors'),
storage = require('../storage'),
updateCheck = require('../update-check'),
Expand Down Expand Up @@ -56,70 +54,6 @@ adminControllers = {
errors.logError(e);
return res.send(500, e.message);
});
},

// Route: doSignup
// Path: /ghost/setup/
// Method: POST
'doSetup': function (req, res) {
var name = req.body.name,
email = req.body.email,
password = req.body.password,
blogTitle = req.body.blogTitle,
users = [{
name: name,
email: email,
password: password,
status: 'active'
}];

api.users.register({users: users}).then(function () {
var settings = [];

settings.push({key: 'email', value: email});

// Handles the additional values set by the setup screen.
if (!_.isEmpty(blogTitle)) {
settings.push({key: 'title', value: blogTitle});
settings.push({key: 'description', value: 'Thoughts, stories and ideas by ' + name});
}

api.settings.edit({settings: settings}, {context: {user: 1}}).then(function () {
var message = {
to: email,
subject: 'Your New Ghost Blog',
html: '<p><strong>Hello!</strong></p>' +
'<p>Good news! You\'ve successfully created a brand new Ghost blog over on ' + config().url + '</p>' +
'<p>You can log in to your admin account with the following details:</p>' +
'<p> Email Address: ' + email + '<br>' +
'Password: The password you chose when you signed up</p>' +
'<p>Keep this email somewhere safe for future reference, and have fun!</p>' +
'<p>xoxo</p>' +
'<p>Team Ghost<br>' +
'<a href="https://ghost.org">https://ghost.org</a></p>'
},
payload = {
mail: [{
message: message,
options: {}
}]
};

api.mail.send(payload).otherwise(function (error) {
errors.logError(
error.message,
"Unable to send welcome email, your blog will continue to function.",
"Please see http://docs.ghost.org/mail/ for instructions on configuring email."
);
});
res.json(200, {
redirect: config().paths.subdir + '/ghost/'
});

});
}).otherwise(function (error) {
res.json(401, {error: error.message});
});
}
};

Expand Down
4 changes: 2 additions & 2 deletions core/server/middleware/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ function updateActiveTheme(req, res, next) {
function redirectToSetup(req, res, next) {
/*jslint unparam:true*/

api.users.doesUserExist().then(function (exists) {
if (!exists && !req.path.match(/\/ghost\/setup\//)) {
api.authentication.isSetup().then(function (exists) {
if (!exists.setup[0].status && !req.path.match(/\/ghost\/setup\//)) {
return res.redirect(config().paths.subdir + '/ghost/setup/');
}
next();
Expand Down
3 changes: 2 additions & 1 deletion core/server/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,11 @@ User = ghostBookshelf.Model.extend({

setup: function (data, options) {
var self = this,
// Clone the _user so we don't expose the hashed password unnecessarily
userData = this.filterData(data);

options = this.filterOptions(options, 'setup');
options.withRelated = _.union([ 'roles' ], options.include);

return validatePasswordLength(userData.password).then(function () {
// Generate a new password hash
return generatePasswordHash(data.password);
Expand Down
1 change: 0 additions & 1 deletion core/server/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ adminRoutes = function (middleware) {
res.redirect(301, subdir + '/ghost/signup/');
});

router.post('/ghost/setup/', admin.doSetup);
router.post('/ghost/upload/', middleware.busboy, admin.upload);

// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
Expand Down
4 changes: 3 additions & 1 deletion core/server/routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ apiRoutes = function (middleware) {
router.get('/users/email/:email', api.http(api.users.read));
router.put('/users/password', api.http(api.users.changePassword));
router.put('/users/:id', api.http(api.users.edit));
router.post('/users', api.http(api.users.invite));
router.post('/users', api.http(api.users.add));
router.del('/users/:id', api.http(api.users.destroy));

// ## Tags
Expand Down Expand Up @@ -70,6 +70,8 @@ apiRoutes = function (middleware) {
router.post('/authentication/passwordreset', api.http(api.authentication.generateResetToken));
router.put('/authentication/passwordreset', api.http(api.authentication.resetPassword));
router.post('/authentication/invitation', api.http(api.authentication.acceptInvitation));
router.post('/authentication/setup', api.http(api.authentication.setup));
router.get('/authentication/setup', api.http(api.authentication.isSetup));
router.post('/authentication/token',
middleware.addClientSecret,
middleware.authenticateClient,
Expand Down
3 changes: 2 additions & 1 deletion core/test/integration/api/api_users_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var testUtils = require('../../utils'),

// Stuff we are testing
UsersAPI = require('../../../server/api/users');
AuthAPI = require('../../../server/api/authentication');

describe('Users API', function () {

Expand All @@ -32,7 +33,7 @@ describe('Users API', function () {
});

it('can add with internal user', function (done) {
UsersAPI.register({ users: [{
AuthAPI.setup({ setup: [{
'name': 'Hello World',
'email': 'hello@world.com',
'password': 'password'
Expand Down

0 comments on commit 8c2258d

Please sign in to comment.