Move api-keys module to src/lib

This removes circular dependencies between the device-api module and
the compose module, reducing total circular dependencies to 15

Change-type: patch
This commit is contained in:
Felipe Lalanne 2024-05-15 15:39:22 -04:00
parent 48f75e61b8
commit ac2db38742
16 changed files with 171 additions and 178 deletions

View File

@ -20,7 +20,7 @@ import type {
import log from '../lib/supervisor-console'; import log from '../lib/supervisor-console';
import * as deviceApi from '../device-api'; import * as apiKeys from '../lib/api-keys';
export function camelCaseConfig( export function camelCaseConfig(
literalConfig: ConfigMap, literalConfig: ConfigMap,
@ -369,7 +369,7 @@ export async function addFeaturesFromLabels(
}, },
'io.balena.features.supervisor-api': async () => { 'io.balena.features.supervisor-api': async () => {
// create a app/service specific API secret // create a app/service specific API secret
const apiSecret = await deviceApi.generateScopedKey( const apiSecret = await apiKeys.generateScopedKey(
service.appId, service.appId,
service.serviceName, service.serviceName,
); );

View File

@ -1,6 +1,6 @@
import _ from 'lodash'; import _ from 'lodash';
import { getGlobalApiKey, refreshKey } from '.'; import { getGlobalApiKey, refreshKey } from '../lib/api-keys';
import * as messages from './messages'; import * as messages from './messages';
import * as eventTracker from '../event-tracker'; import * as eventTracker from '../event-tracker';
import * as deviceState from '../device-state'; import * as deviceState from '../device-state';

View File

@ -1,7 +1,7 @@
import express from 'express'; import express from 'express';
import * as middleware from './middleware'; import * as middleware from './middleware';
import * as apiKeys from './api-keys'; import type * as apiKeys from '../lib/api-keys';
import * as actions from './actions'; import * as actions from './actions';
import log from '../lib/supervisor-console'; import log from '../lib/supervisor-console';
@ -15,10 +15,6 @@ interface SupervisorAPIConstructOpts {
// API key methods // API key methods
// For better black boxing, device-api should serve as the interface // For better black boxing, device-api should serve as the interface
// to the rest of the Supervisor code for accessing API key related methods. // to the rest of the Supervisor code for accessing API key related methods.
export const getGlobalApiKey = apiKeys.getGlobalApiKey;
export const refreshKey = apiKeys.refreshKey;
export const generateScopedKey = apiKeys.generateScopedKey;
export const getScopesForKey = apiKeys.getScopesForKey;
export class SupervisorAPI { export class SupervisorAPI {
private routers: express.Router[]; private routers: express.Router[];

View File

@ -1,4 +1,4 @@
import * as apiKeys from '../api-keys'; import * as apiKeys from '../../lib/api-keys';
import * as config from '../../config'; import * as config from '../../config';
import type { Request } from 'express'; import type { Request } from 'express';

View File

@ -2,7 +2,7 @@ import express from 'express';
import type { Response } from 'express'; import type { Response } from 'express';
import * as actions from './actions'; import * as actions from './actions';
import type { AuthorizedRequest } from './api-keys'; import type { AuthorizedRequest } from '../lib/api-keys';
import * as eventTracker from '../event-tracker'; import * as eventTracker from '../event-tracker';
import type * as deviceState from '../device-state'; import type * as deviceState from '../device-state';

View File

@ -26,7 +26,7 @@ import {
BadRequestError, BadRequestError,
} from '../lib/errors'; } from '../lib/errors';
import { isVPNActive } from '../network'; import { isVPNActive } from '../network';
import type { AuthorizedRequest } from './api-keys'; import type { AuthorizedRequest } from '../lib/api-keys';
import { fromV2TargetState } from '../lib/legacy'; import { fromV2TargetState } from '../lib/legacy';
import * as actions from './actions'; import * as actions from './actions';
import { v2ServiceEndpointError } from './messages'; import { v2ServiceEndpointError } from './messages';

View File

@ -25,7 +25,7 @@ import {
import * as updateLock from './lib/update-lock'; import * as updateLock from './lib/update-lock';
import { takeGlobalLockRO, takeGlobalLockRW } from './lib/process-lock'; import { takeGlobalLockRO, takeGlobalLockRW } from './lib/process-lock';
import * as dbFormat from './device-state/db-format'; import * as dbFormat from './device-state/db-format';
import { getGlobalApiKey } from './device-api'; import { getGlobalApiKey } from './lib/api-keys';
import * as sysInfo from './lib/system-info'; import * as sysInfo from './lib/system-info';
import { log } from './lib/supervisor-console'; import { log } from './lib/supervisor-console';
import { loadTargetFromFile } from './device-state/preload'; import { loadTargetFromFile } from './device-state/preload';

View File

@ -1,7 +1,7 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { Service } from '~/src/compose/service'; import { Service } from '~/src/compose/service';
import * as deviceApi from '~/src/device-api'; import * as apiKeys from '~/lib/api-keys';
describe('compose/service: integration tests', () => { describe('compose/service: integration tests', () => {
describe('Feature labels', () => { describe('Feature labels', () => {
@ -41,7 +41,7 @@ describe('compose/service: integration tests', () => {
}); });
it('sets BALENA_API_KEY env var to the scoped API key value', async () => { it('sets BALENA_API_KEY env var to the scoped API key value', async () => {
const mykey = await deviceApi.generateScopedKey(123456, 'foobar'); const mykey = await apiKeys.generateScopedKey(123456, 'foobar');
const service = await Service.fromComposeObject( const service = await Service.fromComposeObject(
{ {

View File

@ -9,7 +9,7 @@ import { testfs } from 'mocha-pod';
import * as deviceState from '~/src/device-state'; import * as deviceState from '~/src/device-state';
import * as config from '~/src/config'; import * as config from '~/src/config';
import * as hostConfig from '~/src/host-config'; import * as hostConfig from '~/src/host-config';
import * as deviceApi from '~/src/device-api'; import * as apiKeys from '~/lib/api-keys';
import * as actions from '~/src/device-api/actions'; import * as actions from '~/src/device-api/actions';
import * as TargetState from '~/src/device-state/target-state'; import * as TargetState from '~/src/device-state/target-state';
import * as updateLock from '~/lib/update-lock'; import * as updateLock from '~/lib/update-lock';
@ -56,10 +56,10 @@ describe('regenerates API keys', () => {
afterEach(() => (deviceState.reportCurrentState as SinonStub).restore()); afterEach(() => (deviceState.reportCurrentState as SinonStub).restore());
it("communicates new key to cloud if it's a global key", async () => { it("communicates new key to cloud if it's a global key", async () => {
const originalGlobalKey = await deviceApi.getGlobalApiKey(); const originalGlobalKey = await apiKeys.getGlobalApiKey();
const newKey = await actions.regenerateKey(originalGlobalKey); const newKey = await actions.regenerateKey(originalGlobalKey);
expect(originalGlobalKey).to.not.equal(newKey); expect(originalGlobalKey).to.not.equal(newKey);
expect(newKey).to.equal(await deviceApi.getGlobalApiKey()); expect(newKey).to.equal(await apiKeys.getGlobalApiKey());
expect(deviceState.reportCurrentState as SinonStub).to.have.been.calledOnce; expect(deviceState.reportCurrentState as SinonStub).to.have.been.calledOnce;
expect( expect(
(deviceState.reportCurrentState as SinonStub).firstCall.args[0], (deviceState.reportCurrentState as SinonStub).firstCall.args[0],
@ -69,10 +69,10 @@ describe('regenerates API keys', () => {
}); });
it("doesn't communicate new key if it's a service key", async () => { it("doesn't communicate new key if it's a service key", async () => {
const originalScopedKey = await deviceApi.generateScopedKey(111, 'main'); const originalScopedKey = await apiKeys.generateScopedKey(111, 'main');
const newKey = await actions.regenerateKey(originalScopedKey); const newKey = await actions.regenerateKey(originalScopedKey);
expect(originalScopedKey).to.not.equal(newKey); expect(originalScopedKey).to.not.equal(newKey);
expect(newKey).to.not.equal(await deviceApi.getGlobalApiKey()); expect(newKey).to.not.equal(await apiKeys.getGlobalApiKey());
expect(deviceState.reportCurrentState as SinonStub).to.not.have.been.called; expect(deviceState.reportCurrentState as SinonStub).to.not.have.been.called;
}); });
}); });

View File

@ -1,6 +1,7 @@
import type * as express from 'express'; import type * as express from 'express';
import request from 'supertest'; import request from 'supertest';
import * as apiKeys from '~/lib/api-keys';
import * as deviceApi from '~/src/device-api'; import * as deviceApi from '~/src/device-api';
describe('device-api/index', () => { describe('device-api/index', () => {
@ -22,7 +23,7 @@ describe('device-api/index', () => {
await request(api) await request(api)
.get('/ping') .get('/ping')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
}); });

View File

@ -3,7 +3,7 @@ import request from 'supertest';
import * as config from '~/src/config'; import * as config from '~/src/config';
import * as testDb from '~/src/db'; import * as testDb from '~/src/db';
import * as deviceApi from '~/src/device-api'; import * as apiKeys from '~/lib/api-keys';
import * as middleware from '~/src/device-api/middleware'; import * as middleware from '~/src/device-api/middleware';
describe('device-api/middleware', () => { describe('device-api/middleware', () => {
@ -35,7 +35,7 @@ describe('device-api/middleware', () => {
it('validates API key from request query', async () => { it('validates API key from request query', async () => {
await request(app) await request(app)
.get(`/?apikey=${await deviceApi.getGlobalApiKey()}`) .get(`/?apikey=${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
await request(app).get(`/?apikey=${INVALID_KEY}`).expect(401); await request(app).get(`/?apikey=${INVALID_KEY}`).expect(401);
@ -44,7 +44,7 @@ describe('device-api/middleware', () => {
const cases = ['ApiKey', 'apiKey', 'APIKEY', 'ApIKeY']; const cases = ['ApiKey', 'apiKey', 'APIKEY', 'ApIKeY'];
for (const query of cases) { for (const query of cases) {
await request(app) await request(app)
.get(`/?${query}=${await deviceApi.getGlobalApiKey()}`) .get(`/?${query}=${await apiKeys.getGlobalApiKey()}`)
.expect(401); .expect(401);
} }
}); });
@ -55,10 +55,7 @@ describe('device-api/middleware', () => {
for (const scheme of cases) { for (const scheme of cases) {
await request(app) await request(app)
.get('/') .get('/')
.set( .set('Authorization', `${scheme} ${await apiKeys.getGlobalApiKey()}`)
'Authorization',
`${scheme} ${await deviceApi.getGlobalApiKey()}`,
)
.expect(200); .expect(200);
await request(app) await request(app)
@ -74,10 +71,7 @@ describe('device-api/middleware', () => {
for (const scheme of cases) { for (const scheme of cases) {
await request(app) await request(app)
.get('/') .get('/')
.set( .set('Authorization', `${scheme} ${await apiKeys.getGlobalApiKey()}`)
'Authorization',
`${scheme} ${await deviceApi.getGlobalApiKey()}`,
)
.expect(200); .expect(200);
await request(app) await request(app)

View File

@ -8,6 +8,7 @@ import * as config from '~/src/config';
import * as db from '~/src/db'; import * as db from '~/src/db';
import * as hostConfig from '~/src/host-config'; import * as hostConfig from '~/src/host-config';
import type { Service } from '~/src/compose/service'; import type { Service } from '~/src/compose/service';
import * as apiKeys from '~/lib/api-keys';
import * as deviceApi from '~/src/device-api'; import * as deviceApi from '~/src/device-api';
import * as actions from '~/src/device-api/actions'; import * as actions from '~/src/device-api/actions';
import * as v1 from '~/src/device-api/v1'; import * as v1 from '~/src/device-api/v1';
@ -73,7 +74,7 @@ describe('device-api/v1', () => {
it('responds with 200', async () => { it('responds with 200', async () => {
await request(api) await request(api)
.post('/v1/blink') .post('/v1/blink')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
}); });
@ -84,7 +85,7 @@ describe('device-api/v1', () => {
afterEach(() => (actions.regenerateKey as SinonStub).restore()); afterEach(() => (actions.regenerateKey as SinonStub).restore());
it('responds with 200 and valid new API key', async () => { it('responds with 200 and valid new API key', async () => {
const oldKey = await deviceApi.getGlobalApiKey(); const oldKey = await apiKeys.getGlobalApiKey();
const newKey = 'my_new_key'; const newKey = 'my_new_key';
(actions.regenerateKey as SinonStub).resolves(newKey); (actions.regenerateKey as SinonStub).resolves(newKey);
@ -98,7 +99,7 @@ describe('device-api/v1', () => {
}); });
it('responds with 503 if regenerate was unsuccessful', async () => { it('responds with 503 if regenerate was unsuccessful', async () => {
const oldKey = await deviceApi.getGlobalApiKey(); const oldKey = await apiKeys.getGlobalApiKey();
(actions.regenerateKey as SinonStub).throws(new Error()); (actions.regenerateKey as SinonStub).throws(new Error());
await request(api) await request(api)
@ -125,7 +126,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 1234567, force: false }) .send({ appId: 1234567, force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(1234567, false); expect(doRestartStub).to.have.been.calledWith(1234567, false);
doRestartStub.resetHistory(); doRestartStub.resetHistory();
@ -134,7 +135,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 7654321, force: true }) .send({ appId: 7654321, force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(7654321, true); expect(doRestartStub).to.have.been.calledWith(7654321, true);
doRestartStub.resetHistory(); doRestartStub.resetHistory();
@ -143,7 +144,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 7654321 }) .send({ appId: 7654321 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(7654321, false); expect(doRestartStub).to.have.been.calledWith(7654321, false);
}); });
@ -151,12 +152,12 @@ describe('device-api/v1', () => {
it('responds with 400 if appId is missing', async () => { it('responds with 400 if appId is missing', async () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 7654321 }) .send({ appId: 7654321 })
@ -168,7 +169,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -177,7 +178,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -186,7 +187,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/restart') .post('/v1/restart')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -207,7 +208,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 1234567, force: false }) .send({ appId: 1234567, force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(1234567, false); expect(doPurgeStub).to.have.been.calledWith(1234567, false);
doPurgeStub.resetHistory(); doPurgeStub.resetHistory();
@ -216,7 +217,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 7654321, force: true }) .send({ appId: 7654321, force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(7654321, true); expect(doPurgeStub).to.have.been.calledWith(7654321, true);
doPurgeStub.resetHistory(); doPurgeStub.resetHistory();
@ -225,7 +226,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 7654321 }) .send({ appId: 7654321 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(7654321, false); expect(doPurgeStub).to.have.been.calledWith(7654321, false);
}); });
@ -233,12 +234,12 @@ describe('device-api/v1', () => {
it('responds with 400 if appId is missing', async () => { it('responds with 400 if appId is missing', async () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 7654321 }) .send({ appId: 7654321 })
@ -250,7 +251,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -259,7 +260,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -268,7 +269,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/purge') .post('/v1/purge')
.send({ appId: 1234567 }) .send({ appId: 1234567 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -297,7 +298,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -311,7 +312,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/apps/7654321/stop') .post('/v1/apps/7654321/stop')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -324,7 +325,7 @@ describe('device-api/v1', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v1/apps/7654321/stop') .post('/v1/apps/7654321/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -335,7 +336,7 @@ describe('device-api/v1', () => {
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v1/apps/7654321/stop') .post('/v1/apps/7654321/stop')
.set('Authorization', `Bearer ${scopedKey}`) .set('Authorization', `Bearer ${scopedKey}`)
@ -345,7 +346,7 @@ describe('device-api/v1', () => {
it('responds with 200 and containerId if service stop succeeded if service stop succeeded', async () => { it('responds with 200 and containerId if service stop succeeded if service stop succeeded', async () => {
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, { containerId: 'abcdef' }); .expect(200, { containerId: 'abcdef' });
}); });
@ -353,20 +354,20 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new NotFoundError()); executeServiceActionStub.throws(new NotFoundError());
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(404); .expect(404);
}); });
it('responds with 400 if invalid appId or appId corresponds to a multicontainer release', async () => { it('responds with 400 if invalid appId or appId corresponds to a multicontainer release', async () => {
await request(api) await request(api)
.post('/v1/apps/badAppId/stop') .post('/v1/apps/badAppId/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
executeServiceActionStub.throws(new BadRequestError()); executeServiceActionStub.throws(new BadRequestError());
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -374,7 +375,7 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new UpdatesLockedError()); executeServiceActionStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -382,7 +383,7 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new Error()); executeServiceActionStub.throws(new Error());
await request(api) await request(api)
.post('/v1/apps/1234567/stop') .post('/v1/apps/1234567/stop')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -411,7 +412,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -425,7 +426,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/apps/7654321/start') .post('/v1/apps/7654321/start')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -438,7 +439,7 @@ describe('device-api/v1', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v1/apps/7654321/start') .post('/v1/apps/7654321/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -449,7 +450,7 @@ describe('device-api/v1', () => {
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v1/apps/7654321/start') .post('/v1/apps/7654321/start')
.set('Authorization', `Bearer ${scopedKey}`) .set('Authorization', `Bearer ${scopedKey}`)
@ -459,7 +460,7 @@ describe('device-api/v1', () => {
it('responds with 200 and containerId if service start succeeded', async () => { it('responds with 200 and containerId if service start succeeded', async () => {
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, { containerId: 'abcdef' }); .expect(200, { containerId: 'abcdef' });
}); });
@ -467,20 +468,20 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new NotFoundError()); executeServiceActionStub.throws(new NotFoundError());
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(404); .expect(404);
}); });
it('responds with 400 if invalid appId or appId corresponds to a multicontainer release', async () => { it('responds with 400 if invalid appId or appId corresponds to a multicontainer release', async () => {
await request(api) await request(api)
.post('/v1/apps/badAppId/start') .post('/v1/apps/badAppId/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
executeServiceActionStub.throws(new BadRequestError()); executeServiceActionStub.throws(new BadRequestError());
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -488,7 +489,7 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new UpdatesLockedError()); executeServiceActionStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -496,7 +497,7 @@ describe('device-api/v1', () => {
executeServiceActionStub.throws(new Error()); executeServiceActionStub.throws(new Error());
await request(api) await request(api)
.post('/v1/apps/1234567/start') .post('/v1/apps/1234567/start')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -513,7 +514,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'reboot', action: 'reboot',
@ -526,7 +527,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'reboot', action: 'reboot',
@ -538,7 +539,7 @@ describe('device-api/v1', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'reboot', action: 'reboot',
@ -550,7 +551,7 @@ describe('device-api/v1', () => {
it('responds with 202 if request successful', async () => { it('responds with 202 if request successful', async () => {
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(202); .expect(202);
}); });
@ -558,7 +559,7 @@ describe('device-api/v1', () => {
executeDeviceActionStub.throws(new UpdatesLockedError()); executeDeviceActionStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -566,7 +567,7 @@ describe('device-api/v1', () => {
executeDeviceActionStub.throws(new Error()); executeDeviceActionStub.throws(new Error());
await request(api) await request(api)
.post('/v1/reboot') .post('/v1/reboot')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(500); .expect(500);
}); });
}); });
@ -583,7 +584,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'shutdown', action: 'shutdown',
@ -596,7 +597,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'shutdown', action: 'shutdown',
@ -608,7 +609,7 @@ describe('device-api/v1', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(executeDeviceActionStub).to.have.been.calledWith( expect(executeDeviceActionStub).to.have.been.calledWith(
{ {
action: 'shutdown', action: 'shutdown',
@ -620,7 +621,7 @@ describe('device-api/v1', () => {
it('responds with 202 if request successful', async () => { it('responds with 202 if request successful', async () => {
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(202); .expect(202);
}); });
@ -628,7 +629,7 @@ describe('device-api/v1', () => {
executeDeviceActionStub.throws(new UpdatesLockedError()); executeDeviceActionStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -636,7 +637,7 @@ describe('device-api/v1', () => {
executeDeviceActionStub.throws(new Error()); executeDeviceActionStub.throws(new Error());
await request(api) await request(api)
.post('/v1/shutdown') .post('/v1/shutdown')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(500); .expect(500);
}); });
}); });
@ -653,7 +654,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/update') .post('/v1/update')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(updateTargetStub.lastCall.firstArg).to.be.false; expect(updateTargetStub.lastCall.firstArg).to.be.false;
updateTargetStub.resetHistory(); updateTargetStub.resetHistory();
@ -661,14 +662,14 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.post('/v1/update') .post('/v1/update')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(updateTargetStub.lastCall.firstArg).to.be.true; expect(updateTargetStub.lastCall.firstArg).to.be.true;
updateTargetStub.resetHistory(); updateTargetStub.resetHistory();
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v1/update') .post('/v1/update')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(updateTargetStub.lastCall.firstArg).to.be.false; expect(updateTargetStub.lastCall.firstArg).to.be.false;
}); });
@ -676,7 +677,7 @@ describe('device-api/v1', () => {
updateTargetStub.returns(true); updateTargetStub.returns(true);
await request(api) await request(api)
.post('/v1/update') .post('/v1/update')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(204); .expect(204);
}); });
@ -684,7 +685,7 @@ describe('device-api/v1', () => {
updateTargetStub.returns(false); updateTargetStub.returns(false);
await request(api) await request(api)
.post('/v1/update') .post('/v1/update')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(202); .expect(202);
}); });
}); });
@ -706,21 +707,21 @@ describe('device-api/v1', () => {
it('validates data from request body', async () => { it('validates data from request body', async () => {
await request(api) await request(api)
.get('/v1/apps/1234567') .get('/v1/apps/1234567')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`); .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`);
expect(getSingleContainerAppStub).to.have.been.calledWith(1234567); expect(getSingleContainerAppStub).to.have.been.calledWith(1234567);
}); });
it('responds with 200 if request successful', async () => { it('responds with 200 if request successful', async () => {
await request(api) await request(api)
.get('/v1/apps/1234567') .get('/v1/apps/1234567')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, {}); .expect(200, {});
}); });
it('responds with 400 if invalid appId parameter', async () => { it('responds with 400 if invalid appId parameter', async () => {
await request(api) await request(api)
.get('/v1/apps/badAppId') .get('/v1/apps/badAppId')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -728,12 +729,12 @@ describe('device-api/v1', () => {
getSingleContainerAppStub.throws(new BadRequestError()); getSingleContainerAppStub.throws(new BadRequestError());
await request(api) await request(api)
.get('/v1/apps/1234567') .get('/v1/apps/1234567')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(7654321, 'main'); const scopedKey = await apiKeys.generateScopedKey(7654321, 'main');
await request(api) await request(api)
.get('/v1/apps/1234567') .get('/v1/apps/1234567')
.set('Authorization', `Bearer ${scopedKey}`) .set('Authorization', `Bearer ${scopedKey}`)
@ -744,7 +745,7 @@ describe('device-api/v1', () => {
getSingleContainerAppStub.throws(new Error()); getSingleContainerAppStub.throws(new Error());
await request(api) await request(api)
.get('/v1/apps/1234567') .get('/v1/apps/1234567')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -760,7 +761,7 @@ describe('device-api/v1', () => {
getLegacyDeviceStateStub.resolves({ test_state: 'Success' }); getLegacyDeviceStateStub.resolves({ test_state: 'Success' });
await request(api) await request(api)
.get('/v1/device') .get('/v1/device')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, { test_state: 'Success' }); .expect(200, { test_state: 'Success' });
}); });
@ -768,7 +769,7 @@ describe('device-api/v1', () => {
getLegacyDeviceStateStub.throws(new Error()); getLegacyDeviceStateStub.throws(new Error());
await request(api) await request(api)
.get('/v1/device') .get('/v1/device')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -787,7 +788,7 @@ describe('device-api/v1', () => {
getHostConfigStub.resolves({ network: { hostname: 'deadbeef' } }); getHostConfigStub.resolves({ network: { hostname: 'deadbeef' } });
await request(api) await request(api)
.get('/v1/device/host-config') .get('/v1/device/host-config')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, { network: { hostname: 'deadbeef' } }); .expect(200, { network: { hostname: 'deadbeef' } });
}); });
@ -795,7 +796,7 @@ describe('device-api/v1', () => {
getHostConfigStub.throws(new Error()); getHostConfigStub.throws(new Error());
await request(api) await request(api)
.get('/v1/device/host-config') .get('/v1/device/host-config')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -824,7 +825,7 @@ describe('device-api/v1', () => {
for (const key of Object.keys(invalidProxyReqs)) { for (const key of Object.keys(invalidProxyReqs)) {
await request(api) await request(api)
.patch('/v1/device/host-config') .patch('/v1/device/host-config')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.send({ network: { proxy: { [key]: invalidProxyReqs[key] } } }) .send({ network: { proxy: { [key]: invalidProxyReqs[key] } } })
.expect(200) .expect(200)
.then(() => { .then(() => {
@ -852,7 +853,7 @@ describe('device-api/v1', () => {
await request(api) await request(api)
.patch('/v1/device/host-config') .patch('/v1/device/host-config')
.send({}) .send({})
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200) .expect(200)
.then(() => { .then(() => {
expect(log.warn as SinonStub).to.have.been.calledWith( expect(log.warn as SinonStub).to.have.been.calledWith(

View File

@ -6,6 +6,7 @@ import request from 'supertest';
import * as config from '~/src/config'; import * as config from '~/src/config';
import * as db from '~/src/db'; import * as db from '~/src/db';
import * as apiKeys from '~/lib/api-keys';
import * as deviceApi from '~/src/device-api'; import * as deviceApi from '~/src/device-api';
import * as actions from '~/src/device-api/actions'; import * as actions from '~/src/device-api/actions';
import * as v2 from '~/src/device-api/v2'; import * as v2 from '~/src/device-api/v2';
@ -50,7 +51,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart') .post('/v2/applications/1234567/restart')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(1234567, false); expect(doRestartStub).to.have.been.calledWith(1234567, false);
doRestartStub.resetHistory(); doRestartStub.resetHistory();
@ -59,7 +60,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart') .post('/v2/applications/7654321/restart')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(7654321, true); expect(doRestartStub).to.have.been.calledWith(7654321, true);
doRestartStub.resetHistory(); doRestartStub.resetHistory();
@ -67,7 +68,7 @@ describe('device-api/v2', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v2/applications/7654321/restart') .post('/v2/applications/7654321/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doRestartStub).to.have.been.calledWith(7654321, false); expect(doRestartStub).to.have.been.calledWith(7654321, false);
}); });
@ -75,12 +76,12 @@ describe('device-api/v2', () => {
it('responds with 400 if appId is missing', async () => { it('responds with 400 if appId is missing', async () => {
await request(api) await request(api)
.post('/v2/applications/badAppId/restart') .post('/v2/applications/badAppId/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v2/applications/7654321/restart') .post('/v2/applications/7654321/restart')
.set('Authorization', `Bearer ${scopedKey}`) .set('Authorization', `Bearer ${scopedKey}`)
@ -90,7 +91,7 @@ describe('device-api/v2', () => {
it('responds with 200 if restart succeeded', async () => { it('responds with 200 if restart succeeded', async () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart') .post('/v2/applications/1234567/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -98,7 +99,7 @@ describe('device-api/v2', () => {
doRestartStub.throws(new UpdatesLockedError()); doRestartStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v2/applications/1234567/restart') .post('/v2/applications/1234567/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -106,7 +107,7 @@ describe('device-api/v2', () => {
doRestartStub.throws(new Error()); doRestartStub.throws(new Error());
await request(api) await request(api)
.post('/v2/applications/7654321/restart') .post('/v2/applications/7654321/restart')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -128,7 +129,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/purge') .post('/v2/applications/1234567/purge')
.send({ force: false }) .send({ force: false })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(1234567, false); expect(doPurgeStub).to.have.been.calledWith(1234567, false);
doPurgeStub.resetHistory(); doPurgeStub.resetHistory();
@ -137,7 +138,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/purge') .post('/v2/applications/7654321/purge')
.send({ force: true }) .send({ force: true })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(7654321, true); expect(doPurgeStub).to.have.been.calledWith(7654321, true);
doPurgeStub.resetHistory(); doPurgeStub.resetHistory();
@ -145,7 +146,7 @@ describe('device-api/v2', () => {
// Defaults to force: false // Defaults to force: false
await request(api) await request(api)
.post('/v2/applications/7654321/purge') .post('/v2/applications/7654321/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(doPurgeStub).to.have.been.calledWith(7654321, false); expect(doPurgeStub).to.have.been.calledWith(7654321, false);
}); });
@ -153,12 +154,12 @@ describe('device-api/v2', () => {
it('responds with 400 if appId is missing', async () => { it('responds with 400 if appId is missing', async () => {
await request(api) await request(api)
.post('/v2/applications/badAppId/purge') .post('/v2/applications/badAppId/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v2/applications/7654321/purge') .post('/v2/applications/7654321/purge')
.set('Authorization', `Bearer ${scopedKey}`) .set('Authorization', `Bearer ${scopedKey}`)
@ -168,7 +169,7 @@ describe('device-api/v2', () => {
it('responds with 200 if purge succeeded', async () => { it('responds with 200 if purge succeeded', async () => {
await request(api) await request(api)
.post('/v2/applications/1234567/purge') .post('/v2/applications/1234567/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -176,7 +177,7 @@ describe('device-api/v2', () => {
doPurgeStub.throws(new UpdatesLockedError()); doPurgeStub.throws(new UpdatesLockedError());
await request(api) await request(api)
.post('/v2/applications/1234567/purge') .post('/v2/applications/1234567/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -184,7 +185,7 @@ describe('device-api/v2', () => {
doPurgeStub.throws(new Error()); doPurgeStub.throws(new Error());
await request(api) await request(api)
.post('/v2/applications/7654321/purge') .post('/v2/applications/7654321/purge')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -209,7 +210,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/stop-service') .post('/v2/applications/1234567/stop-service')
.send({ force: false, serviceName: 'test' }) .send({ force: false, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -224,7 +225,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ force: true, serviceName: 'test' }) .send({ force: true, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -239,7 +240,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -254,7 +255,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ imageId: 111 }) .send({ imageId: 111 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -269,7 +270,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'stop', action: 'stop',
@ -281,7 +282,7 @@ describe('device-api/v2', () => {
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
@ -293,7 +294,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/stop-service') .post('/v2/applications/1234567/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -302,21 +303,21 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/stop-service') .post('/v2/applications/1234567/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(404); .expect(404);
}); });
it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => { it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => {
await request(api) await request(api)
.post('/v2/applications/badAppId/stop-service') .post('/v2/applications/badAppId/stop-service')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
executeServiceActionStub.throws(new BadRequestError()); executeServiceActionStub.throws(new BadRequestError());
await request(api) await request(api)
.post('/v2/applications/1234567/stop-service') .post('/v2/applications/1234567/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -325,7 +326,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/stop-service') .post('/v2/applications/1234567/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -334,7 +335,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/stop-service') .post('/v2/applications/7654321/stop-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -359,7 +360,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/start-service') .post('/v2/applications/1234567/start-service')
.send({ force: false, serviceName: 'test' }) .send({ force: false, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -374,7 +375,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ force: true, serviceName: 'test' }) .send({ force: true, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -389,7 +390,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -404,7 +405,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ imageId: 111 }) .send({ imageId: 111 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -419,7 +420,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'start', action: 'start',
@ -431,7 +432,7 @@ describe('device-api/v2', () => {
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
@ -443,7 +444,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/start-service') .post('/v2/applications/1234567/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -452,21 +453,21 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/start-service') .post('/v2/applications/1234567/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(404); .expect(404);
}); });
it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => { it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => {
await request(api) await request(api)
.post('/v2/applications/badAppId/start-service') .post('/v2/applications/badAppId/start-service')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
executeServiceActionStub.throws(new BadRequestError()); executeServiceActionStub.throws(new BadRequestError());
await request(api) await request(api)
.post('/v2/applications/1234567/start-service') .post('/v2/applications/1234567/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -475,7 +476,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/start-service') .post('/v2/applications/1234567/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -484,7 +485,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/start-service') .post('/v2/applications/7654321/start-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });
@ -509,7 +510,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart-service') .post('/v2/applications/1234567/restart-service')
.send({ force: false, serviceName: 'test' }) .send({ force: false, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'restart', action: 'restart',
@ -524,7 +525,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ force: true, serviceName: 'test' }) .send({ force: true, serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'restart', action: 'restart',
@ -539,7 +540,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'restart', action: 'restart',
@ -554,7 +555,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ imageId: 111 }) .send({ imageId: 111 })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'restart', action: 'restart',
@ -569,7 +570,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
expect(executeServiceActionStub).to.have.been.calledWith({ expect(executeServiceActionStub).to.have.been.calledWith({
action: 'restart', action: 'restart',
@ -581,7 +582,7 @@ describe('device-api/v2', () => {
}); });
it("responds with 401 if caller's API key is not in scope of appId", async () => { it("responds with 401 if caller's API key is not in scope of appId", async () => {
const scopedKey = await deviceApi.generateScopedKey(1234567, 'main'); const scopedKey = await apiKeys.generateScopedKey(1234567, 'main');
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
@ -593,7 +594,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart-service') .post('/v2/applications/1234567/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
@ -602,21 +603,21 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart-service') .post('/v2/applications/1234567/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(404); .expect(404);
}); });
it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => { it('responds with 400 if invalid appId or missing serviceName/imageId from request body', async () => {
await request(api) await request(api)
.post('/v2/applications/badAppId/restart-service') .post('/v2/applications/badAppId/restart-service')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
executeServiceActionStub.throws(new BadRequestError()); executeServiceActionStub.throws(new BadRequestError());
await request(api) await request(api)
.post('/v2/applications/1234567/restart-service') .post('/v2/applications/1234567/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(400); .expect(400);
}); });
@ -625,7 +626,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/1234567/restart-service') .post('/v2/applications/1234567/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(423); .expect(423);
}); });
@ -634,7 +635,7 @@ describe('device-api/v2', () => {
await request(api) await request(api)
.post('/v2/applications/7654321/restart-service') .post('/v2/applications/7654321/restart-service')
.send({ serviceName: 'test' }) .send({ serviceName: 'test' })
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503); .expect(503);
}); });
}); });

View File

@ -8,7 +8,7 @@ import mockedAPI = require('~/test-lib/mocked-device-api');
import * as apiBinder from '~/src/api-binder'; import * as apiBinder from '~/src/api-binder';
import * as deviceState from '~/src/device-state'; import * as deviceState from '~/src/device-state';
import type SupervisorAPI from '~/src/device-api'; import type SupervisorAPI from '~/src/device-api';
import * as deviceApi from '~/src/device-api'; import * as apiKeys from '~/lib/api-keys';
import * as serviceManager from '~/src/compose/service-manager'; import * as serviceManager from '~/src/compose/service-manager';
import * as images from '~/src/compose/images'; import * as images from '~/src/compose/images';
import * as config from '~/src/config'; import * as config from '~/src/config';
@ -78,7 +78,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
await request await request
.get('/v2/device/vpn') .get('/v2/device/vpn')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(sampleResponses.V2.GET['/device/vpn'].statusCode) .expect(sampleResponses.V2.GET['/device/vpn'].statusCode)
.then((response) => { .then((response) => {
@ -94,7 +94,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
await request await request
.get('/v2/applications/1/state') .get('/v2/applications/1/state')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(sampleResponses.V2.GET['/applications/1/state'].statusCode) .expect(sampleResponses.V2.GET['/applications/1/state'].statusCode)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.then((response) => { .then((response) => {
@ -108,7 +108,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
await request await request
.get('/v2/applications/123invalid/state') .get('/v2/applications/123invalid/state')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect( .expect(
sampleResponses.V2.GET['/applications/123invalid/state'].statusCode, sampleResponses.V2.GET['/applications/123invalid/state'].statusCode,
@ -124,7 +124,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
await request await request
.get('/v2/applications/9000/state') .get('/v2/applications/9000/state')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(sampleResponses.V2.GET['/applications/9000/state'].statusCode) .expect(sampleResponses.V2.GET['/applications/9000/state'].statusCode)
.then((response) => { .then((response) => {
expect(response.body).to.deep.equal( expect(response.body).to.deep.equal(
@ -135,7 +135,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
describe('Scoped API Keys', () => { describe('Scoped API Keys', () => {
it('returns 409 because app is out of scope of the key', async () => { it('returns 409 because app is out of scope of the key', async () => {
const apiKey = await deviceApi.generateScopedKey(3, 'main'); const apiKey = await apiKeys.generateScopedKey(3, 'main');
await request await request
.get('/v2/applications/2/state') .get('/v2/applications/2/state')
.set('Accept', 'application/json') .set('Accept', 'application/json')
@ -157,7 +157,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
it('should return scoped application', async () => { it('should return scoped application', async () => {
// Create scoped key for application // Create scoped key for application
const appScopedKey = await deviceApi.generateScopedKey(1658654, 'main'); const appScopedKey = await apiKeys.generateScopedKey(1658654, 'main');
// Setup device conditions // Setup device conditions
serviceManagerMock.resolves([mockedAPI.mockService({ appId: 1658654 })]); serviceManagerMock.resolves([mockedAPI.mockService({ appId: 1658654 })]);
imagesMock.resolves([mockedAPI.mockImage({ appId: 1658654 })]); imagesMock.resolves([mockedAPI.mockImage({ appId: 1658654 })]);
@ -181,7 +181,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
it('should return no application info due to lack of scope', async () => { it('should return no application info due to lack of scope', async () => {
// Create scoped key for wrong application // Create scoped key for wrong application
const appScopedKey = await deviceApi.generateScopedKey(1, 'main'); const appScopedKey = await apiKeys.generateScopedKey(1, 'main');
// Setup device conditions // Setup device conditions
serviceManagerMock.resolves([mockedAPI.mockService({ appId: 1658654 })]); serviceManagerMock.resolves([mockedAPI.mockService({ appId: 1658654 })]);
imagesMock.resolves([mockedAPI.mockImage({ appId: 1658654 })]); imagesMock.resolves([mockedAPI.mockImage({ appId: 1658654 })]);
@ -204,7 +204,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
it('should return success when device has no applications', async () => { it('should return success when device has no applications', async () => {
// Create scoped key for any application // Create scoped key for any application
const appScopedKey = await deviceApi.generateScopedKey(1658654, 'main'); const appScopedKey = await apiKeys.generateScopedKey(1658654, 'main');
// Setup device conditions // Setup device conditions
serviceManagerMock.resolves([]); serviceManagerMock.resolves([]);
imagesMock.resolves([]); imagesMock.resolves([]);
@ -227,7 +227,7 @@ describe('SupervisorAPI [V2 Endpoints]', () => {
it('should only return 1 application when N > 1 applications on device', async () => { it('should only return 1 application when N > 1 applications on device', async () => {
// Create scoped key for application // Create scoped key for application
const appScopedKey = await deviceApi.generateScopedKey(1658654, 'main'); const appScopedKey = await apiKeys.generateScopedKey(1658654, 'main');
// Setup device conditions // Setup device conditions
serviceManagerMock.resolves([ serviceManagerMock.resolves([
mockedAPI.mockService({ appId: 1658654 }), mockedAPI.mockService({ appId: 1658654 }),

View File

@ -4,9 +4,9 @@ import { expect } from 'chai';
import * as config from '~/src/config'; import * as config from '~/src/config';
import * as testDb from '~/src/db'; import * as testDb from '~/src/db';
import * as deviceApi from '~/src/device-api'; import * as apiKeys from '~/lib/api-keys';
import * as middleware from '~/src/device-api/middleware'; import * as middleware from '~/src/device-api/middleware';
import type { AuthorizedRequest } from '~/src/device-api/api-keys'; import type { AuthorizedRequest } from '~/lib/api-keys';
describe('device-api/api-keys', () => { describe('device-api/api-keys', () => {
let app: express.Application; let app: express.Application;
@ -30,8 +30,8 @@ describe('device-api/api-keys', () => {
}); });
it('should generate a key which is scoped for a single application', async () => { it('should generate a key which is scoped for a single application', async () => {
const appOneKey = await deviceApi.generateScopedKey(111, 'one'); const appOneKey = await apiKeys.generateScopedKey(111, 'one');
const appTwoKey = await deviceApi.generateScopedKey(222, 'two'); const appTwoKey = await apiKeys.generateScopedKey(222, 'two');
await request(app) await request(app)
.get('/test/111') .get('/test/111')
@ -55,7 +55,7 @@ describe('device-api/api-keys', () => {
}); });
it('should generate a key which is scoped for multiple applications', async () => { it('should generate a key which is scoped for multiple applications', async () => {
const multiAppKey = await deviceApi.generateScopedKey(111, 'three', { const multiAppKey = await apiKeys.generateScopedKey(111, 'three', {
scopes: [111, 222].map((appId) => ({ type: 'app', appId })), scopes: [111, 222].map((appId) => ({ type: 'app', appId })),
}); });
@ -73,54 +73,54 @@ describe('device-api/api-keys', () => {
it('should generate a key which is scoped for all applications', async () => { it('should generate a key which is scoped for all applications', async () => {
await request(app) await request(app)
.get('/test/111') .get('/test/111')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
await request(app) await request(app)
.get('/test/222') .get('/test/222')
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`) .set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200); .expect(200);
}); });
it('should have a cached lookup of key scopes', async () => { it('should have a cached lookup of key scopes', async () => {
const globalScopes = await deviceApi.getScopesForKey( const globalScopes = await apiKeys.getScopesForKey(
await deviceApi.getGlobalApiKey(), await apiKeys.getGlobalApiKey(),
); );
const key = 'my-new-key'; const key = 'my-new-key';
await testDb await testDb
.models('apiSecret') .models('apiSecret')
.where({ key: await deviceApi.getGlobalApiKey() }) .where({ key: await apiKeys.getGlobalApiKey() })
.update({ key }); .update({ key });
// Key has been changed, but cache should retain the old key // Key has been changed, but cache should retain the old key
expect( expect(
await deviceApi.getScopesForKey(await deviceApi.getGlobalApiKey()), await apiKeys.getScopesForKey(await apiKeys.getGlobalApiKey()),
).to.deep.equal(globalScopes); ).to.deep.equal(globalScopes);
// Bust the cache and generate a new global API key // Bust the cache and generate a new global API key
const refreshedKey = await deviceApi.refreshKey( const refreshedKey = await apiKeys.refreshKey(
await deviceApi.getGlobalApiKey(), await apiKeys.getGlobalApiKey(),
); );
// Key that we changed in db is no longer valid // Key that we changed in db is no longer valid
expect(await deviceApi.getScopesForKey(key)).to.be.null; expect(await apiKeys.getScopesForKey(key)).to.be.null;
// Refreshed key should have the global scopes // Refreshed key should have the global scopes
expect(await deviceApi.getScopesForKey(refreshedKey)).to.deep.equal( expect(await apiKeys.getScopesForKey(refreshedKey)).to.deep.equal(
globalScopes, globalScopes,
); );
}); });
it('should regenerate a key and invalidate the old one', async () => { it('should regenerate a key and invalidate the old one', async () => {
const appScopedKey = await deviceApi.generateScopedKey(111, 'four'); const appScopedKey = await apiKeys.generateScopedKey(111, 'four');
await request(app) await request(app)
.get('/test/111') .get('/test/111')
.set('Authorization', `Bearer ${appScopedKey}`) .set('Authorization', `Bearer ${appScopedKey}`)
.expect(200); .expect(200);
const newScopedKey = await deviceApi.refreshKey(appScopedKey); const newScopedKey = await apiKeys.refreshKey(appScopedKey);
await request(app) await request(app)
.get('/test/111') .get('/test/111')