From 753ae240986315c4c6ee51491ce479ee75589d99 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Thu, 29 Jun 2023 03:54:51 -0400 Subject: [PATCH] fix(striker-ui-api): add create, update handlers for /fence --- .../lib/request_handlers/fence/createFence.ts | 134 ++++++++++++++++++ .../src/lib/request_handlers/fence/index.ts | 2 + .../lib/request_handlers/fence/updateFence.ts | 3 + striker-ui-api/src/routes/fence.ts | 6 +- 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 striker-ui-api/src/lib/request_handlers/fence/createFence.ts create mode 100644 striker-ui-api/src/lib/request_handlers/fence/updateFence.ts diff --git a/striker-ui-api/src/lib/request_handlers/fence/createFence.ts b/striker-ui-api/src/lib/request_handlers/fence/createFence.ts new file mode 100644 index 00000000..fccf9bab --- /dev/null +++ b/striker-ui-api/src/lib/request_handlers/fence/createFence.ts @@ -0,0 +1,134 @@ +import assert from 'assert'; +import { RequestHandler } from 'express'; + +import { REP_PEACEFUL_STRING } from '../../consts'; + +import { getFenceSpec, timestamp, write } from '../../accessModule'; +import { sanitize } from '../../sanitize'; +import { stderr, stdoutVar, uuid } from '../../shell'; + +const MAP_TO_VAR_TYPE: Record< + AnvilDataFenceParameterType, + 'boolean' | 'number' | 'string' +> = { + boolean: 'boolean', + integer: 'number', + second: 'number', + select: 'string', + string: 'string', +}; + +export const createFence: RequestHandler< + { uuid?: string }, + undefined, + { + agent: string; + name: string; + parameters: { [parameterId: string]: boolean | number | string }; + } +> = async (request, response) => { + const { + body: { agent: rAgent, name: rName, parameters: rParameters }, + params: { uuid: rUuid }, + } = request; + + let fenceSpec: AnvilDataFenceHash; + + try { + fenceSpec = await getFenceSpec(); + } catch (error) { + stderr(`Failed to get fence devices specification; CAUSE: ${error}`); + + return response.status(500).send(); + } + + const agent = sanitize(rAgent, 'string'); + const name = sanitize(rName, 'string'); + const fenceUuid = sanitize(rUuid, 'string', { fallback: uuid() }); + + const { [agent]: agentSpec } = fenceSpec; + + try { + assert.ok(agentSpec, `Agent is unknown to spec; got [${agent}]`); + + assert( + REP_PEACEFUL_STRING.test(name), + `Name must be a peaceful string; got [${name}]`, + ); + + const rParamsType = typeof rParameters; + + assert( + rParamsType === 'object', + `Parameters must be an object; got type [${rParamsType}]`, + ); + } catch (error) { + assert( + `Failed to assert value when working with fence device; CAUSE: ${error}`, + ); + + return response.status(400).send(); + } + + const { parameters: agentSpecParams } = agentSpec; + + const args = Object.entries(agentSpecParams) + .reduce((previous, [paramId, paramSpec]) => { + const { content_type: paramType, default: paramDefault } = paramSpec; + const { [paramId]: rParamValue } = rParameters; + + if ( + [paramDefault, '', null, undefined].some((bad) => rParamValue === bad) + ) + return previous; + + // TODO: add SQL modifier after finding a way to escape single quotes + const paramValue = sanitize(rParamValue, MAP_TO_VAR_TYPE[paramType]); + + previous.push(`${paramId}="${paramValue}"`); + + return previous; + }, []) + .join(' '); + + stdoutVar( + { agent, args, name }, + `Proceed to record fence device (${fenceUuid}): `, + ); + + const modifiedDate = timestamp(); + + try { + const wcode = await write( + `INSERT INTO + fences ( + fence_uuid, + fence_name, + fence_agent, + fence_arguments, + modified_date + ) VALUES ( + '${fenceUuid}', + '${name}', + '${agent}', + '${args}', + '${modifiedDate}' + ) ON CONFLICT (fence_uuid) + DO UPDATE SET + fence_name = '${name}', + fence_agent = '${agent}', + fence_arguments = '${args}', + modified_date = '${modifiedDate}';`, + ); + + assert(wcode === 0, `Write exited with code ${wcode}`); + } catch (error) { + stderr(`Failed to write fence record; CAUSE: ${error}`); + + return response.status(500).send(); + } + + const scode = rUuid ? 201 : 200; + + return response.status(scode).send(); +}; diff --git a/striker-ui-api/src/lib/request_handlers/fence/index.ts b/striker-ui-api/src/lib/request_handlers/fence/index.ts index 8c5ca77f..dc13ad37 100644 --- a/striker-ui-api/src/lib/request_handlers/fence/index.ts +++ b/striker-ui-api/src/lib/request_handlers/fence/index.ts @@ -1,3 +1,5 @@ +export * from './createFence'; export * from './deleteFence'; export * from './getFence'; export * from './getFenceTemplate'; +export * from './updateFence'; diff --git a/striker-ui-api/src/lib/request_handlers/fence/updateFence.ts b/striker-ui-api/src/lib/request_handlers/fence/updateFence.ts new file mode 100644 index 00000000..037b407c --- /dev/null +++ b/striker-ui-api/src/lib/request_handlers/fence/updateFence.ts @@ -0,0 +1,3 @@ +import { createFence } from './createFence'; + +export const updateFence = createFence; diff --git a/striker-ui-api/src/routes/fence.ts b/striker-ui-api/src/routes/fence.ts index f502ce12..9c018ff2 100644 --- a/striker-ui-api/src/routes/fence.ts +++ b/striker-ui-api/src/routes/fence.ts @@ -1,9 +1,11 @@ import express from 'express'; import { + createFence, deleteFence, getFence, getFenceTemplate, + updateFence, } from '../lib/request_handlers/fence'; const router = express.Router(); @@ -11,6 +13,8 @@ const router = express.Router(); router .delete('/:uuid?', deleteFence) .get('/', getFence) - .get('/template', getFenceTemplate); + .get('/template', getFenceTemplate) + .post('/', createFence) + .put('/:uuid', updateFence); export default router;