From 4c3587206455ea0c96aa3e015b1845e4531475d3 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Sierra Date: Mon, 12 Aug 2024 19:07:45 +0200 Subject: [PATCH 1/3] Added cookie support for auth on ESPHome widget --- docs/widgets/services/esphome.md | 1 + src/widgets/esphome/proxy.js | 68 ++++++++++++++++++++++++++++++++ src/widgets/esphome/widget.js | 4 +- 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/widgets/esphome/proxy.js diff --git a/docs/widgets/services/esphome.md b/docs/widgets/services/esphome.md index e14431fd8..3cdebdad0 100644 --- a/docs/widgets/services/esphome.md +++ b/docs/widgets/services/esphome.md @@ -16,4 +16,5 @@ To group both `offline` and `unknown` devices together, users should use the `of widget: type: esphome url: http://esphome.host.or.ip:port + cookie: authenticated=MYCOOKIE # Only for auth enabled, you can get the cookie value watching at a request from the frontend ``` diff --git a/src/widgets/esphome/proxy.js b/src/widgets/esphome/proxy.js new file mode 100644 index 000000000..c9cc6a117 --- /dev/null +++ b/src/widgets/esphome/proxy.js @@ -0,0 +1,68 @@ +import getServiceWidget from "utils/config/service-helpers"; +import { formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers"; +import validateWidgetData from "utils/proxy/validate-widget-data"; +import { httpProxy } from "utils/proxy/http"; +import createLogger from "utils/logger"; +import widgets from "widgets/widgets"; + +const logger = createLogger("espHomeProxyHandler"); + +export default async function espHomeProxyHandler(req, res, map) { + + const { group, service, endpoint } = req.query; + + if (group && service) { + const widget = await getServiceWidget(group, service); + + if (widget) { + const url = new URL( + formatApiCall(widgets[widget.type].api, { endpoint, ...widget }).replace(/(?<=\?.*)\?/g, "&"), + ); + + const params = { + method: widget.method ?? req.method, + headers: widget.headers.cookie ? { "Cookie": widget.headers.cookie } : {} + }; + + const [status, contentType, data] = await httpProxy(url, params); + + let resultData = data; + + if (resultData.error?.url) { + resultData.error.url = sanitizeErrorURL(url); + } + + if (status === 200) { + if (!validateWidgetData(widget, endpoint, resultData)) { + return res + .status(status) + .json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } }); + } + if (map) resultData = map(resultData); + } + + if (contentType) res.setHeader("Content-Type", contentType); + + if (status === 204 || status === 304) { + return res.status(status).end(); + } + + if (status >= 400) { + logger.debug( + "HTTP Error %d calling %s//%s%s%s...", + status, + url.protocol, + url.hostname, + url.port ? `:${url.port}` : "", + url.pathname, + ); + return res.status(status).json({ error: { message: "HTTP Error", url: sanitizeErrorURL(url), resultData } }); + } + + return res.status(status).send(resultData); + } + } + + logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); + return res.status(400).json({ error: "Invalid proxy service type" }); +} \ No newline at end of file diff --git a/src/widgets/esphome/widget.js b/src/widgets/esphome/widget.js index c5a87b682..e04f1cfc6 100644 --- a/src/widgets/esphome/widget.js +++ b/src/widgets/esphome/widget.js @@ -1,8 +1,8 @@ -import genericProxyHandler from "utils/proxy/handlers/generic"; +import espHomeProxyHandler from "./proxy"; const widget = { api: "{url}/ping", - proxyHandler: genericProxyHandler, + proxyHandler: espHomeProxyHandler, }; export default widget; From 55c4da61a7d77a1ea7c05305c017a946ed45bea1 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Sierra Date: Mon, 12 Aug 2024 19:26:59 +0200 Subject: [PATCH 2/3] Lint --- src/widgets/esphome/proxy.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/widgets/esphome/proxy.js b/src/widgets/esphome/proxy.js index c9cc6a117..6424893ab 100644 --- a/src/widgets/esphome/proxy.js +++ b/src/widgets/esphome/proxy.js @@ -8,7 +8,6 @@ import widgets from "widgets/widgets"; const logger = createLogger("espHomeProxyHandler"); export default async function espHomeProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; if (group && service) { @@ -18,10 +17,10 @@ export default async function espHomeProxyHandler(req, res, map) { const url = new URL( formatApiCall(widgets[widget.type].api, { endpoint, ...widget }).replace(/(?<=\?.*)\?/g, "&"), ); - + const params = { method: widget.method ?? req.method, - headers: widget.headers.cookie ? { "Cookie": widget.headers.cookie } : {} + headers: widget.cookie ? { Cookie: widget.cookie } : {}, }; const [status, contentType, data] = await httpProxy(url, params); @@ -65,4 +64,4 @@ export default async function espHomeProxyHandler(req, res, map) { logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); -} \ No newline at end of file +} From cd3648680816b5830564adafb44688c29d6b5168 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 12 Aug 2024 15:30:13 -0700 Subject: [PATCH 3/3] Use credentialedProxyHandler instead --- docs/widgets/services/esphome.md | 2 +- src/utils/proxy/handlers/credentialed.js | 4 ++ src/widgets/esphome/proxy.js | 67 ------------------------ src/widgets/esphome/widget.js | 4 +- 4 files changed, 7 insertions(+), 70 deletions(-) delete mode 100644 src/widgets/esphome/proxy.js diff --git a/docs/widgets/services/esphome.md b/docs/widgets/services/esphome.md index 3cdebdad0..07e511e37 100644 --- a/docs/widgets/services/esphome.md +++ b/docs/widgets/services/esphome.md @@ -16,5 +16,5 @@ To group both `offline` and `unknown` devices together, users should use the `of widget: type: esphome url: http://esphome.host.or.ip:port - cookie: authenticated=MYCOOKIE # Only for auth enabled, you can get the cookie value watching at a request from the frontend + key: myesphomecookie # only if auth enabled, get the value from a request from the frontend e.g. `authenticated=myesphomecookie` ``` diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 870d5b155..f09d0fe94 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -84,6 +84,10 @@ export default async function credentialedProxyHandler(req, res, map) { headers.Key = `${widget.key}`; } else if (widget.type === "myspeed") { headers.Password = `${widget.password}`; + } else if (widget.type === "esphome") { + if (widget.key) { + headers.Cookie = `authenticated=${widget.key}`; + } } else { headers["X-API-Key"] = `${widget.key}`; } diff --git a/src/widgets/esphome/proxy.js b/src/widgets/esphome/proxy.js deleted file mode 100644 index 6424893ab..000000000 --- a/src/widgets/esphome/proxy.js +++ /dev/null @@ -1,67 +0,0 @@ -import getServiceWidget from "utils/config/service-helpers"; -import { formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers"; -import validateWidgetData from "utils/proxy/validate-widget-data"; -import { httpProxy } from "utils/proxy/http"; -import createLogger from "utils/logger"; -import widgets from "widgets/widgets"; - -const logger = createLogger("espHomeProxyHandler"); - -export default async function espHomeProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; - - if (group && service) { - const widget = await getServiceWidget(group, service); - - if (widget) { - const url = new URL( - formatApiCall(widgets[widget.type].api, { endpoint, ...widget }).replace(/(?<=\?.*)\?/g, "&"), - ); - - const params = { - method: widget.method ?? req.method, - headers: widget.cookie ? { Cookie: widget.cookie } : {}, - }; - - const [status, contentType, data] = await httpProxy(url, params); - - let resultData = data; - - if (resultData.error?.url) { - resultData.error.url = sanitizeErrorURL(url); - } - - if (status === 200) { - if (!validateWidgetData(widget, endpoint, resultData)) { - return res - .status(status) - .json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } }); - } - if (map) resultData = map(resultData); - } - - if (contentType) res.setHeader("Content-Type", contentType); - - if (status === 204 || status === 304) { - return res.status(status).end(); - } - - if (status >= 400) { - logger.debug( - "HTTP Error %d calling %s//%s%s%s...", - status, - url.protocol, - url.hostname, - url.port ? `:${url.port}` : "", - url.pathname, - ); - return res.status(status).json({ error: { message: "HTTP Error", url: sanitizeErrorURL(url), resultData } }); - } - - return res.status(status).send(resultData); - } - } - - logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); - return res.status(400).json({ error: "Invalid proxy service type" }); -} diff --git a/src/widgets/esphome/widget.js b/src/widgets/esphome/widget.js index e04f1cfc6..5a628ad88 100644 --- a/src/widgets/esphome/widget.js +++ b/src/widgets/esphome/widget.js @@ -1,8 +1,8 @@ -import espHomeProxyHandler from "./proxy"; +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; const widget = { api: "{url}/ping", - proxyHandler: espHomeProxyHandler, + proxyHandler: credentialedProxyHandler, }; export default widget;