From 4896c63b1a55ccbc8432338bf5f3c3894777a21a2ee2dd0541697518e1f1cb61 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Jan 2025 21:45:09 +0100 Subject: [PATCH 1/5] first basic auth --- server/.env.template | 1 + server/scripts/ttn-webhook-dummy.py | 9 ++++++- server/src/controller/ttnController.ts | 36 +++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/server/.env.template b/server/.env.template index 9740f4c..7d3ca64 100644 --- a/server/.env.template +++ b/server/.env.template @@ -4,6 +4,7 @@ DB_PASSWORD="" DB_HOST="" DB_DIALECT="" DB_PORT="" +WEBHOOK_TOKEN="" #Token that is placed a the TTN Webhook auth WIGLE_TOKEN="" # Go to account and generate token "Encoded for use" WIGLE_BASE_URL="https://api.wigle.net" WIGLE_NETWORK_SEARCH="/api/v2/network/search" diff --git a/server/scripts/ttn-webhook-dummy.py b/server/scripts/ttn-webhook-dummy.py index ac1c4a2..2d9ad8e 100644 --- a/server/scripts/ttn-webhook-dummy.py +++ b/server/scripts/ttn-webhook-dummy.py @@ -9,9 +9,16 @@ import json import argparse import random +token = "ich-bin-da-token" + +headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", # Adjust if needed for your payload format +} + def send_post_request(uri, data): try: - requests.post(uri, json=data, timeout=1) + requests.post(uri, json=data, timeout=1, headers=headers) except requests.exceptions.RequestException as e: pass diff --git a/server/src/controller/ttnController.ts b/server/src/controller/ttnController.ts index 606d9de..07281ae 100644 --- a/server/src/controller/ttnController.ts +++ b/server/src/controller/ttnController.ts @@ -24,6 +24,36 @@ router.post( "/webhook", validateData(ttnMessageValidator), async (req: Request, res: Response) => { + try { + const authorizationHeader = req.headers['authorization']; + if (!authorizationHeader) { + console.log("Authorization header is missing!"); + res.status(401).json({ error: "Authentication failed" }); + return; + } else { + const token = authorizationHeader.split(' ')[1]; // Get the token after 'Bearer' + if (!token) { + console.log("Bearer token is missing!"); + res.status(401).json({ error: "Authentication failed" }); + return; + } + else { + console.log(token) + if (token !== process.env.WEBHOOK_TOKEN) { + console.log("Bearer token is wrong!"); + res.status(401).json({ error: "Authentication failed" }); + return; + } else { + console.log("Bearer token is correct!"); + } + } + } + } catch (error) { + console.log(error); + res.status(401).json({ error: "Authentication failed" }); + return; + } + try { const message = req.body as TtnMessage; @@ -96,9 +126,9 @@ router.post( gnss: gnnsLocation.latitude && gnnsLocation.longitude ? { - latitude: gnnsLocation.latitude, - longitude: gnnsLocation.longitude, - } + latitude: gnnsLocation.latitude, + longitude: gnnsLocation.longitude, + } : undefined, }); }; -- 2.47.1 From 503bb22ea31f9c224a9f7c1e11511e51db86381e6cff77598173433de4f5e894 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Jan 2025 22:10:00 +0100 Subject: [PATCH 2/5] cleanup --- server/scripts/ttn-webhook-dummy.py | 27 +++++++-------- server/src/controller/ttnController.ts | 46 ++++++++++++++------------ 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/server/scripts/ttn-webhook-dummy.py b/server/scripts/ttn-webhook-dummy.py index 2d9ad8e..94c5299 100644 --- a/server/scripts/ttn-webhook-dummy.py +++ b/server/scripts/ttn-webhook-dummy.py @@ -9,14 +9,11 @@ import json import argparse import random -token = "ich-bin-da-token" - -headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", # Adjust if needed for your payload format -} - -def send_post_request(uri, data): +def send_post_request(uri, data, token): + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + } try: requests.post(uri, json=data, timeout=1, headers=headers) except requests.exceptions.RequestException as e: @@ -31,6 +28,11 @@ def main(): type=str, help="The URI to send POST requests to (e.g., http://127.0.0.1:8080/api)", ) + parser.add_argument( + "token", + type=str, + help="Bearer authorization token)", + ) parser.add_argument( "directory", type=str, @@ -53,7 +55,7 @@ def main(): try: data = json.load(file) print(f"Sending {args.directory} to {args.uri}") - send_post_request(args.uri, data) + send_post_request(args.uri, data, args.token) except json.JSONDecodeError as e: print(f"Error reading {args.directory}: {e}") return @@ -74,7 +76,7 @@ def main(): try: data = json.load(file) print(f"Sending {filename} to {args.uri}") - send_post_request(args.uri, data) + send_post_request(args.uri, data, args.token) except json.JSONDecodeError as e: print(f"Error reading {filename}: {e}") @@ -85,7 +87,7 @@ def main(): try: data = json.load(file) print(f"Sending {filename} to {args.uri}") - send_post_request(args.uri, data) + send_post_request(args.uri, data, args.token) input("Press Enter to send the next file...") except json.JSONDecodeError as e: print(f"Error reading {filename}: {e}") @@ -98,11 +100,10 @@ def main(): try: data = json.load(file) print(f"Sending {filename} to {args.uri}") - send_post_request(args.uri, data) + send_post_request(args.uri, data, args.token) input("Press Enter to send another random file...") except json.JSONDecodeError as e: print(f"Error reading {filename}: {e}") - if __name__ == "__main__": main() diff --git a/server/src/controller/ttnController.ts b/server/src/controller/ttnController.ts index 07281ae..21f723b 100644 --- a/server/src/controller/ttnController.ts +++ b/server/src/controller/ttnController.ts @@ -20,38 +20,42 @@ const locationService = container.resolve(LocationService); const router = express.Router(); +const validateBearerToken = (authorizationHeader: string | undefined): boolean => { + if (!authorizationHeader) { + console.log("Authorization header is missing!"); + return false; + } + + const token = authorizationHeader.split(' ')[1]; // Extract token after 'Bearer' + if (!token) { + console.log("Bearer token is missing!"); + return false; + } + + if (token !== process.env.WEBHOOK_TOKEN) { + console.log("Bearer token is incorrect!"); + return false; + } + + return true; +}; + router.post( "/webhook", validateData(ttnMessageValidator), async (req: Request, res: Response) => { try { const authorizationHeader = req.headers['authorization']; - if (!authorizationHeader) { - console.log("Authorization header is missing!"); + + if (!validateBearerToken(authorizationHeader as string)) { res.status(401).json({ error: "Authentication failed" }); return; - } else { - const token = authorizationHeader.split(' ')[1]; // Get the token after 'Bearer' - if (!token) { - console.log("Bearer token is missing!"); - res.status(401).json({ error: "Authentication failed" }); - return; - } - else { - console.log(token) - if (token !== process.env.WEBHOOK_TOKEN) { - console.log("Bearer token is wrong!"); - res.status(401).json({ error: "Authentication failed" }); - return; - } else { - console.log("Bearer token is correct!"); - } - } } + console.log("Bearer token is correct!"); + } catch (error) { - console.log(error); + console.error("Error during authentication:", error); res.status(401).json({ error: "Authentication failed" }); - return; } try { -- 2.47.1 From 41ab1372708d5c29ebd1a3fa27a8825116fe859b217dde328cc8b47881446aa0 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Jan 2025 22:10:44 +0100 Subject: [PATCH 3/5] cleanup --- server/src/controller/ttnController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/controller/ttnController.ts b/server/src/controller/ttnController.ts index 21f723b..448e9b2 100644 --- a/server/src/controller/ttnController.ts +++ b/server/src/controller/ttnController.ts @@ -51,7 +51,7 @@ router.post( res.status(401).json({ error: "Authentication failed" }); return; } - console.log("Bearer token is correct!"); + //console.log("Bearer token is correct!"); } catch (error) { console.error("Error during authentication:", error); -- 2.47.1 From 2c94b7fb7e45d85d5a36fd5c94b6454c5920048efa6682b11bf8212a86ca8d67 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 5 Jan 2025 22:13:42 +0100 Subject: [PATCH 4/5] update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e77d4fb..c716e66 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ TODO - Create new user for database: `GRANT ALL PRIVILEGES ON dev_locationhub.* TO 'dbuser'@'localhost' IDENTIFIED BY '1234';` - Import tables: `/usr/bin/mariadb -u dbuser -p1234 dev_locationhub < server/sql/tables.sql` +### TTN Integration +Create new Webhook for application. Set base url and enable "Uplink message" to api `/api/ttn/webhook`. +Add a addidtional header: +- Type: `authorization` +- Value: `Bearer your-very-secure-token` + ### Testing Webhook - To test the webhook use the python script `ttn-webhook-dummy.py` to send prerecorded TTN Uplinks. - To test the script you can use `while true; do echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nSuccess"; nc -l -p 8080 -q 1; done` \ No newline at end of file -- 2.47.1 From dca88c26a4741ae58c222e6416c3c4448831f15f92e2fc1d1476382ca8c769e0 Mon Sep 17 00:00:00 2001 From: localhorst Date: Mon, 6 Jan 2025 20:07:55 +0100 Subject: [PATCH 5/5] switch to middleware validation --- server/scripts/ttn-webhook-dummy.py | 5 ++- server/src/controller/ttnController.ts | 42 +++---------------- .../middleware/authentificationMiddleware.ts | 42 +++++++++++++++++++ server/src/middleware/validationMiddleware.ts | 2 +- 4 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 server/src/middleware/authentificationMiddleware.ts diff --git a/server/scripts/ttn-webhook-dummy.py b/server/scripts/ttn-webhook-dummy.py index 94c5299..2949eaf 100644 --- a/server/scripts/ttn-webhook-dummy.py +++ b/server/scripts/ttn-webhook-dummy.py @@ -15,9 +15,10 @@ def send_post_request(uri, data, token): "Content-Type": "application/json", } try: - requests.post(uri, json=data, timeout=1, headers=headers) + response = requests.post(uri, json=data, timeout=1, headers=headers) + print("Return code: " + str(response.status_code)) except requests.exceptions.RequestException as e: - pass + print(e) def main(): parser = argparse.ArgumentParser( diff --git a/server/src/controller/ttnController.ts b/server/src/controller/ttnController.ts index 448e9b2..a59b1d7 100644 --- a/server/src/controller/ttnController.ts +++ b/server/src/controller/ttnController.ts @@ -7,6 +7,8 @@ import { LpTtnEndDeviceUplinksService } from "../services/lpTtnEndDeviceUplinksS import { TtnGatewayReceptionService } from "../services/ttnGatewayReceptionService"; import { WifiScanService } from "../services/wifiScanService"; import { ttnMessageValidator } from "../validation/ttn/ttnMessageValidation"; +import { authenticateHeader } from "../middleware/authentificationMiddleware"; +import { StatusCodes } from "http-status-codes"; const lpTtnEndDeviceUplinksService = container.resolve( LpTtnEndDeviceUplinksService @@ -20,44 +22,10 @@ const locationService = container.resolve(LocationService); const router = express.Router(); -const validateBearerToken = (authorizationHeader: string | undefined): boolean => { - if (!authorizationHeader) { - console.log("Authorization header is missing!"); - return false; - } - - const token = authorizationHeader.split(' ')[1]; // Extract token after 'Bearer' - if (!token) { - console.log("Bearer token is missing!"); - return false; - } - - if (token !== process.env.WEBHOOK_TOKEN) { - console.log("Bearer token is incorrect!"); - return false; - } - - return true; -}; - router.post( "/webhook", - validateData(ttnMessageValidator), + [authenticateHeader, validateData(ttnMessageValidator)], async (req: Request, res: Response) => { - try { - const authorizationHeader = req.headers['authorization']; - - if (!validateBearerToken(authorizationHeader as string)) { - res.status(401).json({ error: "Authentication failed" }); - return; - } - //console.log("Bearer token is correct!"); - - } catch (error) { - console.error("Error during authentication:", error); - res.status(401).json({ error: "Authentication failed" }); - } - try { const message = req.body as TtnMessage; @@ -137,10 +105,10 @@ router.post( }); }; createDatabaseEntries().then(); - res.status(200); + res.status(StatusCodes.OK).send(); } catch (error) { console.log(error); - res.status(500).json({ error: "Error creating uplink" }); + res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: "Error creating uplink" }); } } ); diff --git a/server/src/middleware/authentificationMiddleware.ts b/server/src/middleware/authentificationMiddleware.ts new file mode 100644 index 0000000..a6629a4 --- /dev/null +++ b/server/src/middleware/authentificationMiddleware.ts @@ -0,0 +1,42 @@ +import { NextFunction, Request, Response } from "express"; +import { StatusCodes } from "http-status-codes"; + +const validateBearerToken = (authorizationHeader: string | undefined): boolean => { + if (!authorizationHeader) { + console.log("Authorization header is missing!"); + return false; + } + + const token = authorizationHeader.split(' ')[1]; // Extract token after 'Bearer' + if (!token) { + console.log("Bearer token is missing!"); + return false; + } + + if (token !== process.env.WEBHOOK_TOKEN) { + console.log("Bearer token is incorrect!"); + return false; + } + + return true; +}; + +export function authenticateHeader(req: Request, res: Response, next: NextFunction) { + try { + const authorizationHeader = req.headers['authorization']; + + if (!validateBearerToken(authorizationHeader as string)) { + res.status(StatusCodes.UNAUTHORIZED).json({ error: "Authentication failed" }); + return; + } + console.log("Bearer token is correct!"); + + next(); + } catch (error) { + res.status(StatusCodes.INTERNAL_SERVER_ERROR) + .json({ error: "Internal Server Error" }); + } +}; + + + diff --git a/server/src/middleware/validationMiddleware.ts b/server/src/middleware/validationMiddleware.ts index 3f5288b..8dbeaf2 100644 --- a/server/src/middleware/validationMiddleware.ts +++ b/server/src/middleware/validationMiddleware.ts @@ -22,4 +22,4 @@ export function validateData(schema: z.ZodObject) { } } }; -} +} \ No newline at end of file -- 2.47.1