Move setup to API

closes #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 #3145)
- removed unused methods from api/users.js
This commit is contained in:
Sebastian Gierlinger 2014-07-11 14:17:09 +02:00
parent 2658e8ea13
commit 8c2258dc4c
9 changed files with 103 additions and 173 deletions

View File

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

View File

@ -1,4 +1,5 @@
var dataProvider = require('../models'),
var _ = require('lodash'),
dataProvider = require('../models'),
settings = require('./settings'),
mail = require('./mail'),
utils = require('./utils'),
@ -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));
});
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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