From d8060da1180545df5d03a76cd2860191ecf87507 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Mon, 8 Apr 2024 10:42:47 +0200 Subject: Closes #23. Start request id parameter put in path --- src/routes/device.rs | 4 +-- src/routes/start.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 8 deletions(-) (limited to 'src/routes') diff --git a/src/routes/device.rs b/src/routes/device.rs index d01d9f0..bbc832d 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -20,7 +20,7 @@ use utoipa::ToSchema; security(("api_key" = [])) )] #[deprecated] -pub async fn get( +pub async fn get_payload( State(state): State>, Json(payload): Json, ) -> Result, Error> { @@ -53,7 +53,7 @@ pub async fn get( ), security(("api_key" = [])) )] -pub async fn get_path( +pub async fn get( State(state): State>, Path(path): Path, ) -> Result, Error> { diff --git a/src/routes/start.rs b/src/routes/start.rs index ef6e8f2..fa226d8 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -2,27 +2,28 @@ use crate::db::Device; use crate::error::Error; use crate::services::ping::Value as PingValue; use crate::wol::{create_buffer, send_packet}; -use axum::extract::State; +use axum::extract::{Path, State}; use axum::Json; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use utoipa::ToSchema; use std::sync::Arc; use tracing::{debug, info}; +use utoipa::ToSchema; use uuid::Uuid; #[utoipa::path( post, path = "/start", - request_body = Payload, + request_body = PayloadOld, responses( (status = 200, description = "List matching todos by query", body = [Response]) ), security(("api_key" = [])) )] -pub async fn start( +#[deprecated] +pub async fn start_payload( State(state): State>, - Json(payload): Json, + Json(payload): Json, ) -> Result, Error> { info!("POST request"); let device = sqlx::query_as!( @@ -59,6 +60,63 @@ pub async fn start( }))) } +#[utoipa::path( + post, + path = "/start/{id}", + request_body = Payload, + responses( + (status = 200, description = "Start the device with the given id", body = [Response]) + ), + params( + ("id" = String, Path, description = "Device id") + ), + security(("api_key" = [])) +)] +pub async fn start( + State(state): State>, + Path(id): Path, + payload: Option>, +) -> Result, Error> { + info!("Start request for {id}"); + let device = sqlx::query_as!( + Device, + r#" + SELECT id, mac, broadcast_addr, ip, times + FROM devices + WHERE id = $1; + "#, + id + ) + .fetch_one(&state.db) + .await?; + + info!("starting {}", device.id); + + let bind_addr = "0.0.0.0:0"; + + let _ = send_packet( + bind_addr, + &device.broadcast_addr, + &create_buffer(&device.mac.to_string())?, + )?; + let dev_id = device.id.clone(); + let uuid = if let Some(pl) = payload { + if pl.ping.is_some_and(|ping| ping) { + Some(setup_ping(state, device)) + } else { + None + } + } else { + None + }; + + Ok(Json(json!(Response { + id: dev_id, + boot: true, + uuid + }))) +} + fn setup_ping(state: Arc, device: Device) -> String { let mut uuid: Option = None; for (key, value) in state.ping_map.clone() { @@ -99,11 +157,17 @@ fn setup_ping(state: Arc, device: Device) -> String { } #[derive(Deserialize, ToSchema)] -pub struct Payload { +#[deprecated] +pub struct PayloadOld { id: String, ping: Option, } +#[derive(Deserialize, ToSchema)] +pub struct Payload { + ping: Option, +} + #[derive(Serialize, ToSchema)] pub struct Response { id: String, -- cgit v1.2.3 From 52851787329c48c1e70f98a3610ad52fe1fa4aa4 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Mon, 8 Apr 2024 15:14:21 +0200 Subject: Closes #25. Apikey not required anymore --- src/auth.rs | 35 +++++++++++++++++++++++++++++++++++ src/config.rs | 10 +++++++++- src/extractors.rs | 24 ------------------------ src/main.rs | 4 ++-- src/routes/start.rs | 2 +- 5 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 src/auth.rs delete mode 100644 src/extractors.rs (limited to 'src/routes') diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..1f4518a --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,35 @@ +use crate::AppState; +use axum::{ + extract::{Request, State}, + http::{HeaderMap, StatusCode}, + middleware::Next, + response::Response, +}; +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +pub enum Methods { + Key, + None, +} + +pub async fn auth( + State(state): State, + headers: HeaderMap, + request: Request, + next: Next, +) -> Result { + let auth = state.config.auth; + match auth.method { + Methods::Key => { + if let Some(secret) = headers.get("authorization") { + if !(auth.secret.as_str() == secret) { return Err(StatusCode::UNAUTHORIZED); }; + let response = next.run(request).await; + Ok(response) + } else { + return Err(StatusCode::UNAUTHORIZED); + } + } + Methods::None => Ok(next.run(request).await), + } +} diff --git a/src/config.rs b/src/config.rs index 9605361..9636af4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,14 +1,22 @@ use config::File; use serde::Deserialize; +use crate::auth; + #[derive(Debug, Clone, Deserialize)] pub struct Config { pub database_url: String, - pub apikey: String, pub serveraddr: String, pub pingtimeout: i64, pub pingthreshold: i64, pub timeoffset: i8, + pub auth: Auth, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Auth { + pub method: auth::Methods, + pub secret: String, } impl Config { diff --git a/src/extractors.rs b/src/extractors.rs deleted file mode 100644 index 4d441e9..0000000 --- a/src/extractors.rs +++ /dev/null @@ -1,24 +0,0 @@ -use axum::{ - extract::{Request, State}, - http::{HeaderMap, StatusCode}, - middleware::Next, - response::Response, -}; - -use crate::AppState; - -pub async fn auth( - State(state): State, - headers: HeaderMap, - request: Request, - next: Next, -) -> Result { - let secret = headers.get("authorization"); - match secret { - Some(token) if token == state.config.apikey.as_str() => { - let response = next.run(request).await; - Ok(response) - } - _ => Err(StatusCode::UNAUTHORIZED), - } -} diff --git a/src/main.rs b/src/main.rs index 75f491a..43957ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ use utoipa_swagger_ui::SwaggerUi; mod config; mod db; mod error; -mod extractors; +mod auth; mod routes; mod services; mod wol; @@ -126,7 +126,7 @@ async fn main() -> color_eyre::eyre::Result<()> { ) .route("/device/:id", get(device::get)) .route("/status", get(status::status)) - .route_layer(from_fn_with_state(shared_state.clone(), extractors::auth)) + .route_layer(from_fn_with_state(shared_state.clone(), auth::auth)) .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi())) .with_state(Arc::new(shared_state)); diff --git a/src/routes/start.rs b/src/routes/start.rs index fa226d8..c61d5a3 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -63,7 +63,7 @@ pub async fn start_payload( #[utoipa::path( post, path = "/start/{id}", - request_body = Payload, + request_body = Option, responses( (status = 200, description = "Start the device with the given id", body = [Response]) ), -- cgit v1.2.3 From a91a2ca5c88403e905bf0f798393587fc4d900fa Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Mon, 8 Apr 2024 15:44:31 +0200 Subject: Closes #26. Addtional GET request for /start --- src/auth.rs | 6 ++++-- src/main.rs | 6 ++++-- src/routes/start.rs | 32 +++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 7 deletions(-) (limited to 'src/routes') diff --git a/src/auth.rs b/src/auth.rs index 1f4518a..74008b5 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -23,11 +23,13 @@ pub async fn auth( match auth.method { Methods::Key => { if let Some(secret) = headers.get("authorization") { - if !(auth.secret.as_str() == secret) { return Err(StatusCode::UNAUTHORIZED); }; + if auth.secret.as_str() != secret { + return Err(StatusCode::UNAUTHORIZED); + }; let response = next.run(request).await; Ok(response) } else { - return Err(StatusCode::UNAUTHORIZED); + Err(StatusCode::UNAUTHORIZED) } } Methods::None => Ok(next.run(request).await), diff --git a/src/main.rs b/src/main.rs index 43957ff..a8acc5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,8 @@ mod wol; #[derive(OpenApi)] #[openapi( paths( - start::start, + start::post, + start::get, start::start_payload, device::get, device::get_payload, @@ -119,13 +120,14 @@ async fn main() -> color_eyre::eyre::Result<()> { let app = Router::new() .route("/start", post(start::start_payload)) - .route("/start/:id", post(start::start)) + .route("/start/:id", post(start::post).get(start::get)) .route( "/device", post(device::post).get(device::get_payload).put(device::put), ) .route("/device/:id", get(device::get)) .route("/status", get(status::status)) + // TODO: Don't load on `None` Auth .route_layer(from_fn_with_state(shared_state.clone(), auth::auth)) .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi())) .with_state(Arc::new(shared_state)); diff --git a/src/routes/start.rs b/src/routes/start.rs index c61d5a3..e74a943 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -18,7 +18,7 @@ use uuid::Uuid; responses( (status = 200, description = "List matching todos by query", body = [Response]) ), - security(("api_key" = [])) + security((), ("api_key" = [])) )] #[deprecated] pub async fn start_payload( @@ -70,12 +70,38 @@ pub async fn start_payload( params( ("id" = String, Path, description = "Device id") ), - security(("api_key" = [])) + security((), ("api_key" = [])) )] -pub async fn start( +pub async fn post( State(state): State>, Path(id): Path, payload: Option>, +) -> Result, Error> { + send_wol(state, &id, payload).await +} + +#[utoipa::path( + get, + path = "/start/{id}", + responses( + (status = 200, description = "Start the device with the given id", body = [Response]) + ), + params( + ("id" = String, Path, description = "Device id") + ), + security((), ("api_key" = [])) +)] +pub async fn get( + State(state): State>, + Path(id): Path, +) -> Result, Error> { + send_wol(state, &id, None).await +} + +async fn send_wol( + state: Arc, + id: &str, + payload: Option>, ) -> Result, Error> { info!("Start request for {id}"); let device = sqlx::query_as!( -- cgit v1.2.3 From 25492f14292afc0fbfd442cd188bc93ffc3d293e Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Tue, 9 Apr 2024 14:41:15 +0200 Subject: Closes #21. Api desc are useful now... --- src/main.rs | 3 +-- src/routes/device.rs | 45 +++++++++++++++++++-------------------------- src/routes/start.rs | 10 +++++----- 3 files changed, 25 insertions(+), 33 deletions(-) (limited to 'src/routes') diff --git a/src/main.rs b/src/main.rs index a8acc5f..70c67cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,9 +50,8 @@ mod wol; start::PayloadOld, start::Payload, start::Response, - device::PutDevicePayload, + device::DevicePayload, device::GetDevicePayload, - device::PostDevicePayload, db::DeviceSchema, ) ), diff --git a/src/routes/device.rs b/src/routes/device.rs index bbc832d..40b5cd8 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -49,9 +49,9 @@ pub async fn get_payload( (status = 200, description = "Get `Device` information", body = [Device]) ), params( - ("id" = String, Path, description = "Device id") + ("id" = String, Path, description = "device id") ), - security(("api_key" = [])) + security((), ("api_key" = [])) )] pub async fn get( State(state): State>, @@ -76,22 +76,31 @@ pub async fn get( } #[derive(Deserialize, ToSchema)] +#[deprecated] pub struct GetDevicePayload { id: String, } +#[derive(Deserialize, ToSchema)] +pub struct DevicePayload { + id: String, + mac: String, + broadcast_addr: String, + ip: String, +} + #[utoipa::path( put, path = "/device", - request_body = PutDevicePayload, + request_body = DevicePayload, responses( - (status = 200, description = "List matching todos by query", body = [DeviceSchema]) + (status = 200, description = "add device to storage", body = [DeviceSchema]) ), - security(("api_key" = [])) + security((), ("api_key" = [])) )] pub async fn put( State(state): State>, - Json(payload): Json, + Json(payload): Json, ) -> Result, Error> { info!( "add device {} ({}, {}, {})", @@ -118,26 +127,18 @@ pub async fn put( Ok(Json(json!(device))) } -#[derive(Deserialize, ToSchema)] -pub struct PutDevicePayload { - id: String, - mac: String, - broadcast_addr: String, - ip: String, -} - #[utoipa::path( post, path = "/device", - request_body = PostDevicePayload, + request_body = DevicePayload, responses( - (status = 200, description = "List matching todos by query", body = [DeviceSchema]) + (status = 200, description = "update device in storage", body = [DeviceSchema]) ), - security(("api_key" = [])) + security((), ("api_key" = [])) )] pub async fn post( State(state): State>, - Json(payload): Json, + Json(payload): Json, ) -> Result, Error> { info!( "edit device {} ({}, {}, {})", @@ -162,11 +163,3 @@ pub async fn post( Ok(Json(json!(device))) } - -#[derive(Deserialize, ToSchema)] -pub struct PostDevicePayload { - id: String, - mac: String, - broadcast_addr: String, - ip: String, -} diff --git a/src/routes/start.rs b/src/routes/start.rs index e74a943..ff3d1be 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -16,7 +16,7 @@ use uuid::Uuid; path = "/start", request_body = PayloadOld, responses( - (status = 200, description = "List matching todos by query", body = [Response]) + (status = 200, description = "DEP", body = [Response]) ), security((), ("api_key" = [])) )] @@ -65,10 +65,10 @@ pub async fn start_payload( path = "/start/{id}", request_body = Option, responses( - (status = 200, description = "Start the device with the given id", body = [Response]) + (status = 200, description = "start the device with the given id", body = [Response]) ), params( - ("id" = String, Path, description = "Device id") + ("id" = String, Path, description = "device id") ), security((), ("api_key" = [])) )] @@ -84,10 +84,10 @@ pub async fn post( get, path = "/start/{id}", responses( - (status = 200, description = "Start the device with the given id", body = [Response]) + (status = 200, description = "start the device with the given id", body = [Response]) ), params( - ("id" = String, Path, description = "Device id") + ("id" = String, Path, description = "device id") ), security((), ("api_key" = [])) )] -- cgit v1.2.3