bridge-node-server/node_server/swagger_api/specs/api_security_device.spec.js
Martin Donnelly 57bd6c8e6a init
2018-06-24 21:15:03 +01:00

846 lines
33 KiB
JavaScript

/**
* Unit testing file for ElevateSession command
*/
'use strict';
/* eslint max-nested-callbacks: ["error", 7] import/max-dependencies: ["error", {"max": 13}] */
// eslint-disable-next-line no-unused-vars
const testGlobals = require('../../tools/test/testGlobals.js');
const _ = require('lodash');
const Q = require('q');
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const chaiAsPromised = require('chai-as-promised');
const rewire = require('rewire');
const JsonRefs = require('json-refs');
const mongodb = require('mongodb');
const {MockRequest} = require('../../utils/test/mock-request');
const {bridgeBodyParser} = require('../api_body_middleware.js');
const utils = require('../../ComServe/utils');
/**
* Use rewire to pull in the unit under test, and then get access to the
* private variables to stub them
*/
const apiSecurityDevice = rewire('../api_security_device.js');
const authStub = apiSecurityDevice.__get__('auth');
const flagsStub = apiSecurityDevice.__get__('featureFlags');
const referencesStub = apiSecurityDevice.__get__('references');
const mainDBPStub = apiSecurityDevice.__get__('mainDBP');
/**
* Set up chai & sinon to simplify the tests
*/
const expect = chai.expect;
const sandbox = sinon.createSandbox();
chai.use(sinonChai);
chai.use(chaiAsPromised);
/**
* Make a promise-style version of the security handler for easier testing
*/
const deviceSessionP = (req, def, scopes) => Q.nfcall(
apiSecurityDevice.deviceSession,
req,
def,
scopes
);
const hmacNoSessionP = (req, def, scopes) => Q.nfcall(
apiSecurityDevice.deviceHmacNoSession,
req,
def,
scopes
);
const COLLECTION_DEVICES = 'Mock devices collection parameter';
/**
* Valid values
*/
const DEVICE_TOKEN = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop';
const SESSION_TOKEN = 'qrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUV';
const DEVICE_MONGO_ID = (new mongodb.ObjectID()).toHexString(); // New random ObjectID
const CLIENT_NAME = 'a@example.com';
const CLIENT_MONGO_ID = (new mongodb.ObjectID()).toHexString(); // New random ObjectID
const CLIENT_ID = 'A unique random value generated by us';
const CLIENT_DISPLAY_NAME = 'Display Name';
const SESSION_HEADER = DEVICE_TOKEN + ':' + SESSION_TOKEN;
const HMAC_HEADER = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
const TIMESTAMP_HEADER = new Date().toISOString();
const PROTOCOL = 'https';
const PATH = '/api/v0/devices';
const EXPECTED_FULL_URL = 'https://unittest.example.com' + PATH;
const METHOD = 'get';
const SESSION_ID = 'A session id as-if generated by express-session middleware';
const DB_FEATURE_FLAGS = ['unit-test'];
const DB_DEVICE = {
ClientID: CLIENT_ID,
DeviceToken: DEVICE_TOKEN,
DeviceStatus: utils.DeviceFullyRegistered
};
const DB_CLIENT = {
_id: CLIENT_MONGO_ID,
ClientName: CLIENT_NAME,
ClientID: CLIENT_ID,
DisplayName: CLIENT_DISPLAY_NAME,
FeatureFlags: DB_FEATURE_FLAGS,
ClientStatus: utils.ClientEmailVerifiedMask
};
const MOCK_SWAGGER_PATHNAME = '/test-api-security-device';
const MOCK_SWAGGER_FEATURE_FLAG = 'unit-test';
const MOCK_SWAGGER_DEFINITION = {
post: {
summary: 'Just a test',
description: 'Just a test',
'x-feature-flag': MOCK_SWAGGER_FEATURE_FLAG,
responses: {
200: {
description: 'Success'
}
}
}
};
/**
* Mock request for requests that use the standard security model
*/
const MOCK_REQUEST_OPTIONS = {
headers: {
'x-bridge-device-session': SESSION_HEADER,
'x-bridge-hmac': HMAC_HEADER,
'x-bridge-timestamp': TIMESTAMP_HEADER
},
originalUrl: PATH,
protocol: PROTOCOL,
method: METHOD,
sessionID: SESSION_ID,
session: {} // Empty session as-if created by express-session
};
const MOCK_REQUEST_BODY = '{\n' +
' "test": "value",\n' +
' "other": "value2"\n' +
'}';
const MOCK_REQUEST_BODY_OPTIONS = {
mockBody: MOCK_REQUEST_BODY
};
/**
* Mock request for requests that use the "no session" security model
*/
const SWAGGER_PATH_LOGIN = '/devices/{objectId}/login';
const PATH_LOGIN = '/api/v0/devices/' + DEVICE_MONGO_ID + '/login';
const EXPECTED_FULL_URL_LOGIN = 'https://unittest.example.com' + PATH_LOGIN;
const METHOD_LOGIN = 'POST';
const MOCK_LOGIN_REQUEST_OPTIONS = {
headers: {
'x-bridge-hmac': HMAC_HEADER,
'x-bridge-timestamp': TIMESTAMP_HEADER
},
originalUrl: PATH_LOGIN,
protocol: PROTOCOL,
method: METHOD_LOGIN,
sessionID: SESSION_ID,
session: {} // Empty session as-if created by express-session
};
const MOCK_LOGIN_REQUEST_BODY = '{\n' +
' "ClientName": "' + CLIENT_NAME + '"\n' +
'}';
const MOCK_LOGIN_REQUEST_BODY_OPTIONS = {
mockBody: MOCK_LOGIN_REQUEST_BODY
};
/**
* Function to create a mock `req` objects that mimics the important parts of a
* real request object.
*
* @param {Object} resolvedSwagger - The **resolved** swagger object (i.e. all refs resolved)
* @param {Object} reqOptions - The additional fields to add to the request object
* @param {Object} bodyOptions - Additional params for creating the MockRequest (provides the body)
*/
function createMockReq(resolvedSwagger, reqOptions, bodyOptions) {
const req = new MockRequest(_.cloneDeep(bodyOptions));
_.merge(req, _.cloneDeep(reqOptions));
return req;
}
/**
* The tests
*/
describe('Device security validation', () => {
let resolvedSwagger;
let req;
const def = {};
/**
* Before we run any tests we need to resolve all the references within
* the swagger specification.
*/
before(() => {
/**
* Set some values for the collections so we can differentiate them
*/
mainDBPStub.mainDB._collectionDevice = mainDBPStub.mainDB.collectionDevice;
mainDBPStub.mainDB.collectionDevice = COLLECTION_DEVICES;
/**
* Load the swagger files and merge them back into a single file
*/
return JsonRefs
.resolveRefsAt(require.resolve('../api_swagger_def.json'))
.then((swagger) => {
/**
* Add our test path to the swagger
*/
resolvedSwagger = swagger.resolved;
resolvedSwagger.paths[MOCK_SWAGGER_PATHNAME] = MOCK_SWAGGER_DEFINITION;
//
// Add them to the default options
//
_.assign(MOCK_REQUEST_OPTIONS, {
swagger: {
swaggerObject: resolvedSwagger,
operation: resolvedSwagger.paths[MOCK_SWAGGER_PATHNAME].post
}
});
return resolvedSwagger;
});
});
after(() => {
/**
* Set the collections back
*/
mainDBPStub.mainDB.collectionDevice = mainDBPStub.mainDB._collectionDevice;
delete mainDBPStub.mainDB._collectionDevice;
});
describe('device_session security', () => {
/**
* Before each test, stub the auth functions we will be calling.
*/
beforeEach((done) => {
req = createMockReq(resolvedSwagger, MOCK_REQUEST_OPTIONS, MOCK_REQUEST_BODY_OPTIONS);
sandbox.stub(authStub, 'validateCurrentSession').resolves([DB_DEVICE, DB_CLIENT]);
sandbox.stub(authStub, 'checkHMAC').resolves();
sandbox.spy(flagsStub, 'isEnabled');
//
// Run the mock request through the middleware to get the parsed and raw bodies
//
bridgeBodyParser()(req, {}, done);
});
/**
* After each test we reset the sanbox to reset all stubs etc.
*/
afterEach(() => {
sandbox.restore();
});
describe('With validly formatted params that are correct', () => {
it('checks the device and session tokens', () => {
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(authStub.validateCurrentSession)
.to.have.been.calledOnce
.calledWith(
DEVICE_TOKEN,
SESSION_TOKEN
);
});
});
it('checks the HMAC', () => {
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(authStub.checkHMAC)
.to.have.been.calledOnce
.calledWith(
DB_DEVICE,
{
address: EXPECTED_FULL_URL,
method: METHOD,
body: MOCK_REQUEST_BODY + DEVICE_TOKEN + ':' + SESSION_TOKEN,
ClientName: CLIENT_NAME,
timestamp: TIMESTAMP_HEADER,
hmac: HMAC_HEADER
}
);
});
});
it('checks the FeatureFlags if they are required for the request', () => {
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(flagsStub.isEnabled)
.to.have.been.calledOnce
.calledWith(
MOCK_SWAGGER_FEATURE_FLAG,
DB_CLIENT
);
});
});
it('doesn\'t check the FeatureFlags if they are NOT required for the request', () => {
delete req.swagger.operation['x-feature-flag'];
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(flagsStub.isEnabled)
.to.not.have.been.called;
});
});
it('stores web session data + the client (as clientObj) and device (as deviceObj) in req.session.data for controller to use', () => {
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(req.session.data)
.to.deep.equal({
//
// Existing session details for old web console requests
//
client: CLIENT_MONGO_ID,
clientID: CLIENT_ID,
displayName: CLIENT_DISPLAY_NAME,
email: CLIENT_NAME,
isMerchant: false,
isVATRegistered: false,
FeatureFlags: DB_FEATURE_FLAGS,
//
// New sessiond details for App APIs copied across
//
clientObj: DB_CLIENT,
deviceObj: DB_DEVICE,
isDeviceSession: true
});
});
});
it('clears req.sessionID so express-session doesn\'t persist the session', () => {
return deviceSessionP(req, def, SESSION_HEADER).then(() => {
return expect(req.sessionID)
.to.be.null;
});
});
it('passes the security tests', () => {
return expect(deviceSessionP(req, def, SESSION_HEADER))
.to.eventually.be.fulfilled;
});
});
describe('With validly formatted params that are wrong', () => {
it('rejects when required feature flag isn\'t enabled', () => {
const NO_FEATURE_FLAG_CLIENT = {
ClientName: CLIENT_NAME
};
authStub.validateCurrentSession.resolves([DB_DEVICE, NO_FEATURE_FLAG_CLIENT]);
return expect(deviceSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when validating current session fails', () => {
authStub.validateCurrentSession.rejects();
return expect(deviceSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when checking HMAC fails', () => {
authStub.checkHMAC.rejects();
return expect(deviceSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('leaves req.sessionID alone when device session verifcation fails', () => {
authStub.checkHMAC.rejects();
return deviceSessionP(req, def, SESSION_HEADER).catch(() => {
return expect(req.sessionID)
.to.equal(SESSION_ID);
});
});
});
describe('With invalidly formatted params', () => {
describe('Rejects when x-bridge-session-device is the wrong format: ', () => {
it('missing entirely', () => {
return expect(deviceSessionP(req, def, undefined)).to
.eventually.be.rejected;
});
it('device token too short', () => {
const token = DEVICE_TOKEN.slice(0, -1) + ':' + SESSION_TOKEN;
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('device token too long', () => {
const token = DEVICE_TOKEN + 'a:' + SESSION_TOKEN;
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('device token invalid char', () => {
const token = DEVICE_TOKEN.slice(0, -1) + '!:' + SESSION_TOKEN;
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('session token too short', () => {
const token = DEVICE_TOKEN + ':' + SESSION_TOKEN.slice(0, -1);
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('session token too long', () => {
const token = DEVICE_TOKEN + ':b' + SESSION_TOKEN;
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('session token invalid char', () => {
const token = DEVICE_TOKEN + ':?' + SESSION_TOKEN.slice(0, -1);
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
it('wrong character between tokens', () => {
const token = DEVICE_TOKEN + ';' + SESSION_TOKEN;
return expect(deviceSessionP(req, def, token)).to
.eventually.be.rejected;
});
});
describe('Rejects when x-bridge-hmac is the wrong format: ', () => {
it('missing entirely', () => {
delete req.headers['x-bridge-hmac'];
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too short', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER.slice(0, -1);
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too long', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER + 'a';
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('invalid char', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER.slice(0, -1) + '!';
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
describe('Rejects when x-bridge-timestamp is the wrong format: ', () => {
it('missing entirely', () => {
delete req.headers['x-bridge-timestamp'];
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('not a string', () => {
req.headers['x-bridge-timestamp'] = Date.now();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('only has date', () => {
req.headers['x-bridge-timestamp'] = new Date().toDateString();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('only has time', () => {
req.headers['x-bridge-timestamp'] = new Date().toTimeString();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
});
});
describe('device_hmac_nosession security', () => {
/**
* Before each test, stub the auth functions we will be calling.
*/
beforeEach((done) => {
//
// Setup the swagger details as-if parsed from the request by the
// swagger middleware
//
_.assign(MOCK_LOGIN_REQUEST_OPTIONS, {
swagger: {
swaggerObject: resolvedSwagger,
operation: resolvedSwagger.paths[SWAGGER_PATH_LOGIN].post,
params: {
objectId: {
value: DEVICE_MONGO_ID
},
body: {
value: {
ClientName: CLIENT_NAME
}
}
}
}
});
req = createMockReq(
resolvedSwagger,
MOCK_LOGIN_REQUEST_OPTIONS,
MOCK_LOGIN_REQUEST_BODY_OPTIONS
);
sandbox.stub(authStub, 'checkHMAC').resolves();
sandbox.stub(referencesStub, 'getClientByEmail').resolves(DB_CLIENT);
sandbox.stub(mainDBPStub, 'findOneObject').resolves(DB_DEVICE);
sandbox.spy(flagsStub, 'isEnabled');
sandbox.spy(authStub, 'checkClientStatus');
sandbox.spy(authStub, 'checkDeviceStatus');
//
// Run the mock request through the middleware to get the parsed and raw bodies
//
bridgeBodyParser()(req, {}, done);
});
/**
* After each test we reset the sanbox to reset all stubs etc.
*/
afterEach(() => {
sandbox.restore();
});
describe('With validly formatted params that are correct', () => {
it('gets the client based on the ClientName in the body', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(referencesStub.getClientByEmail)
.to.have.been.calledOnce
.calledWith(CLIENT_NAME);
});
});
it('gets the device based on the objectID if it is owned by the correct client', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(mainDBPStub.findOneObject)
.to.have.been.calledOnce
.calledWith(
mainDBPStub.mainDB.collectionDevice,
{
_id: mongodb.ObjectID(DEVICE_MONGO_ID),
ClientID: CLIENT_ID
}
);
});
});
it('checks the client is in a valid state', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(authStub.checkClientStatus)
.to.have.been.calledOnce
.calledWith(utils.ClientEmailVerifiedMask)
.returned(null); // Null for no errors
});
});
it('checks the device is in a valid state', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(authStub.checkDeviceStatus)
.to.have.been.calledOnce
.calledWith(utils.DeviceFullyRegistered)
.returned(null); // Null for no errors
});
});
it('checks the HMAC, with the function name set to Login1.process', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(authStub.checkHMAC)
.to.have.been.calledOnce
.calledWith(
DB_DEVICE,
{
address: EXPECTED_FULL_URL_LOGIN,
method: METHOD_LOGIN,
body: MOCK_LOGIN_REQUEST_BODY,
ClientName: CLIENT_NAME,
timestamp: TIMESTAMP_HEADER,
hmac: HMAC_HEADER
},
'Login1.process' // Renamed to Login1.process to match expectations
);
});
});
it('checks the FeatureFlags if they are required for the request', () => {
req.swagger.operation['x-feature-flag'] = MOCK_SWAGGER_FEATURE_FLAG;
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(flagsStub.isEnabled)
.to.have.been.calledOnce
.calledWith(
MOCK_SWAGGER_FEATURE_FLAG,
DB_CLIENT
);
});
});
it('doesn\'t check the FeatureFlags if they are NOT required for the request', () => {
delete req.swagger.operation['x-feature-flag'];
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(flagsStub.isEnabled)
.to.not.have.been.called;
});
});
it('stores web session data + the client (as clientObj) and device (as deviceObj) in req.session.data for controller to use', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(req.session.data)
.to.deep.equal({
//
// Existing session details for old web console requests
//
client: CLIENT_MONGO_ID,
clientID: CLIENT_ID,
displayName: CLIENT_DISPLAY_NAME,
email: CLIENT_NAME,
isMerchant: false,
isVATRegistered: false,
FeatureFlags: DB_FEATURE_FLAGS,
//
// New sessiond details for App APIs copied across
//
clientObj: DB_CLIENT,
deviceObj: DB_DEVICE,
isDeviceSession: true
});
});
});
it('clears req.sessionID so express-session doesn\'t persist the session', () => {
return hmacNoSessionP(req, def, SESSION_HEADER).then(() => {
return expect(req.sessionID)
.to.be.null;
});
});
it('passes the security tests', () => {
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.to.eventually.be.fulfilled;
});
});
describe('With validly formatted params that are wrong', () => {
it('rejects when required feature flag isn\'t enabled', () => {
// Fake that a feature flag is required
req.swagger.operation['x-feature-flag'] = MOCK_SWAGGER_FEATURE_FLAG;
// Return a client that doesn't have that flag
const modifiedClient = _.cloneDeep(DB_CLIENT);
modifiedClient.FeatureFlags = [];
referencesStub.getClientByEmail.resolves(modifiedClient);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when finding the client fails', () => {
referencesStub.getClientByEmail.rejects();
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when finding the device fails', () => {
mainDBPStub.findOneObject.rejects();
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when the client isn\'t verified', () => {
const modifiedClient = _.cloneDeep(DB_CLIENT);
modifiedClient.ClientStatus = 0;
referencesStub.getClientByEmail.resolves(modifiedClient);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when the client is barred', () => {
const modifiedClient = _.cloneDeep(DB_CLIENT);
modifiedClient.ClientStatus |= utils.ClientBarredMask;
referencesStub.getClientByEmail.resolves(modifiedClient);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when the device isn\'t completely registered', () => {
const modifiedDevice = _.cloneDeep(DB_DEVICE);
modifiedDevice.DeviceStatus = utils.DeviceRegister2Mask;
mainDBPStub.findOneObject.resolves(modifiedDevice);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when the device is suspended', () => {
const modifiedDevice = _.cloneDeep(DB_DEVICE);
modifiedDevice.DeviceStatus |= utils.DeviceSuspendedMask;
mainDBPStub.findOneObject.resolves(modifiedDevice);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when the device is barred', () => {
const modifiedDevice = _.cloneDeep(DB_DEVICE);
modifiedDevice.DeviceStatus |= utils.DeviceBarredMask;
mainDBPStub.findOneObject.resolves(modifiedDevice);
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('rejects when checking HMAC fails', () => {
authStub.checkHMAC.rejects();
return expect(hmacNoSessionP(req, def, SESSION_HEADER))
.eventually.be.rejected;
});
it('leaves req.sessionID alone when device session verifcation fails', () => {
authStub.checkHMAC.rejects();
return hmacNoSessionP(req, def, SESSION_HEADER).catch(() => {
return expect(req.sessionID)
.to.equal(SESSION_ID);
});
});
});
describe('With invalidly formatted params', () => {
describe('Rejects when x-bridge-hmac is the wrong format: ', () => {
it('missing entirely', () => {
delete req.headers['x-bridge-hmac'];
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too short', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER.slice(0, -1);
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too long', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER + 'a';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('invalid char', () => {
req.headers['x-bridge-hmac'] = HMAC_HEADER.slice(0, -1) + '!';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
describe('Rejects when x-bridge-timestamp is the wrong format: ', () => {
it('missing entirely', () => {
delete req.headers['x-bridge-timestamp'];
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('not a string', () => {
req.headers['x-bridge-timestamp'] = Date.now();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('only has date', () => {
req.headers['x-bridge-timestamp'] = new Date().toDateString();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('only has time', () => {
req.headers['x-bridge-timestamp'] = new Date().toTimeString();
return expect(deviceSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
describe('Rejects when objectId is the wrong format: ', () => {
it('too short', () => {
req.swagger.params.objectId.value = DEVICE_MONGO_ID.slice(0, -1);
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too long', () => {
req.swagger.params.objectId.value = DEVICE_MONGO_ID + 'a';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('invalid char', () => {
req.swagger.params.objectId.value = DEVICE_MONGO_ID.slice(0, -1) + 'g';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
describe('Rejects when ClientName is the wrong format: ', () => {
it('too short', () => {
req.swagger.params.body.value.ClientName = 'a@b.co';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('too long', () => {
req.swagger.params.body.value.ClientName =
'a@' +
'b'.repeat(249) +
'.com';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('invalid char', () => {
req.swagger.params.body.value.ClientName = 'a@Bücher.example'; // No IDN support
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('missing the @', () => {
req.swagger.params.body.value.ClientName = 'example.com';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
it('missing the tld', () => {
req.swagger.params.body.value.ClientName = 'aexample';
return expect(hmacNoSessionP(req, def, SESSION_HEADER)).to
.eventually.be.rejected;
});
});
});
});
});