add token auth

This commit is contained in:
JMARyA 2024-09-08 02:37:39 +02:00
parent 9e9c48e5dd
commit 21fe567ba6
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
9 changed files with 152 additions and 13 deletions

1
config.toml Normal file
View file

@ -0,0 +1 @@
allowed_tokens = ["password"]

View file

@ -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"

23
src/config.rs Normal file
View file

@ -0,0 +1,23 @@
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
/// Allowed tokens for access
pub allowed_tokens: Vec<String>,
}
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()
}

View file

@ -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)
}

View file

@ -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 = "<f>")]
pub async fn demand_route(f: Json<DemandForm>) -> FallibleApiResponse {
pub async fn demand_route(f: Json<DemandForm>, t: Token, c: &State<Config>) -> 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<ItemDB>,
t: Token,
c: &State<Config>,
) -> FallibleApiResponse {
check_auth!(t, c);
let variant = itemdb
.get_item(item_id)
.ok_or_else(item_does_not_exist_error)?

View file

@ -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/<id>")]
pub async fn location_info(
id: &str,
locations: &State<JSONStore<Location>>,
t: Token,
c: &State<Config>,
) -> 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<JSONStore<Location>>) -> FallibleApiResponse {
pub async fn locations_list(
locations: &State<JSONStore<Location>>,
t: Token,
c: &State<Config>,
) -> 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<JSONStore<Location>>) -> FallibleA
}
#[get("/location_map")]
pub async fn locations_info(locations: &State<JSONStore<Location>>) -> FallibleApiResponse {
pub async fn locations_info(
locations: &State<JSONStore<Location>>,
t: Token,
c: &State<Config>,
) -> FallibleApiResponse {
check_auth!(t, c);
let mut root_locations = HashMap::new();
for loc in locations.inner().deref() {

View file

@ -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<ItemDB>) -> serde_json::Value {
pub fn get_items_route(itemdb: &State<ItemDB>, t: Token, c: &State<Config>) -> 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/<item_id>")]
pub fn item_route(item_id: &str, itemdb: &State<ItemDB>) -> FallibleApiResponse {
pub fn item_route(
item_id: &str,
itemdb: &State<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>) -> FallibleApiResponse
/// Returns all variants of an Item
#[get("/item/<item_id>/variants")]
pub fn item_variants_page(item_id: &str, itemdb: &State<ItemDB>) -> FallibleApiResponse {
pub fn item_variants_page(
item_id: &str,
itemdb: &State<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>) -> FallibleApiR
/// Returns an API Response for a `Transaction`
#[get("/transaction/<transaction>")]
pub async fn transaction_route(transaction: &str) -> FallibleApiResponse {
pub async fn transaction_route(
transaction: &str,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>,
t: Token,
c: &State<Config>,
) -> FallibleApiResponse {
check_auth!(t, c);
let variant = itemdb
.get_item(item_id)
.ok_or_else(item_does_not_exist_error)?

View file

@ -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 = "<form>")]
pub async fn supply_route(form: Json<SupplyForm>, itemdb: &State<ItemDB>) -> FallibleApiResponse {
println!("{form:?}");
pub async fn supply_route(
form: Json<SupplyForm>,
itemdb: &State<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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/<item_id>/inventory")]
pub async fn inventory_route(item_id: &str, itemdb: &State<ItemDB>) -> FallibleApiResponse {
pub async fn inventory_route(
item_id: &str,
itemdb: &State<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>,
t: Token,
c: &State<Config>,
) -> 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<ItemDB>,
t: Token,
c: &State<Config>,
) -> FallibleApiResponse {
check_auth!(t, c);
let variant = itemdb
.get_item(item_id)
.ok_or_else(item_does_not_exist_error)?

View file

@ -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<Self, Self::Error> {
match request.headers().get_one("token") {
Some(key) => Outcome::Success(Token(key.to_string())),
None => Outcome::Error((Status::Unauthorized, ())),
}
}
}