From 9430f5fa5138c88575518ca7c6f13caab0959f7c Mon Sep 17 00:00:00 2001 From: cobbspur Date: Wed, 1 Jun 2016 17:46:41 +0100 Subject: [PATCH] Add token added event, update last_login closes #6845 - emit token.added event when new access token is created - update last_login for user token.added event is triggered --- core/server/models/accesstoken.js | 16 ++++++- core/server/models/base/events.js | 17 ++++++++ core/server/models/index.js | 3 ++ .../model/model_accesstoken_spec.js | 43 +++++++++++++++++++ core/test/unit/model_events_spec.js | 37 ++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 core/server/models/base/events.js create mode 100644 core/test/integration/model/model_accesstoken_spec.js create mode 100644 core/test/unit/model_events_spec.js diff --git a/core/server/models/accesstoken.js b/core/server/models/accesstoken.js index e4d568ce44..02532eeb7d 100644 --- a/core/server/models/accesstoken.js +++ b/core/server/models/accesstoken.js @@ -1,11 +1,25 @@ var ghostBookshelf = require('./base'), Basetoken = require('./base/token'), + events = require('../events'), Accesstoken, Accesstokens; Accesstoken = Basetoken.extend({ - tableName: 'accesstokens' + tableName: 'accesstokens', + + emitChange: function emitChange(event) { + // Event named 'token' as access and refresh token will be merged in future, see #6626 + events.emit('token' + '.' + event, this); + }, + + initialize: function initialize() { + ghostBookshelf.Model.prototype.initialize.apply(this, arguments); + + this.on('created', function onCreated(model) { + model.emitChange('added'); + }); + } }); Accesstokens = ghostBookshelf.Collection.extend({ diff --git a/core/server/models/base/events.js b/core/server/models/base/events.js new file mode 100644 index 0000000000..850dfe2f3e --- /dev/null +++ b/core/server/models/base/events.js @@ -0,0 +1,17 @@ +var config = require('../../config'), + moment = require('moment'), + events = require(config.paths.corePath + '/server/events'), + models = require(config.paths.corePath + '/server/models'), + errors = require(config.paths.corePath + '/server/errors'); + +/** + * WHEN access token is created we will update last_login for user. + */ +events.on('token.added', function (tokenModel) { + models.User.edit( + {last_login: moment().utc()}, {id: tokenModel.get('user_id')} + ) + .catch(function (err) { + errors.logError(err); + }); +}); diff --git a/core/server/models/index.js b/core/server/models/index.js index 077de072bf..87b33927b4 100644 --- a/core/server/models/index.js +++ b/core/server/models/index.js @@ -7,6 +7,9 @@ var _ = require('lodash'), exports, models; +// Initialise model events +require('./base/events'); + /** * Expose all models */ diff --git a/core/test/integration/model/model_accesstoken_spec.js b/core/test/integration/model/model_accesstoken_spec.js new file mode 100644 index 0000000000..82e001fab7 --- /dev/null +++ b/core/test/integration/model/model_accesstoken_spec.js @@ -0,0 +1,43 @@ +/*globals describe, it, before, beforeEach, afterEach */ +var testUtils = require('../../utils'), + should = require('should'), + events = require('../../../server/events'), + utils = require('../../../server/utils'), + + sinon = require('sinon'), + sandbox = sinon.sandbox.create(), + // Stuff we are testing + AccesstokenModel = require('../../../server/models/accesstoken').Accesstoken; + +describe('Accesstoken Model', function () { + // Keep the DB clean + before(testUtils.teardown); + afterEach(testUtils.teardown); + + afterEach(function () { + sandbox.restore(); + }); + + beforeEach(testUtils.setup('users', 'clients')); + + it('on creation emits token.added event', function (done) { + // Setup + var eventSpy = sandbox.spy(events, 'emit'); + // Test + // Stub refreshtoken + AccesstokenModel.add({ + token: 'foobartoken', + user_id: 1, + client_id: 1, + expires: Date.now() + utils.ONE_HOUR_MS + }) + .then(function (token) { + should.exist(token); + // Assert + eventSpy.calledOnce.should.be.true(); + eventSpy.calledWith('token.added').should.be.true(); + + done(); + }).catch(done); + }); +}); diff --git a/core/test/unit/model_events_spec.js b/core/test/unit/model_events_spec.js new file mode 100644 index 0000000000..90b9232d20 --- /dev/null +++ b/core/test/unit/model_events_spec.js @@ -0,0 +1,37 @@ +/*globals describe, before, afterEach, it*/ +var should = require('should'), + sinon = require('sinon'), + sandbox = sinon.sandbox.create(), + events = require('../../server/events'), + Models = require('../../server/models'); + +// To stop jshint complaining +should.equal(true, true); + +describe('Model Events', function () { + before(function () { + // Loads all the models + Models.init(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('on token added', function () { + it('calls User edit when event is emitted', function (done) { + // Setup + var userModelSpy = sandbox.spy(Models.User, 'edit'); + // Test + events.emit('token.added', {get: function () { return 1; }}); + // Assert + userModelSpy.calledOnce.should.be.true(); + userModelSpy.calledWith( + sinon.match.has('last_login'), + sinon.match.has('id') + ); + + done(); + }); + }); +});