From c6813a4582f2d12f801fec469937de2e722c68ff Mon Sep 17 00:00:00 2001 From: Ian Fox Date: Wed, 24 May 2017 10:28:37 -0700 Subject: [PATCH] feat: Get a user by token value --- README.md | 6 ++++ lib/tokenFactory.js | 2 +- lib/userFactory.js | 32 +++++++++++++++++++ test/lib/userFactory.test.js | 61 ++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7eeb1f50..2cc3ca2c 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,7 @@ factory.create(config).then(model => { #### Get Get a user based on id. Can pass the generatedId for the user, or the username, and the id will be determined automatically. +Can also pass a Screwdriver access token, and will get the user associated with that token. ```js factory.get(id).then(model => { // do stuff with user model @@ -425,12 +426,17 @@ factory.get(id).then(model => { factory.get({ username }).then(model => { // do stuff with user model }); + +factory.get({ token }).then(model => { + // do stuff with user model +}); ``` | Parameter | Type | Description | | :------------- | :---- | :-------------| | id | String | The unique ID for the build | | config.username | String | User name | +| config.accessToken | String | A user access token value | ### User Model ```js diff --git a/lib/tokenFactory.js b/lib/tokenFactory.js index 3eee100d..f24b49a3 100644 --- a/lib/tokenFactory.js +++ b/lib/tokenFactory.js @@ -61,7 +61,7 @@ class TokenFactory extends BaseFactory { * @return {Promise} */ get(config) { - if (config && config.value) { + if (config.value) { config.hash = generateToken.hashValue(config.value); delete config.value; } diff --git a/lib/userFactory.js b/lib/userFactory.js index 7b1fc713..c0e16ea4 100644 --- a/lib/userFactory.js +++ b/lib/userFactory.js @@ -64,6 +64,38 @@ class UserFactory extends BaseFactory { }); } + /** + * Get a user model by name, id, or personal access token + * @method get + * @param {Mixed} config + * @param {String} [config.username] Username of the user to look up + * @param {String} [config.accessToken] Access token of the user to look up + * @return {Promise} + */ + get(config) { + if (!config.accessToken) { + return super.get(config); + } + + // Lazy load factory dependency to prevent circular dependency issues + // https://nodejs.org/api/modules.html#modules_cycles + /* eslint-disable global-require */ + const TokenFactory = require('./tokenFactory'); + /* eslint-enable global-require */ + const tokenFactory = TokenFactory.getInstance(); + + return tokenFactory.get({ value: config.accessToken }) + .then((token) => { + if (!token) { + return token; + } + + token.lastUsed = (new Date()).toISOString(); + + return token.update().then(() => this.get(token.userId)); + }); + } + /** * Get an instance of the UserFactory * @method getInstance diff --git a/test/lib/userFactory.test.js b/test/lib/userFactory.test.js index ee49acc4..34a41b75 100644 --- a/test/lib/userFactory.test.js +++ b/test/lib/userFactory.test.js @@ -5,6 +5,7 @@ const mockery = require('mockery'); const sinon = require('sinon'); sinon.assert.expose(assert, { prefix: '' }); +require('sinon-as-promised'); describe('User Factory', () => { const password = 'totallySecurePassword'; @@ -12,6 +13,7 @@ describe('User Factory', () => { let datastore; let hashaMock; let ironMock; + let tokenFactoryMock; let factory; let User; @@ -35,9 +37,15 @@ describe('User Factory', () => { seal: sinon.stub(), defaults: 'defaults' }; + tokenFactoryMock = { + get: sinon.stub() + }; mockery.registerMock('screwdriver-hashr', hashaMock); mockery.registerMock('iron', ironMock); + mockery.registerMock('./tokenFactory', { + getInstance: sinon.stub().returns(tokenFactoryMock) + }); // eslint-disable-next-line global-require User = require('../../lib/user'); @@ -96,6 +104,59 @@ describe('User Factory', () => { }); }); + describe('get a user by access token', () => { + const accessToken = 'an access token goes here'; + const now = 1111; + const tokenMock = { + userId: 123, + lastUsed: null, + update: sinon.stub() + }; + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + sandbox.useFakeTimers(now); + tokenFactoryMock.get.resolves(tokenMock); + tokenMock.update.resolves(tokenMock); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return a user and update the last used field of the token', () => { + const expected = { + id: 123, + username: 'frodo' + }; + + datastore.get.resolves(expected); + + return factory.get({ accessToken }) + .then((user) => { + assert.isOk(user); + assert.calledWith(tokenFactoryMock.get, { value: accessToken }); + assert.calledOnce(tokenMock.update); + assert.equal(tokenMock.lastUsed, (new Date(now)).toISOString()); + }); + }); + + it('should return null if the user doesn\'t exist', () => { + datastore.get.resolves(null); + + return factory.get({ accessToken }) + .then(user => assert.isNull(user)); + }); + + it('should return null if the token doesn\'t exist', () => { + tokenFactoryMock.get.resolves(null); + + return factory.get({ accessToken }) + .then(user => assert.isNull(user)); + }); + }); + describe('getInstance', () => { let config;