Skip to content

Commit

Permalink
Add provision config generation to controller
Browse files Browse the repository at this point in the history
Add tests for this
  • Loading branch information
Gum-Joe committed Dec 18, 2020
1 parent 65e26bf commit 4ca7249
Show file tree
Hide file tree
Showing 18 changed files with 206 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
],
"group": {
"kind": "build",
"isDefault": true
"isDefault": true,
},
"label": "tsc: build - tsconfig.json"
},
Expand Down
3 changes: 3 additions & 0 deletions detectors/detector-desktop/controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@
"dependencies": {
"@twokeys/addons": "^1.0.0",
"@twokeys/core": "^1.0.0",
"@twokeys/detector-desktop-schemas": "^1.0.0",
"handlebars": "^4.7.6",
"rimraf": "^3.0.2",
"which": "^2.0.2"
},
"devDependencies": {
"@types/chai-json-schema": "^1.4.6",
"@types/mock-fs": "^4.10.0",
"@types/which": "^1.3.2",
"chai-json-schema": "^1.5.1",
"mock-fs": "^4.13.0"
}
}
23 changes: 23 additions & 0 deletions detectors/detector-desktop/controller/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ export const VIRTUALBOX_NAME = "VirtualBox";
export const VIRTUALBOX_EXECUTABLE_NAME = "VirtualBox Install Folder";
export const VAGRANT_NAME = "Vagrant";
export const VAGRANT_EXECUTABLE_NAME = "Vagrant Install Folder";
/** Controls the root mount point for stuff into the VM, i.e. where configs, Vagrantfile and ansible scripts will all be mounted. MUST NOT END IN A SLASH (`/`) */
export const VAGRANT_MOUNT_POINT = "/vagrant";
/**
* Where configs will be stored on the client
*/
// TODO: Move to shared folder so python can also access this
export const VM_MOUNT_CONFIGS = VAGRANT_MOUNT_POINT + "/config";
/**
* Where projects will be stored on the client
*/
// TODO: Move to shared folder so python can also access this
export const VM_MOUNT_PROJECTS = VAGRANT_MOUNT_POINT + "/projects";
/**
* VM Mount location of client config from controller ({@link ClientConfigHere}).
*
* Not to be confused with the client provisioning config
*/
export const VM_MOUNT_CLIENT_CONFIG = VM_MOUNT_CONFIGS + "/client.yml";
/**
* Storage path for provision script, relative to root.
*/
// TODO: Move to a common package so Python also can access this
export const PROVISION_CONFIG_STORAGE = "config/provision.yml";

/** Root where assets are stored */
export const ASSETS_ROOT = join(__dirname, "../assets");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ContentCopier from "@twokeys/addons/lib/util/copy-contents";
import { ClientConfig } from "@twokeys/core/lib/interfaces";
import { ClientConfigHere } from "../../config";
import { VM_ASSETS_ROOT } from "../../constants";
import { generateProvisionScript, PROVISION_CONFIG_TEMPLATE } from "./provision";
import { updateVagrantFile, updateVMLaunchFiles } from "./template-updates";
import { startVM } from "./vm";

Expand All @@ -42,6 +43,10 @@ export const newClient: DetectorPromisedTaskFunction<ClientConfig<ClientConfigHe
twokeys.logger.substatus("Filling out templates");
await updateVagrantFile(twokeys, config.controllerConfig);
await updateVMLaunchFiles(twokeys, config.controllerConfig);

// 1.1: Create provision config, which is fed into twokeys on the client to set the software up
await generateProvisionScript(twokeys, config);

// TODO: Add to startup (once certain issues are fixed)
// if (config.controllerConfig.perms.addToStartup) {
// twokeys.logger.substatus("Adding startup script launch.vbs to startup");
Expand All @@ -57,4 +62,4 @@ export const newClient: DetectorPromisedTaskFunction<ClientConfig<ClientConfigHe
// 4: SSH in and validate 2Keys is installed; copy client config & configure shares (run 2Keys client init)
};

export default newClient;
export default newClient;
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @license
* Copyright 2020 Kishan Sambhi
*
* This file is part of 2Keys.
*
* 2Keys is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 2Keys is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 2Keys. If not, see <https://www.gnu.org/licenses/>.
*/

