Skip to content

Commit

Permalink
Merge pull request #1439 from balena-io/config-vars-check-dt-access
Browse files Browse the repository at this point in the history
Check user access to the device type provided to /config/vars
  • Loading branch information
thgreasi authored Sep 28, 2023
2 parents cd99532 + a0c5b70 commit 4bedac0
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 11 deletions.
45 changes: 40 additions & 5 deletions src/features/vars-schema/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RequestHandler } from 'express';
import type { JSONSchema6 } from 'json-schema';

import { sbvrUtils } from '@balena/pinejs';
import {
BLOCKED_NAMES,
DEVICE_TYPE_SPECIFIC_CONFIG_VAR_PROPERTIES,
Expand All @@ -13,19 +13,54 @@ import {
ALLOWED_NAMESPACES,
} from './env-vars';

const { api } = sbvrUtils;

// Return config variable constants for use by external components.
// A query string parameter of 'deviceType' is accepted, which should
// be a device type slug.
export const schema: RequestHandler = (req, res) => {
export const schema: RequestHandler = async (req, res) => {
const deviceTypeSlug = await (async () => {
if (typeof req.query.deviceType !== 'string') {
return;
}

const resinApi = api.resin.clone({ passthrough: { req } });
// Ensure that the user has access to the provided device type.
const [dt] = (await resinApi.get({
resource: 'device_type',
options: {
$top: 1,
$select: 'slug',
$filter: {
device_type_alias: {
$any: {
$alias: 'dta',
$expr: {
dta: {
is_referenced_by__alias: req.query.deviceType,
},
},
},
},
},
},
})) as Array<{ slug: string }>;

// We do not throw when the DT is not found for backwards compatibility reasons.
return dt?.slug;
})();

const configVarSchema: JSONSchema6 = {
type: 'object',
$schema: 'http://json-schema.org/draft-06/schema#',
properties: Object.assign(
{},
SUPERVISOR_CONFIG_VAR_PROPERTIES,
...DEVICE_TYPE_SPECIFIC_CONFIG_VAR_PROPERTIES.filter((config) =>
config.capableDeviceTypes.includes(req.query.deviceType as string),
).map((config) => config.properties),
...(deviceTypeSlug != null
? DEVICE_TYPE_SPECIFIC_CONFIG_VAR_PROPERTIES.filter((config) =>
config.capableDeviceTypes.includes(deviceTypeSlug),
).map((config) => config.properties)
: []),
),
};

Expand Down
8 changes: 8 additions & 0 deletions test/01_basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ describe('Basic', () => {
checkBaseVarsResult(vars);
});

it(`should return the base vars when device type is not found`, async () => {
const { body: vars } = await supertest()
.get(`/config/vars?deviceType=wrong-device-type`)
.expect(200);

checkBaseVarsResult(vars);
});

[
{ deviceType: 'beaglebone-black' },
{
Expand Down
4 changes: 2 additions & 2 deletions test/02_device-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('device type resource', () => {
expect(deviceType).to.have.property('name').that.is.a('string');
});

expect(res.body.d).to.have.property('length', 14);
expect(res.body.d).to.have.property('length', 16);
});
});

Expand Down Expand Up @@ -217,7 +217,7 @@ describe('device type endpoints', () => {
it('should return a proper result', async () => {
const res = await supertest().get('/device-types/v1').expect(200);
expect(res.body).to.be.an('array');
expect(res.body).to.have.property('length', 15);
expect(res.body).to.have.property('length', 17);
const rpi3config = _.find(res.body, { slug: 'raspberrypi3' });
expect(rpi3config).to.be.an('object');
expect(rpi3config).to.have.property('buildId', '2.19.0+rev1.prod');
Expand Down
8 changes: 4 additions & 4 deletions test/09_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('contracts', () => {
await fetchContractsLocally([contractRepository]);
const contracts = await getContracts('hw.device-type');

expect(contracts).to.have.length(14);
expect(contracts).to.have.length(16);
expect(contracts.find((contract) => contract.slug === 'raspberrypi3')).to
.not.be.undefined;
});
Expand All @@ -96,7 +96,7 @@ describe('contracts', () => {
]);
const contracts = await getContracts('hw.device-type');

expect(contracts).to.have.length(15);
expect(contracts).to.have.length(17);
expect(
contracts.find((contract) => contract.slug === 'other-contract-dt'),
).to.not.be.undefined;
Expand Down Expand Up @@ -163,7 +163,7 @@ describe('contracts', () => {
(dbDeviceType) => dbDeviceType.slug === 'fincm3',
);

expect(contracts).to.have.length(15);
expect(contracts).to.have.length(17);
expect(newDt).to.not.be.undefined;
expect(finDt).to.have.property('name', 'Fin');
});
Expand Down Expand Up @@ -232,7 +232,7 @@ describe('contracts', () => {
(dbDeviceType) => dbDeviceType.slug === 'raspberry-pi',
);

expect(dbDeviceTypes).to.have.length(15);
expect(dbDeviceTypes).to.have.length(17);
expect(newDt).to.not.be.undefined;
expect(finDt).to.have.property('name', 'Fin');
expect(finDt).to.have.deep.property(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"slug": "jetson-tx2",
"version": "1",
"type": "hw.device-type",
"aliases": [],
"name": "Nvidia Jetson TX2",
"assets": {
"logo": {
"url": "./jetson-tx2.svg",
"name": "logo"
}
},
"data": {
"arch": "aarch64",
"hdmi": true,
"led": false,
"connectivity": {
"bluetooth": true,
"wifi": true
},
"storage": {
"internal": true
},
"media": {
"defaultBoot": "internal",
"altBoot": ["sdcard"]
},
"is_private": false
},
"partials": {
"bootDeviceExternal": [
"Connect power to the {{name}} and press and hold the POWER push button for 1 second"
],
"flashIndicator": ["all LEDs are off"],
"bootDevice": ["Remove and re-connect power to the {{name}}"]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"slug": "up-board",
"version": "1",
"type": "hw.device-type",
"aliases": [],
"name": "UP Board",
"assets": {
"logo": {
"url": "./up-board.svg",
"name": "logo"
}
},
"data": {
"arch": "amd64",
"family": "family-upboard",
"hdmi": true,
"led": true,
"connectivity": {
"bluetooth": false,
"wifi": false
},
"storage": {
"internal": true
},
"media": {
"defaultBoot": "internal",
"altBoot": ["usb_mass_storage"]
},
"is_private": false
},
"partials": {
"bootDeviceExternal": [
"Power on the {{name}} with a keyboard connected.",
"Press the F7 key while BIOS is loading to enter the boot menu.",
"Select the \"UEFI:\" option from the boot menu."
],
"flashIndicator": ["all LEDs are off"],
"bootDevice": ["Power up the {{name}}"]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4bedac0

Please sign in to comment.