import { SSMClient, GetParameterCommand, } from "@aws-sdk/client-ssm"; import { timingSafeEqual } from "node:crypto"; const ssm = new SSMClient({}); function tokensMatch(provided: string, expected: string): boolean { const a = Buffer.from(provided); const b = Buffer.from(expected); // timingSafeEqual throws on unequal-length buffers; check length first. return a.length === b.length && timingSafeEqual(a, b); } let cachedAuthToken: string | undefined; let cachedApiKey: string | undefined; const LOCKDOWN_PROFILES: Record = { bohemia: { id: "4b4a3e6b-c903-4cce-8cd6-288612bf0542", name: "Bohemia - Whole Building", linekey: 3 }, ronkonkoma: { id: "ff9876bc-c54f-472e-aef9-d2bffd4b7cf7", name: "Ronkonkoma - Whole Building", linekey: 4 }, }; const ELEMENTS_BASE_URL = "https://api.elementssecure.com/v1"; async function getParameter(name: string, decrypt: boolean): Promise { const res = await ssm.send( new GetParameterCommand({ Name: name, WithDecryption: decrypt }) ); return res.Parameter!.Value!; } async function loadSecrets() { const [authToken, apiKey] = await Promise.all([ cachedAuthToken ?? getParameter(process.env.AUTH_TOKEN_PARAM!, true), cachedApiKey ?? getParameter(process.env.ELEMENTS_API_KEY_PARAM!, true), ]); cachedAuthToken = authToken; cachedApiKey = apiKey; return { authToken, apiKey }; } async function getLockdownStatus(lockdownId: string, apiKey: string): Promise { const response = await fetch(`${ELEMENTS_BASE_URL}/lockdowns/${lockdownId}`, { headers: { "api-key": apiKey }, }); if (!response.ok) { const body = await response.text(); throw new Error(`Elements status check failed: ${response.status} - ${body}`); } const data = await response.json() as Record; console.log(JSON.stringify({ action: "lockdown_raw_status", lockdownId, data })); return String(data.status).toLowerCase() === "active"; } async function setLockdown(lockdownId: string, apiKey: string, start: boolean): Promise { const action = start ? "start" : "stop"; const response = await fetch(`${ELEMENTS_BASE_URL}/lockdowns/${lockdownId}/${action}`, { method: "POST", headers: { "api-key": apiKey, "Content-Type": "application/json", }, body: JSON.stringify({}), }); if (!response.ok) { const body = await response.text(); throw new Error(`Elements ${action} failed: ${response.status} - ${body}`); } } function textScreenXml(title: string, text: string): string { return [ ``, ``, ` ${title}`, ` ${text}`, ``, ].join("\n"); } function xmlResponse(statusCode: number, body: string) { return { statusCode, headers: { "Content-Type": "application/xml" }, body, }; } export async function handler(event: { queryStringParameters?: Record; rawPath?: string; requestContext?: { http?: { sourceIp?: string } }; }) { const sourceIp = event.requestContext?.http?.sourceIp ?? "unknown"; const token = event.queryStringParameters?.token; const profile = event.queryStringParameters?.profile; const path = event.rawPath ?? ""; if (!token) { console.log(JSON.stringify({ action: "lockdown", status: "rejected", reason: "missing_token", sourceIp })); return { statusCode: 403, body: JSON.stringify({ error: "Forbidden" }) }; } let secrets; try { secrets = await loadSecrets(); } catch (err) { console.error(JSON.stringify({ action: "lockdown", status: "error", reason: "ssm_failure", sourceIp, error: String(err) })); return xmlResponse(500, textScreenXml("Error", "Internal error")); } if (!tokensMatch(token, secrets.authToken)) { console.log(JSON.stringify({ action: "lockdown", status: "rejected", reason: "invalid_token", sourceIp })); return { statusCode: 403, body: JSON.stringify({ error: "Forbidden" }) }; } if (path === "/lockdown/status") { return handleStatus(secrets.apiKey, sourceIp); } return handleToggle(profile, secrets.apiKey, sourceIp); } async function handleStatus(apiKey: string, sourceIp: string) { try { const lines: string[] = []; for (const [key, config] of Object.entries(LOCKDOWN_PROFILES)) { const active = await getLockdownStatus(config.id, apiKey); lines.push(`${config.name}: ${active ? "LOCKED DOWN" : "Normal"}`); console.log(JSON.stringify({ action: "lockdown_status", profile: key, active, sourceIp })); } return xmlResponse(200, textScreenXml("Lockdown Status", lines.join("\n"))); } catch (err) { console.error(JSON.stringify({ action: "lockdown_status", status: "error", sourceIp, error: String(err) })); return xmlResponse(502, textScreenXml("Error", "Unable to check lockdown status")); } } async function handleToggle(profile: string | undefined, apiKey: string, sourceIp: string) { if (!profile || !LOCKDOWN_PROFILES[profile]) { return xmlResponse(400, textScreenXml("Error", "Invalid profile")); } const config = LOCKDOWN_PROFILES[profile]; try { const wasActive = await getLockdownStatus(config.id, apiKey); await setLockdown(config.id, apiKey, !wasActive); const nowActive = !wasActive; console.log(JSON.stringify({ action: "lockdown_toggle", profile, wasActive, nowActive, sourceIp, })); const statusText = nowActive ? "LOCKED DOWN" : "Normal"; return xmlResponse(200, textScreenXml(config.name, statusText)); } catch (err) { console.error(JSON.stringify({ action: "lockdown_toggle", status: "error", profile, sourceIp, error: String(err) })); return xmlResponse(502, textScreenXml("Error", `Lockdown error: ${config.name}`)); } }