mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-29 10:08:52 +00:00
2007a98940
Closes: #1249 Change-type: minor Signed-off-by: Miguel Casqueira <miguel@balena.io>
133 lines
3.4 KiB
TypeScript
133 lines
3.4 KiB
TypeScript
import { expect } from 'chai';
|
|
import { fs } from 'mz';
|
|
import * as requestLib from 'request';
|
|
|
|
import Config from '../src/config';
|
|
import Database from '../src/db';
|
|
import EventTracker from '../src/event-tracker';
|
|
import SupervisorAPI from '../src/supervisor-api';
|
|
|
|
const mockedOptions = {
|
|
listenPort: 12345,
|
|
timeout: 30000,
|
|
dbPath: './test/data/supervisor-api.sqlite',
|
|
};
|
|
|
|
const VALID_SECRET = 'secure_api_secret';
|
|
const INVALID_SECRET = 'bad_api_secret';
|
|
const ALLOWED_INTERFACES = ['lo']; // Only need loopback since this is for testing
|
|
|
|
describe('SupervisorAPI authentication', () => {
|
|
let api: SupervisorAPI;
|
|
|
|
before(async () => {
|
|
const db = new Database({
|
|
databasePath: mockedOptions.dbPath,
|
|
});
|
|
await db.init();
|
|
const mockedConfig = new Config({ db });
|
|
await mockedConfig.init();
|
|
// Set apiSecret that we can test with
|
|
await mockedConfig.set({ apiSecret: VALID_SECRET });
|
|
api = new SupervisorAPI({
|
|
config: mockedConfig,
|
|
eventTracker: new EventTracker(),
|
|
routers: [],
|
|
healthchecks: [],
|
|
});
|
|
return api.listen(
|
|
ALLOWED_INTERFACES,
|
|
mockedOptions.listenPort,
|
|
mockedOptions.timeout,
|
|
);
|
|
});
|
|
|
|
after(async () => {
|
|
api.stop();
|
|
try {
|
|
await fs.unlink(mockedOptions.dbPath);
|
|
} catch (e) {
|
|
/* noop */
|
|
}
|
|
});
|
|
|
|
it('finds no apiKey and rejects', async () => {
|
|
const response = await postAsync('/v1/blink');
|
|
expect(response.statusCode).to.equal(401);
|
|
});
|
|
|
|
it('finds apiKey from query', async () => {
|
|
const response = await postAsync(`/v1/blink?apikey=${VALID_SECRET}`);
|
|
expect(response.statusCode).to.equal(200);
|
|
});
|
|
|
|
it('finds apiKey from Authorization header (ApiKey scheme)', async () => {
|
|
const response = await postAsync(`/v1/blink`, {
|
|
Authorization: `ApiKey ${VALID_SECRET}`,
|
|
});
|
|
expect(response.statusCode).to.equal(200);
|
|
});
|
|
|
|
it('finds apiKey from Authorization header (Bearer scheme)', async () => {
|
|
const response = await postAsync(`/v1/blink`, {
|
|
Authorization: `Bearer ${VALID_SECRET}`,
|
|
});
|
|
expect(response.statusCode).to.equal(200);
|
|
});
|
|
|
|
it('finds apiKey from Authorization header (case insensitive)', async () => {
|
|
const randomCases = [
|
|
'Bearer',
|
|
'bearer',
|
|
'BEARER',
|
|
'BeAReR',
|
|
'ApiKey',
|
|
'apikey',
|
|
'APIKEY',
|
|
'ApIKeY',
|
|
];
|
|
for (const scheme of randomCases) {
|
|
const response = await postAsync(`/v1/blink`, {
|
|
Authorization: `${scheme} ${VALID_SECRET}`,
|
|
});
|
|
expect(response.statusCode).to.equal(200);
|
|
}
|
|
});
|
|
|
|
it('rejects invalid apiKey from query', async () => {
|
|
const response = await postAsync(`/v1/blink?apikey=${INVALID_SECRET}`);
|
|
expect(response.statusCode).to.equal(401);
|
|
});
|
|
|
|
it('rejects invalid apiKey from Authorization header (ApiKey scheme)', async () => {
|
|
const response = await postAsync(`/v1/blink`, {
|
|
Authorization: `ApiKey ${INVALID_SECRET}`,
|
|
});
|
|
expect(response.statusCode).to.equal(401);
|
|
});
|
|
|
|
it('rejects invalid apiKey from Authorization header (Bearer scheme)', async () => {
|
|
const response = await postAsync(`/v1/blink`, {
|
|
Authorization: `Bearer ${INVALID_SECRET}`,
|
|
});
|
|
expect(response.statusCode).to.equal(401);
|
|
});
|
|
});
|
|
|
|
function postAsync(path: string, headers = {}): Promise<any> {
|
|
return new Promise((resolve, reject) => {
|
|
requestLib.post(
|
|
{
|
|
url: `http://127.0.0.1:${mockedOptions.listenPort}${path}`,
|
|
headers,
|
|
},
|
|
(error: Error, response: requestLib.Response) => {
|
|
if (error) {
|
|
reject(error);
|
|
}
|
|
resolve(response);
|
|
},
|
|
);
|
|
});
|
|
}
|