Skip to content

Commit

Permalink
Merge pull request #161 from screwdriver-cd/user-tokens
Browse files Browse the repository at this point in the history
feat: Add token models and tests
  • Loading branch information
d2lam authored May 17, 2017
2 parents 48f5310 + 1967690 commit 0d0d6ef
Show file tree
Hide file tree
Showing 12 changed files with 424 additions and 9 deletions.
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const PipelineFactory = require('./lib/pipelineFactory');
const SecretFactory = require('./lib/secretFactory');
const UserFactory = require('./lib/userFactory');
const TemplateFactory = require('./lib/templateFactory');
const TokenFactory = require('./lib/tokenFactory');

module.exports = {
BuildFactory,
Expand All @@ -15,5 +16,6 @@ module.exports = {
PipelineFactory,
SecretFactory,
UserFactory,
TemplateFactory
TemplateFactory,
TokenFactory
};
2 changes: 1 addition & 1 deletion lib/eventFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class EventFactory extends BaseFactory {
/**
* Instantiate an Event class
* @method createClass
* @param config
* @param {Object} config
* @return {Event}
*/
createClass(config) {
Expand Down
2 changes: 1 addition & 1 deletion lib/secretFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class SecretFactory extends BaseFactory {
/**
* Instantiate a Secret class
* @method createClass
* @param config
* @param {Object} config
* @return {Secret}
*/
createClass(config) {
Expand Down
22 changes: 22 additions & 0 deletions lib/token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const BaseModel = require('./base');

class TokenModel extends BaseModel {
/**
* Construct a TokenModel object
* @method constructor
* @param {Object} config
* @param {Object} config.datastore Object that will perform operations on the datastore
* @param {Number} config.userId The ID of the associated user
* @param {String} config.uuid UUID for revoking the token
* @param {String} config.name The token name
* @param {String} config.description The token description
* @param {String} config.lastUsed The last time the token was used (ISO String)
*/
constructor(config) {
super('token', config);
}
}

module.exports = TokenModel;
59 changes: 59 additions & 0 deletions lib/tokenFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

const BaseFactory = require('./baseFactory');
const Token = require('./token');

let instance;

class TokenFactory extends BaseFactory {
/**
* Construct a TokenFactory object
* @method constructor
* @param {Object} config
* @param {Object} config.datastore Object that will perform operations on the datastore
*/
constructor(config) {
super('token', config);
}

/**
* Instantiate a Token class
* @method createClass
* @param {Object} config
* @return {Token}
*/
createClass(config) {
return new Token(config);
}

/**
* Create a token model
* @method create
* @param {Object} config
* @param {String} config.userId The ID of the associated user
* @param {String} config.value The token value
* @param {String} config.name The token name
* @param {String} config.description The token description
* @return {Promise}
*/
create(config) {
config.lastUsed = null;

return super.create(config);
}

/**
* Get an instance of the TokenFactory
* @method getInstance
* @param {Object} config
* @param {Datastore} config.datastore A datastore instance
* @return {TokenFactory}
*/
static getInstance(config) {
instance = BaseFactory.getInstance(TokenFactory, instance, config);

return instance;
}
}

module.exports = TokenFactory;
56 changes: 56 additions & 0 deletions lib/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
const BaseModel = require('./base');
const iron = require('iron');
const nodeify = require('./nodeify');
const PAGINATE_COUNT = 50;
const PAGINATE_PAGE = 1;
// Get symbols for private fields
const password = Symbol('password');

Expand Down Expand Up @@ -43,6 +45,60 @@ class UserModel extends BaseModel {
[this.token, this[password], iron.defaults]);
}

/** Fetch a user's tokens
/* @property tokens
/* @return {Promise}
*/
get tokens() {
const listConfig = {
params: {
userId: this.id
},
paginate: {
count: PAGINATE_COUNT,
page: PAGINATE_PAGE
}
};

// 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 factory = TokenFactory.getInstance();
const tokens = factory.list(listConfig);

// ES6 has weird getters and setters in classes,
// so we redefine the pipeline property here to resolve to the
// resulting promise and not try to recreate the factory, etc.
Object.defineProperty(this, 'tokens', {
enumerable: true,
value: tokens
});

return tokens;
}

/**
* Test if a token is valid and belongs to the user
* @method validateToken
* @param {String} uuid UUID of token to validate
* @return {Promise}
*/
validateToken(uuid) {
return this.tokens.then((tokens) => {
const token = tokens.find(t => t.uuid === uuid);

if (!token) {
throw new Error('Token has been revoked.');
}

token.lastUsed = (new Date()).toISOString();

return token.update();
});
}

/**
* Get permissions on a specific repo
* @method getPermissions
Expand Down
2 changes: 1 addition & 1 deletion lib/userFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class UserFactory extends BaseFactory {
/**
* Instantiate a User class
* @method createClass
* @param config
* @param {Object} config
* @return {User}
*/
createClass(config) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@
"hoek": "^4.0.1",
"iron": "^4.0.1",
"screwdriver-config-parser": "^3.0.1",
"screwdriver-data-schema": "^16.0.0"
"screwdriver-data-schema": "^16.8.2"
}
}
4 changes: 4 additions & 0 deletions test/lib/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ describe('Build Model', () => {
});
});

afterEach(() => {
sandbox.restore();
});

it('promises to start a build', () =>
build.start()
.then(() => {
Expand Down
75 changes: 75 additions & 0 deletions test/lib/token.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const assert = require('chai').assert;
const sinon = require('sinon');
const mockery = require('mockery');
const schema = require('screwdriver-data-schema');
const BaseModel = require('../../lib/base');
const TokenModel = require('../../lib/token');

sinon.assert.expose(assert, { prefix: '' });
require('sinon-as-promised');

describe('Token Model', () => {
const password = 'password';
let datastore;
let createConfig;
let token;

before(() => {
mockery.enable({
useCleanCache: true,
warnOnUnregistered: false
});
datastore = {
update: sinon.stub()
};
});

beforeEach(() => {
datastore.update.resolves({});

createConfig = {
datastore,
userId: 12345,
uuid: '1a2b3c',
id: 6789,
name: 'Mobile client auth token',
description: 'For the mobile app',
lastUsed: '2017-05-10T01:49:59.327Z',
password
};
token = new TokenModel(createConfig);
});

after(() => {
mockery.disable();
});

it('is constructed properly', () => {
assert.instanceOf(token, TokenModel);
assert.instanceOf(token, BaseModel);
schema.models.token.allKeys.forEach((key) => {
assert.strictEqual(token[key], createConfig[key]);
});
});

describe('update', () => {
it('promises to update a token', () => {
const newTimestamp = '2017-05-13T02:01:17.588Z';

token.lastUsed = newTimestamp;

return token.update()
.then(() => {
assert.calledWith(datastore.update, {
table: 'tokens',
params: {
id: 6789,
lastUsed: newTimestamp
}
});
});
});
});
});
Loading

0 comments on commit 0d0d6ef

Please sign in to comment.