From 34133476601b1de8131e6b2ae11de72ae034f077 Mon Sep 17 00:00:00 2001 From: Jason Fischer Date: Sun, 18 Sep 2022 11:02:44 -0700 Subject: [PATCH] Add authentik Widget - disallow proxied client calls to set HTTP verbs for generic and credentiald proxies --- public/locales/en/common.json | 5 ++ src/components/services/widget.jsx | 2 + .../services/widgets/service/authentik.jsx | 47 +++++++++++++++++++ src/pages/api/services/proxy.js | 1 + src/utils/api-helpers.js | 1 + src/utils/proxies/credentialed.js | 4 +- src/utils/proxies/generic.js | 2 +- 7 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/components/services/widgets/service/authentik.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 26f2e432..fb4de8c5 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -168,5 +168,10 @@ "jackett": { "configured": "Configured", "errored": "Errored" + }, + "authentik": { + "users": "Users", + "loginsLast24H": "Logins (24h)", + "failedLoginsLast24H": "Failed Logins (24h)" } } diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx index 01dec306..03794e72 100644 --- a/src/components/services/widget.jsx +++ b/src/components/services/widget.jsx @@ -27,6 +27,7 @@ import Gotify from "./widgets/service/gotify"; import Prowlarr from "./widgets/service/prowlarr"; import Jackett from "./widgets/service/jackett"; import AdGuard from "./widgets/service/adguard"; +import Authentik from "./widgets/service/authentik"; const widgetMappings = { docker: Docker, @@ -56,6 +57,7 @@ const widgetMappings = { prowlarr: Prowlarr, jackett: Jackett, adguard: AdGuard, + authentik: Authentik, }; export default function Widget({ service }) { diff --git a/src/components/services/widgets/service/authentik.jsx b/src/components/services/widgets/service/authentik.jsx new file mode 100644 index 00000000..138f2d7f --- /dev/null +++ b/src/components/services/widgets/service/authentik.jsx @@ -0,0 +1,47 @@ +import useSWR from "swr"; +import { useTranslation } from "react-i18next"; + +import Widget from "../widget"; +import Block from "../block"; + +import { formatApiUrl } from "utils/api-helpers"; + +export default function Authentik({ service }) { + const { t } = useTranslation(); + + const config = service.widget; + + const { data: usersData, error: usersError } = useSWR(formatApiUrl(config, "core/users?page_size=1")); + const { data: loginsData, error: loginsError } = useSWR(formatApiUrl(config, "events/events/per_month/?action=login&query={}")); + const { data: failedLoginsData, error: failedLoginsError } = useSWR(formatApiUrl(config, "events/events/per_month/?action=login_failed&query={}")); + + if (usersError || loginsError || failedLoginsError) { + return ; + } + + if (!usersData || !loginsData || !failedLoginsData) { + return ( + + + + + + ); + } + + const yesterday = new Date(Date.now()).setHours(-24); + const loginsLast24H = loginsData.reduce((total, current) => { + return current.x_cord >= yesterday ? total + current.y_cord : total; + }, 0); + const failedLoginsLast24H = failedLoginsData.reduce((total, current) => { + return current.x_cord >= yesterday ? total + current.y_cord : total; + }, 0); + + return ( + + + + + + ); +} diff --git a/src/pages/api/services/proxy.js b/src/pages/api/services/proxy.js index 889b3849..60e37eb9 100644 --- a/src/pages/api/services/proxy.js +++ b/src/pages/api/services/proxy.js @@ -89,6 +89,7 @@ const serviceProxyHandlers = { ombi: credentialedProxyHandler, coinmarketcap: credentialedProxyHandler, prowlarr: credentialedProxyHandler, + authentik: credentialedProxyHandler, // super specific handlers rutorrent: rutorrentProxyHandler, nzbget: nzbgetProxyHandler, diff --git a/src/utils/api-helpers.js b/src/utils/api-helpers.js index be24f8a2..0b18046b 100644 --- a/src/utils/api-helpers.js +++ b/src/utils/api-helpers.js @@ -24,6 +24,7 @@ const formats = { prowlarr: `{url}/api/v1/{endpoint}`, jackett: `{url}/api/v2.0/{endpoint}?apikey={key}&configured=true`, adguard: `{url}/control/{endpoint}`, + authentik: `{url}/api/v3/{endpoint}`, }; export function formatApiCall(api, args) { diff --git a/src/utils/proxies/credentialed.js b/src/utils/proxies/credentialed.js index 736da48e..04a19c06 100644 --- a/src/utils/proxies/credentialed.js +++ b/src/utils/proxies/credentialed.js @@ -19,12 +19,14 @@ export default async function credentialedProxyHandler(req, res) { headers["X-CMC_PRO_API_KEY"] = `${widget.key}`; } else if (widget.type === "gotify") { headers["X-gotify-Key"] = `${widget.key}`; + } else if (widget.type === "authentik") { + headers["Authorization"] = `Bearer ${widget.key}`; } else { headers["X-API-Key"] = `${widget.key}`; } const [status, contentType, data] = await httpProxy(url, { - method: req.method, + method: "GET", // disallow a client SWR call to set a destructive HTTP verb withCredentials: true, credentials: "include", headers, diff --git a/src/utils/proxies/generic.js b/src/utils/proxies/generic.js index 41c013db..a3b4d50c 100644 --- a/src/utils/proxies/generic.js +++ b/src/utils/proxies/generic.js @@ -19,7 +19,7 @@ export default async function genericProxyHandler(req, res, maps) { } const [status, contentType, data] = await httpProxy(url, { - method: req.method, + method: "GET", // disallow a client SWR call to set a destructive HTTP verb headers, });