akamai-edgegrid
Version:
Authentication handler for the Akamai OPEN EdgeGrid Authentication scheme in Node.js
639 lines (539 loc) • 26.3 kB
JavaScript
const assert = require('assert'),
nock = require('nock'),
path = require('path'),
Api = require('../../src/api');
const EdgeGrid = require("../../index");
describe('Api', function () {
beforeEach(function () {
this.api = new Api(
'clientToken',
'clientSecret',
'accessToken',
'base.com',
false
);
});
// clear env variables which might be set in tests
beforeEach(function () {
process.env['AKAMAI_HOST'] = '';
process.env['AKAMAI_CLIENT_TOKEN'] = '';
process.env['AKAMAI_CLIENT_SECRET'] = '';
process.env['AKAMAI_ACCESS_TOKEN'] = '';
});
describe('.config', function () {
it('reports the client token', function () {
assert.strictEqual(this.api.config.client_token, 'clientToken');
});
it('reports the client secret', function () {
assert.strictEqual(this.api.config.client_secret, 'clientSecret');
});
it('reports the access token', function () {
assert.strictEqual(this.api.config.access_token, 'accessToken');
});
it('reports the API host', function () {
assert.strictEqual(this.api.config.host, 'https://base.com');
});
describe('when it is instantiated with an API host that already contains the protocol', function () {
it('it does not double declare the protocol', function () {
this.api = new Api(
'clientToken',
'clientSecret',
'accessToken',
'https://base.com'
);
assert.strictEqual(this.api.config.host, 'https://base.com');
});
});
describe('when it is instantiated with an object', function () {
beforeEach(function () {
this.api = new Api({
path: path.resolve(__dirname, '../test_edgerc'),
section: 'section'
});
});
it('reports the client token from the edgerc associated with the specified section', function () {
assert.strictEqual(this.api.config.client_token, 'sectionClientToken');
});
it('reports the client secret from the edgerc associated with the specified section', function () {
assert.strictEqual(this.api.config.client_secret, 'sectionClientSecret');
});
it('reports the access token from the edgerc associated with the specified section', function () {
assert.strictEqual(this.api.config.access_token, 'sectionAccessToken');
});
it('reports the API host from the edgerc associated with the specified section', function () {
assert.strictEqual(this.api.config.host, 'https://sectionexample.luna.akamaiapis.net');
});
it('reports the max-body from the edgerc associated with the specified section', function() {
assert.equal(this.api.config.max_body, 131072);
});
describe('when it is instantiated with an object with custom `max_body` value', function () {
beforeEach(function () {
this.api = new Api({
path: path.resolve(__dirname, '../test_edgerc'),
section: 'custom:max_body'
});
});
it('reports the client token from the edgerc associated with the specified section with custom `max_body`', function () {
assert.strictEqual(this.api.config.client_token, 'sectionClientToken');
});
it('reports the client secret from the edgerc associated with the specified section with custom `max_body`', function () {
assert.strictEqual(this.api.config.client_secret, 'sectionClientSecret');
});
it('reports the access token from the edgerc associated with the specified section with custom `max_body`', function () {
assert.strictEqual(this.api.config.access_token, 'sectionAccessToken');
});
it('reports the API host from the edgerc associated with the specified section with custom `max_body`', function () {
assert.strictEqual(this.api.config.host, 'https://sectionexample.luna.akamaiapis.net');
});
it('reports the max-body from the edgerc associated with the specified section with custom `max_body`', function () {
assert.equal(this.api.config.max_body, 131072);
});
});
describe('when it is instantiated with an object without specified `max_body` value', function () {
beforeEach(function () {
this.api = new Api({
path: path.resolve(__dirname, '../test_edgerc'),
section: 'no-max-body'
});
});
it('reports the client token from the edgerc associated with the specified section without specified `max_body`', function () {
assert.strictEqual(this.api.config.client_token, 'sectionClientToken');
});
it('reports the client secret from the edgerc associated with the specified section without specified `max_body`', function () {
assert.strictEqual(this.api.config.client_secret, 'sectionClientSecret');
});
it('reports the access token from the edgerc associated with the specified section without specified `max_body`', function () {
assert.strictEqual(this.api.config.access_token, 'sectionAccessToken');
});
it('reports the API host from the edgerc associated with the specified section without specified `max_body`', function () {
assert.strictEqual(this.api.config.host, 'https://sectionexample.luna.akamaiapis.net');
});
it('reports the max-body from the edgerc associated with the specified section without specified `max_body`', function () {
assert.equal(this.api.config.max_body, 131072);
});
});
describe('when it is instantiated with an object that does not specify a section', function () {
beforeEach(function () {
this.api = new Api({
path: path.resolve(__dirname, '../test_edgerc')
});
});
it('reports the client token from the edgerc associated with the default section', function () {
assert.strictEqual(this.api.config.client_token, 'clientToken');
});
it('reports the client secret from the edgerc associated with the default section', function () {
assert.strictEqual(this.api.config.client_secret, 'clientSecret');
});
it('reports the access token from the edgerc associated with the default section', function () {
assert.strictEqual(this.api.config.access_token, 'accessToken');
});
it('reports the API host from the edgerc associated with the default section', function () {
assert.strictEqual(this.api.config.host, 'https://example.luna.akamaiapis.net');
});
it('reports the max-body from the edgerc associated with the default section', function() {
assert.equal(this.api.config.max_body, 131072);
});
});
describe('when it is instantiated with an object that does not specify a path nor a section', function () {
beforeEach(function () {
process.env['AKAMAI_HOST'] = 'https://example.luna.akamaiapis.net';
process.env['AKAMAI_CLIENT_TOKEN'] = 'clientToken';
process.env['AKAMAI_CLIENT_SECRET'] = 'clientSecret';
process.env['AKAMAI_ACCESS_TOKEN'] = 'accessToken';
this.api = new Api({});
});
it('uses config from env variables with default section', function () {
assert.strictEqual(this.api.config.host, "https://example.luna.akamaiapis.net");
assert.strictEqual(this.api.config.client_token, "clientToken");
assert.strictEqual(this.api.config.client_secret, "clientSecret");
assert.strictEqual(this.api.config.access_token, "accessToken");
assert.strictEqual(this.api.config.max_body, 131072);
});
});
describe('when it is instantiated with an object that specifies an inadequate path', function () {
it('throws the appropriate error', function () {
assert.throws(
function () {
return new Api({
path: ''
});
},
/Either path to '.edgerc' or environment variables with edgerc configuration has to be provided./
);
});
});
});
});
describe('when it is not instantiated with valid credentials', function () {
it('throws the appropriate error', function () {
assert.throws(
function () {
return new Api();
},
/Insufficient Akamai credentials/
);
});
});
describe('#auth', function () {
it('should be chainable', function () {
assert.deepStrictEqual(this.api, this.api.auth({path: '/foo'}));
});
describe('when minimal request options are passed', function () {
beforeEach(function () {
this.api.auth({
path: '/foo'
});
});
it('adds an Authorization header to the request it is passed', function () {
assert.strictEqual(typeof this.api.request.headers.Authorization === 'string', true);
});
it('ensures a default Content-Type of application/json', function () {
assert.strictEqual(this.api.request.headers['Content-Type'], 'application/json');
});
it('ensures a default Accept of application/json', function () {
assert.strictEqual(this.api.request.headers['Accept'], 'application/json');
});
it('ensures a default GET method', function () {
assert.strictEqual(this.api.request.method, 'GET');
});
it('ensures a default undefined body', function () {
assert.strictEqual(this.api.request.body, undefined);
});
it('ensures a url is properly declared', function () {
assert.strictEqual(this.api.request.url, 'https://base.com/foo');
});
it('ensures no User-Agent is added when AkamaiCLI env variables not set', function () {
assert.ok(!this.api.request.headers.hasOwnProperty('User-Agent'));
});
});
describe('when more specific request options are passed', function () {
beforeEach(function () {
this.api.auth({
path: '/foo',
method: 'POST',
body: {
foo: 'bar'
},
somethingArbitrary: 'someValue',
headers: {
'User-Agent': 'testUserAgent',
'Accept': 'text/html'
}
});
});
it('adds an Authorization header to the request it is passed', function () {
assert.strictEqual(typeof this.api.request.headers.Authorization === 'string', true);
});
it('ensures a default Content-Type of application/json', function () {
assert.strictEqual(this.api.request.headers['Content-Type'], 'application/json');
});
it('uses the specified POST method', function () {
assert.strictEqual(this.api.request.method, 'POST');
});
it('uses the specified body parsed as a JSON string', function () {
console.log("BODY: ", this.api.request.body);
assert.strictEqual(this.api.request.body, '{"foo":"bar"}');
});
it('extends the default request options with any others specified', function () {
assert.strictEqual(this.api.request.somethingArbitrary, 'someValue');
});
it('ensures provided User-Agent header is preserved', function () {
assert.strictEqual(this.api.request.headers['User-Agent'], 'testUserAgent');
});
it('ensures provided Accept header is preserved', function () {
assert.strictEqual(this.api.request.headers['Accept'], 'text/html');
});
});
describe("when gzip response format is expected", function () {
beforeEach(function () {
this.api.auth({
path: '/foo',
body: 'someBody',
headers: {
'Accept': `application/gzip`,
'Content-Type': `application/gzip`
}
});
});
it('adds an Authorization header to the request it is passed', function () {
assert.strictEqual(typeof this.api.request.headers.Authorization === 'string', true);
});
it('ensures a default GET method', function () {
assert.strictEqual(this.api.request.method, 'GET');
});
it('ensures the specified body is not modified', function () {
assert.strictEqual(this.api.request.body, 'someBody');
});
it('should return response as buffer', function () {
assert.strictEqual(this.api.request["responseType"], "arraybuffer");
});
});
describe("when tar+gzip response format is expected", function () {
beforeEach(function () {
this.api.auth({
path: '/foo',
body: 'someBody',
headers: {
'Accept': `application/tar+gzip`,
'Content-Type': `application/tar+gzip`
}
});
});
it('adds an Authorization header to the request it is passed', function () {
assert.strictEqual(typeof this.api.request.headers.Authorization === 'string', true);
});
it('ensures a default GET method', function () {
assert.strictEqual(this.api.request.method, 'GET');
});
it('ensures the specified body is not modified', function () {
assert.strictEqual(this.api.request.body, 'someBody');
});
it('should return response as buffer', function () {
assert.strictEqual(this.api.request["responseType"], "arraybuffer");
});
});
describe("when akamai cli user agent is expected", function () {
beforeEach(function () {
process.env['AKAMAI_CLI'] = 'AkamaiCLI';
process.env['AKAMAI_CLI_VERSION'] = '1.0.0';
process.env['AKAMAI_CLI_COMMAND'] = 'command';
process.env['AKAMAI_CLI_COMMAND_VERSION'] = '0.0.1';
});
afterEach(function () {
process.env['AKAMAI_CLI'] = '';
process.env['AKAMAI_CLI_VERSION'] = '';
process.env['AKAMAI_CLI_COMMAND'] = '';
process.env['AKAMAI_CLI_COMMAND_VERSION'] = '';
});
describe("when no User-Agent set in the request", function () {
beforeEach(function () {
this.api.auth({
path: '/foo'
});
});
it("should set User-Agent", function () {
assert.strictEqual(this.api.request.headers['User-Agent'], 'AkamaiCLI/1.0.0 AkamaiCLI-command/0.0.1');
});
});
describe("when User-Agent is already in the request", function () {
beforeEach(function () {
this.api.auth({
path: '/foo',
headers: {
'User-Agent': 'testAgent'
}
});
});
it("should append to already present User-Agent header", function () {
assert.strictEqual(this.api.request.headers['User-Agent'], 'testAgent AkamaiCLI/1.0.0 AkamaiCLI-command/0.0.1');
});
});
describe("when only AkamaiCLI info is set", function () {
beforeEach(function () {
process.env['AKAMAI_CLI_COMMAND'] = '';
process.env['AKAMAI_CLI_COMMAND_VERSION'] = '';
this.api.auth({
path: '/foo'
});
});
it("should only set AkamaiCLI/version User-Agent", function () {
assert.strictEqual(this.api.request.headers['User-Agent'], 'AkamaiCLI/1.0.0');
});
});
describe("when only AkamaiCLI command info is set", function () {
beforeEach(function () {
process.env['AKAMAI_CLI'] = '';
process.env['AKAMAI_CLI_VERSION'] = '';
this.api.auth({
path: '/foo'
});
});
it("should only set AkamaiCLI/version User-Agent", function () {
assert.strictEqual(this.api.request.headers['User-Agent'], 'AkamaiCLI-command/0.0.1');
});
});
});
});
describe('#send', function () {
it('should be chainable', function () {
assert.deepStrictEqual(this.api, this.api.auth({path: '/foo'}).send());
});
describe('when authentication is done with a simple options object specifying only a path', function () {
beforeEach(function () {
nock('https://base.com')
.get('/foo')
.reply(200, {
foo: 'bar'
});
});
it('sends the HTTP GET request created by #auth', function (done) {
this.api.auth({
path: '/foo'
});
this.api.send(function (err, resp, body) {
assert.strictEqual(JSON.parse(body).foo, 'bar');
done();
});
});
});
describe('when authentication is done with a more complex options object specifying only a path', function () {
beforeEach(function () {
nock('https://base.com')
.post('/foo')
.reply(200, {
foo: 'bar'
});
});
it('sends the HTTP created by #auth', function (done) {
this.api.auth({
path: '/foo',
method: 'POST'
});
this.api.send(function (err, resp, body) {
assert.strictEqual(JSON.parse(body).foo, 'bar');
done();
});
});
});
describe('when the initial request redirects', function () {
it('correctly follows the redirect and re-signs the request', function (done) {
let authHeader;
nock('https://base.com')
.get('/foo')
.reply(function () {
authHeader = this.req.headers["authorization"];
return [
302,
'',
{'location': 'https://base.com/bar'}
];
})
.get('/bar')
.reply(function () {
assert.notStrictEqual(this.req.headers["authorization"], authHeader);
return [
200,
{someKey: 'value'}
];
});
this.api.auth({
path: '/foo',
});
this.api.send(function (err, resp, body) {
assert.strictEqual(JSON.parse(body).someKey, 'value');
done();
});
});
});
describe('when the initial request fails', function () {
it('correctly handles the error in the callback', function (done) {
nock('https://base.com')
.get('/foo')
.replyWithError('something awful happened');
this.api.auth({
path: '/foo',
});
this.api.send(function (data) {
assert.strictEqual(data.message, 'something awful happened');
done();
});
});
});
describe('Builds the request using the properties of the local config Object (.edgerc file)', () => {
it('when max_body is provided in the config', () => {
const req = {
path: '/api/resource',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: {key: 'value'}
};
const edgercObject = {
path: path.resolve(__dirname, '../test_edgerc'),
section: 'custom:max_body'
};
const edgeGrid = new EdgeGrid(edgercObject);
// Call auth method
const response = edgeGrid.auth(req);
// Assertions
assert.strictEqual(response.config.max_body, 131072);
});
it('when max_body is provided in the config - NAN', () => {
const req = {
path: '/api/resource',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: {key: 'value'}
};
const edgercObject = {
path: path.resolve(__dirname, '../test_edgerc'),
section: 'custom:max_body_NAN'
};
const edgeGrid = new EdgeGrid(edgercObject);
// Call auth method
const response = edgeGrid.auth(req);
// Assertions
assert.strictEqual(response.config.max_body, 131072);
});
it('when max_body is not provided in the configuration, the default value is used', () => {
const req = {
path: '/api/resource',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: {key: 'value'}
};
const edgercObject = {
path: path.resolve(__dirname, '../test_edgerc'),
section: 'no-max-body'
};
const edgeGrid = new EdgeGrid(edgercObject, undefined, undefined, undefined, undefined, 1000);
// Call auth method
const response = edgeGrid.auth(req);
// Assertions
assert.strictEqual(response.config.max_body, 131072); // picks default max_body 131072
});
});
describe('Builds the request using the config as string parameters', () => {
it('when max_body is provided in the config', () => {
const req = {
path: '/api/resource',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: {key: 'value'}
};
const edgeGrid = new EdgeGrid('clientToken', 'clientSecret', 'accessToken', 'example.com', false, 8192);
// Call auth method
const response = edgeGrid.auth(req);
// Assertions
assert.strictEqual(response.config.max_body, 131072);
});
it('when max_body is not provided in the config', () => {
const req = {
path: '/api/resource',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: {key: 'value'}
};
const edgeGrid = new EdgeGrid('clientToken', 'clientSecret', 'accessToken', 'example.com');
// Call auth method
const response = edgeGrid.auth(req);
// Assertions
assert.strictEqual(response.config.max_body, 131072);
});
});
});
});