/**
* Contains the provision config template to use & function to create it
* This is then ran through the detector via `twokeys -f provision.yml` to provision the detector for 2Keys
* @packageDocumentation
*/
import { ClientSideProvisioningConfig as ProvisioningConfig } from "@twokeys/detector-desktop-schemas/types/client-side-provisioning-config";
import { TwoKeys } from "@twokeys/addons";
import { ClientConfig } from "@twokeys/core/lib/interfaces";
import { ClientSideProvisioningConfig } from "@twokeys/detector-desktop-schemas/types/client-side-provisioning-config";
import { ClientConfigHere } from "../../config";
import { PROVISION_CONFIG_STORAGE, VM_ASSETS_ROOT, VM_MOUNT_CLIENT_CONFIG, VM_MOUNT_CONFIGS, VM_MOUNT_PROJECTS } from "../../constants";
import { promises as fs } from "fs";
import { stringifyAnyConfig } from "@twokeys/core/lib/config";
import { join } from "path";

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const packageJSON = require("../../../package.json");

/**
* Template for provisioning the client with `twokeys -f config/provision.yml`
*/
export const PROVISION_CONFIG_TEMPLATE: { twokeys: ProvisioningConfig["twokeys"]; client: {} } = {
twokeys: {
version: packageJSON.version,
type: "CLIENT_SETUP",
createdBy: "CONTROLLER",
},

client: {} // These details are left to individual clients
};

