Skip to content

Commit

Permalink
馃帹 Ghost OAuth improvements (#7550)
Browse files Browse the repository at this point in the history
refs #7452

- 馃帹  logging.debug for public client registration
- 馃帹  add tests for passport ghost
- improve readability for passport file
- add basic tests
  • Loading branch information
kirrg001 authored and ErisDS committed Oct 12, 2016
1 parent 06151ef commit 22589e8
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 63 deletions.
131 changes: 68 additions & 63 deletions core/server/auth/passport.js
Expand Up @@ -8,73 +8,77 @@ var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy
errors = require('../errors'),
logging = require('../logging'),
models = require('../models'),
_private = {};
_private = {
retryTimeout: 3000,
retries: 10
};

/**
* Public client registration at Ghost.org
*/
_private.registerClient = function registerClient(options) {
_private.registerClient = function (options) {
var ghostOAuth2Strategy = options.ghostOAuth2Strategy,
url = options.url;

return new Promise(function (resolve, reject) {
var retry = function retry(retryCount, done) {
models.Client.findOne({slug: 'ghost-auth'}, {context: {internal: true}})
.then(function (client) {
// CASE: Ghost Auth client is already registered
if (client) {
return done(null, {
client_id: client.get('uuid'),
client_secret: client.get('secret')
});
}
return models.Client.findOne({slug: 'ghost-auth'}, {context: {internal: true}})
.then(function fetchedClient(client) {
// CASE: Ghost Auth client is already registered
if (client) {
return {
client_id: client.get('uuid'),
client_secret: client.get('secret')
};
}

return ghostOAuth2Strategy.registerClient({clientName: url})
.then(function addClient(credentials) {
return models.Client.add({
name: 'Ghost Auth',
slug: 'ghost-auth',
uuid: credentials.client_id,
secret: credentials.client_secret
}, {context: {internal: true}});
})
.then(function returnClient(client) {
return done(null, {
client_id: client.get('uuid'),
client_secret: client.get('secret')
});
})
.catch(function publicClientRegistrationError(err) {
logging.error(err);
return ghostOAuth2Strategy.registerClient({clientName: url})
.then(function addClient(credentials) {
return models.Client.add({
name: 'Ghost Auth',
slug: 'ghost-auth',
uuid: credentials.client_id,
secret: credentials.client_secret
}, {context: {internal: true}});
})
.then(function returnClient(client) {
return {
client_id: client.get('uuid'),
client_secret: client.get('secret')
};
});
});
};

if (retryCount < 0) {
return done(new errors.IncorrectUsageError({
message: 'Public client registration failed: ' + err.code || err.message,
context: 'Please verify that the url is reachable: ' + ghostOAuth2Strategy.url
}));
}
_private.startPublicClientRegistration = function startPublicClientRegistration(options) {
return new Promise(function (resolve, reject) {
(function retry(retries) {
options.retryCount = retries;

console.log('RETRY: Public Client Registration...');
var timeout = setTimeout(function () {
clearTimeout(timeout);
retryCount = retryCount - 1;
retry(retryCount, done);
}, 3000);
});
})
.catch(reject);
};
_private.registerClient(options)
.then(resolve)
.catch(function publicClientRegistrationError(err) {
logging.error(err);

retry(10, function retryPublicClientRegistration(err, client) {
if (err) {
return reject(err);
}
if (options.retryCount < 0) {
return reject(new errors.IncorrectUsageError({
message: 'Public client registration failed: ' + err.code || err.message,
context: 'Please verify that the url can be reached: ' + options.ghostOAuth2Strategy.url
}));
}

resolve(client);
});
logging.debug('Trying to register Public Client...');
var timeout = setTimeout(function () {
clearTimeout(timeout);

options.retryCount = options.retryCount - 1;
retry(options.retryCount);
}, _private.retryTimeout);
});
})(_private.retries);
});
};

/**
* auth types:
* - password: local login
* - ghost: remote login at Ghost.org
*/
exports.init = function initPassport(options) {
var type = options.type,
url = options.url;
Expand All @@ -93,14 +97,15 @@ exports.init = function initPassport(options) {
passReqToCallback: true
}, authStrategies.ghostStrategy);

_private.registerClient({ghostOAuth2Strategy: ghostOAuth2Strategy, url: utils.url.getBaseUrl()})
.then(function setClient(client) {
console.log('SUCCESS: Public Client Registration');
_private.startPublicClientRegistration({
ghostOAuth2Strategy: ghostOAuth2Strategy,
url: utils.url.getBaseUrl()
}).then(function setClient(client) {
logging.debug('Public Client Registration was successful');

ghostOAuth2Strategy.setClient(client);
passport.use(ghostOAuth2Strategy);
return resolve({passport: passport.initialize()});
})
.catch(reject);
ghostOAuth2Strategy.setClient(client);
passport.use(ghostOAuth2Strategy);
return resolve({passport: passport.initialize()});
}).catch(reject);
});
};
106 changes: 106 additions & 0 deletions core/test/unit/auth/passport_spec.js
@@ -0,0 +1,106 @@
var passport = require('passport'),
sinon = require('sinon'),
Promise = require('bluebird'),
rewire = require('rewire'),
should = require('should'),
sandbox = sinon.sandbox.create(),
testUtils = require('../../utils'),
GhostPassport = rewire('../../../server/auth/passport'),
models = require('../../../server/models'),
errors = require('../../../server/errors');

should.equal(true, true);

describe('Ghost Passport', function () {
var client;

function FakeGhostOAuth2Strategy() {
this.name = 'FakeGhostOAuth2Strategy';
}

before(function () {
models.init();
GhostPassport.__set__('GhostOAuth2Strategy', FakeGhostOAuth2Strategy);
GhostPassport.__set__('_private.retryTimeout', 50);
});

beforeEach(function () {
sandbox.spy(passport, 'use');

sandbox.stub(models.Client, 'findOne', function () {
return Promise.resolve(client);
});

sandbox.stub(models.Client, 'add').returns(Promise.resolve(new models.Client(testUtils.DataGenerator.forKnex.createClient())));

FakeGhostOAuth2Strategy.prototype.setClient = sandbox.stub();
FakeGhostOAuth2Strategy.prototype.registerClient = sandbox.stub().returns(Promise.resolve({}));
});

afterEach(function () {
sandbox.restore();
});

describe('auth_type: password', function () {
it('initialise passport with passport auth type', function () {
return GhostPassport.init({
type: 'passport'
}).then(function (response) {
should.exist(response.passport);
passport.use.callCount.should.eql(2);

models.Client.findOne.called.should.eql(false);
models.Client.add.called.should.eql(false);
FakeGhostOAuth2Strategy.prototype.setClient.called.should.eql(false);
FakeGhostOAuth2Strategy.prototype.registerClient.called.should.eql(false);
});
});
});

describe('auth_type: ghost', function () {
it('ghost client is already present in database', function () {
client = new models.Client(testUtils.DataGenerator.forKnex.createClient());

return GhostPassport.init({
type: 'ghost'
}).then(function (response) {
should.exist(response.passport);
passport.use.callCount.should.eql(3);

models.Client.findOne.called.should.eql(true);
models.Client.add.called.should.eql(false);
FakeGhostOAuth2Strategy.prototype.setClient.called.should.eql(true);
FakeGhostOAuth2Strategy.prototype.registerClient.called.should.eql(false);
});
});

it('ghost client does not exist', function () {
client = null;

return GhostPassport.init({
type: 'ghost'
}).then(function (response) {
should.exist(response.passport);
passport.use.callCount.should.eql(3);

models.Client.findOne.called.should.eql(true);
models.Client.add.called.should.eql(true);
FakeGhostOAuth2Strategy.prototype.setClient.called.should.eql(true);
FakeGhostOAuth2Strategy.prototype.registerClient.called.should.eql(true);
});
});

it('ghost client does not exist, ghost.org register client does not work', function () {
client = null;

FakeGhostOAuth2Strategy.prototype.registerClient.returns(Promise.reject(new Error('cannot connect to ghost.org')));

return GhostPassport.init({
type: 'ghost'
}).catch(function (err) {
(err instanceof errors.IncorrectUsageError).should.eql(true);
FakeGhostOAuth2Strategy.prototype.registerClient.callCount.should.eql(12);
});
});
});
});

0 comments on commit 22589e8

Please sign in to comment.