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

ghost auth: sync email (#8027)

*   ghost auth: sync email

refs #7452

- sync email changes in background (every hour right now)
- sync logged in user only!
- no sync if auth strategy password is used
- GET /users/me is triggered on every page refresh
- added TODO to support or add long polling for syncing data later
- no tests yet on purpose, as i would like to get a basic review first

* 🐩  use events

- remember sync per user
This commit is contained in:
Katharina Irrgang 2017-02-23 19:04:24 +01:00 committed by Hannah Wolfe
parent ffcf7d1d83
commit 319a388277
5 changed files with 98 additions and 6 deletions

View file

@ -5,6 +5,7 @@ var Promise = require('bluebird'),
dataProvider = require('../models'),
canThis = require('../permissions').canThis,
errors = require('../errors'),
events = require('../events'),
utils = require('./utils'),
pipeline = require('../utils/pipeline'),
i18n = require('../i18n'),
@ -59,10 +60,11 @@ users = {
*/
read: function read(options) {
var attrs = ['id', 'slug', 'status', 'email', 'role'],
tasks;
tasks,
isMe = options.id === 'me';
// Special handling for id = 'me'
if (options.id === 'me' && options.context && options.context.user) {
// Special handling for /users/me request
if (isMe && options.context && options.context.user) {
options.id = options.context.user;
}
@ -87,6 +89,10 @@ users = {
// Pipeline calls each task passing the result of one to be the arguments for the next
return pipeline(tasks, options).then(function formatResponse(result) {
if (result) {
if (isMe) {
events.emit('read:users:me', result);
}
return {users: [result.toJSON(options)]};
}

View file

@ -0,0 +1,19 @@
var passport = require('passport'),
Promise = require('bluebird');
module.exports.getUser = function getUser(options) {
options = options || {};
var token = options.token,
ghostOAuth2Strategy = passport._strategies.ghost;
return new Promise(function (resolve, reject) {
ghostOAuth2Strategy.userProfile(token, function (err, profile) {
if (err) {
return reject(err);
}
resolve(profile);
});
});
};

View file

@ -1,10 +1,13 @@
var passport = require('./passport'),
authorize = require('./authorize'),
authenticate = require('./authenticate'),
oauth = require('./oauth');
sync = require('./sync'),
oauth = require('./oauth'),
ghostAuth = require('./ghost-auth');
exports.init = function (options) {
oauth.init();
oauth.init(options);
sync.init(options);
return passport.init(options)
.then(function (response) {
@ -15,3 +18,4 @@ exports.init = function (options) {
exports.oauth = oauth;
exports.authorize = authorize;
exports.authenticate = authenticate;
exports.ghostAuth = ghostAuth;

63
core/server/auth/sync.js Normal file
View file

@ -0,0 +1,63 @@
var debug = require('ghost-ignition').debug('sync'),
models = require('../models'),
ghostAuth = require('./ghost-auth'),
logging = require('../logging'),
errors = require('../errors'),
events = require('../events'),
knex = require('../data/db').knex,
_private = {
syncIntervalInMs: 1000 * 60 * 60,
lastSync: {}
};
/**
* @TODO: support long polling in the ghost auth service
*/
_private.syncUser = function syncUser(loggedInUserModel) {
debug('syncUser');
// CASE: sync every hour for now
if (_private.lastSync[loggedInUserModel.id]) {
if ((_private.lastSync[loggedInUserModel.id] + _private.syncIntervalInMs) > Date.now()) {
debug('too early too sync');
return;
}
}
return ghostAuth.getUser({
token: loggedInUserModel.get('ghost_auth_access_token')
}).then(function (ghostUser) {
debug('ghost_email', ghostUser.email);
debug('user_email', loggedInUserModel.get('email'));
if (ghostUser.email === loggedInUserModel.get('email')) {
debug('email has not changed');
return;
}
debug('sync email');
// CASE: we update the user in a transaction to avoid collisions
return knex.transaction(function onTransaction(transaction) {
return models.User.edit({
email: ghostUser.email
}, {id: loggedInUserModel.id, transacting: transaction});
});
}).then(function () {
debug('update lastSync');
_private.lastSync[loggedInUserModel.id] = Date.now();
}).catch(function onError(err) {
logging.error(new errors.InternalServerError({
message: 'ghost-auth: sync failed',
err: err
}));
});
};
module.exports.init = function init(options) {
var authType = options.authType;
if (authType === 'ghost') {
events.on('read:users:me', _private.syncUser);
}
};

View file

@ -370,7 +370,7 @@ User = ghostBookshelf.Model.extend({
if (data.email) {
ops.push(function checkForDuplicateEmail() {
return self.getByEmail(data.email).then(function then(user) {
return self.getByEmail(data.email, options).then(function then(user) {
if (user && user.id !== options.id) {
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.user.userUpdateError.emailIsAlreadyInUse')}));
}