/**
* Generates config used to provision the detector client-side
* @see ClientSideProvisioningConfig
* @param twokeys TwoKeys object
* @param config Client config (passed to add-on by 2Keys)
*/
export async function generateProvisionScript(twokeys: TwoKeys<"detector">, config: ClientConfig<ClientConfigHere>) {
twokeys.logger.substatus("Creating provision script");
const provisionConfigFile: ClientSideProvisioningConfig = {
...PROVISION_CONFIG_TEMPLATE,
client: {
name: config.name,
id: config.id,
roots: {
config: VM_MOUNT_CONFIGS,
projects: VM_MOUNT_PROJECTS,
clientConfigName: VM_MOUNT_CLIENT_CONFIG,
},
server: {
ipv4: (await twokeys.configs.getMainConfig()).network.ipv4,
}
}
};
// Write it
twokeys.logger.info("Writing provision config...");
await fs.writeFile(join(twokeys.properties.clientRoot, PROVISION_CONFIG_STORAGE), stringifyAnyConfig(provisionConfigFile));
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { TwoKeys, TWOKEYS_ADDON_TYPE_DETECTOR } from "@twokeys/addons";
import { promises as fs } from "fs";
import { join } from "path";
import { ClientConfigHere } from "../../../config";
import { VAGRANT_FILE_TEMPLATE, VAGRANT_FILE_DEST } from "../../../constants";
import { VAGRANT_FILE_TEMPLATE, VAGRANT_FILE_DEST, VAGRANT_MOUNT_POINT } from "../../../constants";
import Handlebars from "handlebars";

/** Parameters for the Vagrantfile template */
Expand Down Expand Up @@ -59,7 +59,7 @@ export interface UsbPassthroughParams {
// TODO: Adjust script param to be relative
export const ANSIBLE_SCRIPT_TEMPLATE =
` config.vm.provision "ansible_local" do |ansible|
ansible.playbook = "/vagrant/{{ script }}"
ansible.playbook = "${VAGRANT_MOUNT_POINT}/{{ script }}"
end`;

/** @see ANSIBLE_SCRIPT_TEMPLATE */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { ClientConfig } from "@twokeys/core/lib/interfaces";
import { expect } from "chai";
import chai, { expect } from "chai";
import chaiJSONSchema from "chai-json-schema";
import chaiFS from "chai-fs";
import { join } from "path";
import sinon from "sinon";
import { ClientConfigHere } from "../../../src/config";
import newClient from "../../../src/setup/client/newClient";
import * as vmCode from "../../../src/setup/client/vm";
import { MOCK_CLIENT_LOCATION, twokeys } from "../../constants";
import { promises as fs } from "fs";
import { VAGRANT_FILE_DEST, VM_LAUNCH_BAT_FILE_DEST, VM_LAUNCH_VBS_FILE_DEST } from "../../../src/constants";
import { PROVISION_CONFIG_STORAGE, VAGRANT_FILE_DEST, VM_LAUNCH_BAT_FILE_DEST, VM_LAUNCH_VBS_FILE_DEST } from "../../../src/constants";

chai.use(chaiJSONSchema);
chai.use(chaiFS);

// For coverage
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import detectorFull from "../../../src/index";
import { loadConfig } from "@twokeys/core/lib/config";

// Property access check
detectorFull.install;
Expand Down Expand Up @@ -49,7 +55,7 @@ describe("Client creation tests", () => {
sinon.replace(vmCode, "startVM", async () => {return;});
});

it("should successfully generate a client", async () => {
it("should successfully generate a client, with created configs matching schemas", async () => {
await expect(newClient(twokeys, clientConfig)).to.eventually.be.fulfilled;

expect((await fs.readFile(join(MOCK_CLIENT_LOCATION, VAGRANT_FILE_DEST))).toString()).to.include(checkForUSB);
Expand All @@ -58,6 +64,13 @@ describe("Client creation tests", () => {
// Check templates
expect((await fs.readFile(join(MOCK_CLIENT_LOCATION, VM_LAUNCH_BAT_FILE_DEST))).toString()).to.include(`cd "${MOCK_CLIENT_LOCATION}"`);
expect((await fs.readFile(join(MOCK_CLIENT_LOCATION, VM_LAUNCH_VBS_FILE_DEST))).toString()).to.include(`"cmd /c '${join(MOCK_CLIENT_LOCATION, VM_LAUNCH_BAT_FILE_DEST)}'"`);

// Check against schemas
const provisionSchema = require("@twokeys/detector-desktop-schemas/src/client-side-provisioning-config.json");
expect(join(MOCK_CLIENT_LOCATION, PROVISION_CONFIG_STORAGE)).to.be.a.file();
// @ts-ignore
expect(await loadConfig(join(MOCK_CLIENT_LOCATION, PROVISION_CONFIG_STORAGE))).to.be.jsonSchema(provisionSchema);

});

it("should respect custom vagrant paths in config", async () => {
Expand Down
4 changes: 4 additions & 0 deletions detectors/detector-desktop/controller/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
{
"path": "../../../packages/@twokeys/addons",
"prepend": false
},
{
"path": "../schemas",
"prepend": false
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,14 @@
}
},
"server": {
"description": "Information about the 2Keys server this client is for",
"description": "Information about the 2Keys server this client is for. Contain address of server, but not port, as port is project specific",
"required": [
"ipv4",
"port"
"ipv4"
],
"properties": {
"ipv4": {
"type": "string",
"description": "ipv4 address of server"
},
"port": {
"type": "number",
"description": "Server port"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* and run json-schema-to-typescript to regenerate this file.
*/

export interface ClientSideConfig {
export interface ClientSideProvisioningConfig {
/**
* Contains information about the 2Keys install that provisioned this client
*/
Expand Down Expand Up @@ -54,17 +54,13 @@ export interface ClientSideConfig {
[k: string]: unknown;
};
/**
* Information about the 2Keys server this client is for
* Information about the 2Keys server this client is for. Contain address of server, but not port, as port is project specific
*/
server: {
/**
* ipv4 address of server
*/
ipv4: string;
/**
* Server port
*/
port: number;
[k: string]: unknown;
};
[k: string]: unknown;
Expand Down
1 change: 0 additions & 1 deletion detectors/detector-pi/detector/mock/client-provision.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ client:
id: UUIDv4
server:
ipv4: 10.0.2.2
port: 8080
roots:
config: ./mock/vagrant/config # NOTE: Should be absolute paths
projects: ./mock/vagrant/projects # NOTE: Should be absolute paths
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ client:
name: Example
# id: UUIDv4 # MISSING!
server:
# ipv4: 10.0.2.2 # MISSING!
port: "badtype"
ipv4: 1000 # Bad type!
roots:
# config: ./tests/mock/provision/config # NOTE: Should be absolute paths # MISSING!
projects: ./tests/mock/provision/projects # NOTE: Should be absolute paths
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ client:
id: UUIDv4
server:
ipv4: 10.0.2.2
port: 8080
roots:
config: ./tests/mock/provision/config # NOTE: Should be absolute paths
projects: ./tests/mock/provision/projects # NOTE: Should be absolute paths
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
}
},
"server": {
"description": "Information about the 2Keys server this client is for",
"description": "Information about the 2Keys server this client is for. Contain address of server, but not port, as port is project specific",
"required": [
"ipv4",
"port"
Expand All @@ -70,10 +70,6 @@
"ipv4": {
"type": "string",
"description": "ipv4 address of server"
},
"port": {
"type": "number",
"description": "Server port"
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion detectors/detector-pi/detector/twokeys/util/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@

# SCHEMAS
SCHEMA_ROOT = os.path.join(SCRIPTS_ROOT, "./assets/schemas")
SCHEMA_PROVISIONING = os.path.join(SCHEMA_ROOT, "client-side-config.json")
SCHEMA_PROVISIONING = os.path.join(
SCHEMA_ROOT, "client-side-provisioning-config.json")
SCHEMA_KDB_MAP = os.path.join(SCHEMA_ROOT, "client-side-keyboard-map.json")
SCHEMA_PROJECTS_MAP = os.path.join(SCHEMA_ROOT, "client-side-projects-map.json")

Expand Down
Loading

0 comments on commit 4ca7249

Please sign in to comment.