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 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..2949eaf 100644 --- a/server/scripts/ttn-webhook-dummy.py +++ b/server/scripts/ttn-webhook-dummy.py @@ -9,11 +9,16 @@ import json import argparse import random -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) + 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( @@ -24,6 +29,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, @@ -46,7 +56,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 @@ -67,7 +77,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}") @@ -78,7 +88,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}") @@ -91,11 +101,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 8fc7f66..0b0dbcc 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 @@ -21,7 +23,7 @@ const router = express.Router(); router.post( "/webhook", - validateData(ttnMessageValidator), + [authenticateHeader, validateData(ttnMessageValidator)], async (req: Request, res: Response) => { try { const message = req.body as TtnMessage; @@ -92,17 +94,17 @@ router.post( gnss: gnnsLocation.latitude && gnnsLocation.longitude ? { - latitude: gnnsLocation.latitude, - longitude: gnnsLocation.longitude, - } + latitude: gnnsLocation.latitude, + longitude: gnnsLocation.longitude, + } : undefined, }); }; 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