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:
parent
ffcf7d1d83
commit
319a388277
5 changed files with 98 additions and 6 deletions
|
@ -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)]};
|
||||
}
|
||||
|
||||
|
|
19
core/server/auth/ghost-auth.js
Normal file
19
core/server/auth/ghost-auth.js
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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
63
core/server/auth/sync.js
Normal 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);
|
||||
}
|
||||
};
|
|
@ -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')}));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue