From 21fe567ba6fce636b787a6f4a076dd6541a16a0a Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sun, 8 Sep 2024 02:37:39 +0200 Subject: [PATCH] add token auth --- config.toml | 1 + docker-compose.yml | 2 ++ src/config.rs | 23 ++++++++++++++++++++++ src/main.rs | 4 ++++ src/routes/item/demand.rs | 11 ++++++++++- src/routes/item/location.rs | 24 ++++++++++++++++++++--- src/routes/item/mod.rs | 39 ++++++++++++++++++++++++++++++++----- src/routes/item/supply.rs | 34 +++++++++++++++++++++++++++++--- src/routes/mod.rs | 27 ++++++++++++++++++++++++- 9 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 config.toml create mode 100644 src/config.rs diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..a686620 --- /dev/null +++ b/config.toml @@ -0,0 +1 @@ +allowed_tokens = ["password"] diff --git a/docker-compose.yml b/docker-compose.yml index 6525c06..9fd2fb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,8 @@ services: - mongodb volumes: - ./itemdb/items:/itemdb + - ./locations:/locations + - ./config.toml:/config.toml environment: - "DB_URI=mongodb://user:pass@mongodb:27017" - "DB=cdb" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..c3d59e1 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,23 @@ +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +pub struct Config { + /// Allowed tokens for access + pub allowed_tokens: Vec, +} + +impl Default for Config { + fn default() -> Self { + Self { + allowed_tokens: Vec::new(), + } + } +} + +pub fn get_config() -> Config { + if let Ok(content) = std::fs::read_to_string("./config.toml") { + return toml::from_str(&content).unwrap(); + } + + Config::default() +} diff --git a/src/main.rs b/src/main.rs index 319f5d5..7606968 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use location::Location; use rocket::routes as route; use rocket::{http::Method, launch}; +mod config; mod db; mod item; mod json_store; @@ -54,6 +55,8 @@ async fn rocket() -> _ { location.1.clone().add(location.0).await; } + let config = config::get_config(); + rocket::build() .mount( "/", @@ -77,5 +80,6 @@ async fn rocket() -> _ { ) .manage(itemdb) .manage(locations) + .manage(config) .attach(cors) } diff --git a/src/routes/item/demand.rs b/src/routes/item/demand.rs index a068302..b6b81a4 100644 --- a/src/routes/item/demand.rs +++ b/src/routes/item/demand.rs @@ -3,6 +3,9 @@ use rocket::{get, post, State}; use serde::Deserialize; use serde_json::json; +use crate::check_auth; +use crate::config::Config; +use crate::routes::Token; use crate::variant::Variant; use crate::{ db::ItemDB, @@ -20,7 +23,9 @@ pub struct DemandForm { /// Consumes a Transaction with Price and Destination #[post("/demand", data = "")] -pub async fn demand_route(f: Json) -> FallibleApiResponse { +pub async fn demand_route(f: Json, t: Token, c: &State) -> FallibleApiResponse { + check_auth!(t, c); + Variant::demand( &f.uuid, f.price @@ -41,7 +46,11 @@ pub async fn demand_log_route( item_id: &str, variant_id: &str, itemdb: &State, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)? diff --git a/src/routes/item/location.rs b/src/routes/item/location.rs index 99b7e0a..3dfa161 100644 --- a/src/routes/item/location.rs +++ b/src/routes/item/location.rs @@ -5,16 +5,22 @@ use rocket::{get, State}; use serde_json::json; use crate::{ + check_auth, + config::Config, json_store::JSONStore, location::Location, - routes::{api_error, FallibleApiResponse}, + routes::{api_error, FallibleApiResponse, Token}, }; #[get("/location/")] pub async fn location_info( id: &str, locations: &State>, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let loc = locations .get(id) .ok_or_else(|| api_error("No location with that ID"))?; @@ -49,7 +55,13 @@ async fn location_api_resolve( } #[get("/locations")] -pub async fn locations_list(locations: &State>) -> FallibleApiResponse { +pub async fn locations_list( + locations: &State>, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let mut lst = Vec::with_capacity(locations.len()); for id in locations.deref().keys() { @@ -60,7 +72,13 @@ pub async fn locations_list(locations: &State>) -> FallibleA } #[get("/location_map")] -pub async fn locations_info(locations: &State>) -> FallibleApiResponse { +pub async fn locations_info( + locations: &State>, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let mut root_locations = HashMap::new(); for loc in locations.inner().deref() { diff --git a/src/routes/item/mod.rs b/src/routes/item/mod.rs index 3e8cd24..1790c7a 100644 --- a/src/routes/item/mod.rs +++ b/src/routes/item/mod.rs @@ -14,7 +14,10 @@ use rocket::get; use rocket::State; use serde_json::json; +use crate::check_auth; +use crate::config::Config; use crate::db::ItemDB; +use crate::routes::Token; use crate::transaction::Transaction; use super::api_error; @@ -22,14 +25,23 @@ use crate::routes::FallibleApiResponse; /// Returns a JSON response with all items in the database. #[get("/items")] -pub fn get_items_route(itemdb: &State) -> serde_json::Value { +pub fn get_items_route(itemdb: &State, t: Token, c: &State) -> FallibleApiResponse { + check_auth!(t, c); + let items = itemdb.items(); - json!({"items": items}) + Ok(json!({"items": items})) } /// Return an API Response for an `Item` #[get("/item/")] -pub fn item_route(item_id: &str, itemdb: &State) -> FallibleApiResponse { +pub fn item_route( + item_id: &str, + itemdb: &State, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let item = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)?; @@ -38,7 +50,14 @@ pub fn item_route(item_id: &str, itemdb: &State) -> FallibleApiResponse /// Returns all variants of an Item #[get("/item//variants")] -pub fn item_variants_page(item_id: &str, itemdb: &State) -> FallibleApiResponse { +pub fn item_variants_page( + item_id: &str, + itemdb: &State, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let item = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)?; @@ -52,7 +71,13 @@ pub fn item_variants_page(item_id: &str, itemdb: &State) -> FallibleApiR /// Returns an API Response for a `Transaction` #[get("/transaction/")] -pub async fn transaction_route(transaction: &str) -> FallibleApiResponse { +pub async fn transaction_route( + transaction: &str, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let t = Transaction::get(transaction) .await .ok_or_else(|| api_error("No transaction with this UUID"))?; @@ -66,7 +91,11 @@ pub async fn unique_field_route( variant_id: &str, field: &str, itemdb: &State, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)? diff --git a/src/routes/item/supply.rs b/src/routes/item/supply.rs index ad9537c..8984f23 100644 --- a/src/routes/item/supply.rs +++ b/src/routes/item/supply.rs @@ -3,6 +3,9 @@ use rocket::{get, post, State}; use serde::Deserialize; use serde_json::json; +use crate::check_auth; +use crate::config::Config; +use crate::routes::Token; use crate::{ db::ItemDB, routes::{api_error, FallibleApiResponse}, @@ -21,8 +24,14 @@ pub struct SupplyForm { /// Route for supply action. Creates a new Transaction for the specified Item Variant. #[post("/supply", data = "
")] -pub async fn supply_route(form: Json, itemdb: &State) -> FallibleApiResponse { - println!("{form:?}"); +pub async fn supply_route( + form: Json, + itemdb: &State, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(&form.item) .ok_or_else(item_does_not_exist_error)? @@ -49,7 +58,11 @@ pub async fn supply_log_route( item_id: &str, variant_id: &str, itemdb: &State, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)? @@ -63,7 +76,14 @@ pub async fn supply_log_route( /// Returns current active Transactions for Item #[get("/item//inventory")] -pub async fn inventory_route(item_id: &str, itemdb: &State) -> FallibleApiResponse { +pub async fn inventory_route( + item_id: &str, + itemdb: &State, + t: Token, + c: &State, +) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)?; @@ -79,7 +99,11 @@ pub async fn inventory_route_variant( item_id: &str, variant_id: &str, itemdb: &State, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)? @@ -97,7 +121,11 @@ pub async fn variant_stat_route( item_id: &str, variant_id: &str, itemdb: &State, + t: Token, + c: &State, ) -> FallibleApiResponse { + check_auth!(t, c); + let variant = itemdb .get_item(item_id) .ok_or_else(item_does_not_exist_error)? diff --git a/src/routes/mod.rs b/src/routes/mod.rs index be6d9ca..5242207 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,4 +1,6 @@ -use rocket::response::status::BadRequest; +use rocket::{ + http::Status, outcome::Outcome, request::FromRequest, response::status::BadRequest, Request, +}; use serde_json::json; pub mod item; @@ -11,3 +13,26 @@ fn api_error(msg: &str) -> ApiError { "error": msg })) } + +pub struct Token(pub String); + +#[macro_export] +macro_rules! check_auth { + ($t:ident, $c:ident) => { + if !$c.allowed_tokens.contains(&$t.0) { + return Err(api_error("Unauthorized")); + } + }; +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Token { + type Error = (); + + async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { + match request.headers().get_one("token") { + Some(key) => Outcome::Success(Token(key.to_string())), + None => Outcome::Error((Status::Unauthorized, ())), + } + } +}