diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/auth.rs | 49 | ||||
-rw-r--r-- | src/error.rs | 19 | ||||
-rw-r--r-- | src/routes/device.rs | 63 | ||||
-rw-r--r-- | src/routes/start.rs | 2 |
4 files changed, 72 insertions, 61 deletions
diff --git a/src/auth.rs b/src/auth.rs index eb4d1bf..22f87e7 100644 --- a/src/auth.rs +++ b/src/auth.rs | |||
@@ -1,49 +1,30 @@ | |||
1 | use axum::http::{StatusCode, HeaderValue}; | 1 | use axum::http::HeaderValue; |
2 | use axum::http::header::ToStrError; | 2 | use tracing::{debug, trace}; |
3 | use tracing::{debug, error, trace}; | ||
4 | use crate::auth::Error::{MissingSecret, WrongSecret}; | ||
5 | use crate::config::Config; | 3 | use crate::config::Config; |
4 | use crate::error::Error; | ||
6 | 5 | ||
7 | pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<bool, Error> { | 6 | pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<Response, Error> { |
8 | debug!("auth request with secret {:?}", secret); | 7 | debug!("auth request with secret {:?}", secret); |
9 | if let Some(value) = secret { | 8 | let res = if let Some(value) = secret { |
10 | trace!("value exists"); | 9 | trace!("auth value exists"); |
11 | let key = &config.apikey; | 10 | let key = &config.apikey; |
12 | if value.to_str()? == key.as_str() { | 11 | if value.to_str()? == key.as_str() { |
13 | debug!("successful auth"); | 12 | debug!("successful auth"); |
14 | Ok(true) | 13 | Response::Success |
15 | } else { | 14 | } else { |
16 | debug!("unsuccessful auth (wrong secret)"); | 15 | debug!("unsuccessful auth (wrong secret)"); |
17 | Err(WrongSecret) | 16 | Response::WrongSecret |
18 | } | 17 | } |
19 | } else { | 18 | } else { |
20 | debug!("unsuccessful auth (no secret)"); | 19 | debug!("unsuccessful auth (no secret)"); |
21 | Err(MissingSecret) | 20 | Response::MissingSecret |
22 | } | 21 | }; |
22 | Ok(res) | ||
23 | } | 23 | } |
24 | 24 | ||
25 | #[derive(Debug, thiserror::Error)] | 25 | #[derive(Debug)] |
26 | pub enum Error { | 26 | pub enum Response { |
27 | #[error("wrong secret")] | 27 | Success, |
28 | WrongSecret, | 28 | WrongSecret, |
29 | #[error("missing secret")] | 29 | MissingSecret |
30 | MissingSecret, | ||
31 | #[error("parse error: {source}")] | ||
32 | HeaderToStr { | ||
33 | #[from] | ||
34 | source: ToStrError | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl Error { | ||
39 | pub fn get(self) -> (StatusCode, &'static str) { | ||
40 | match self { | ||
41 | Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), | ||
42 | Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), | ||
43 | Self::HeaderToStr { source } => { | ||
44 | error!("auth: {}", source); | ||
45 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | ||
46 | }, | ||
47 | } | ||
48 | } | ||
49 | } | 30 | } |
diff --git a/src/error.rs b/src/error.rs index 4f1bedd..63b214e 100644 --- a/src/error.rs +++ b/src/error.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::auth::Error as AuthError; | 1 | use axum::http::header::ToStrError; |
2 | use axum::http::StatusCode; | 2 | use axum::http::StatusCode; |
3 | use axum::response::{IntoResponse, Response}; | 3 | use axum::response::{IntoResponse, Response}; |
4 | use axum::Json; | 4 | use axum::Json; |
@@ -11,12 +11,6 @@ pub enum Error { | |||
11 | #[error("generic error")] | 11 | #[error("generic error")] |
12 | Generic, | 12 | Generic, |
13 | 13 | ||
14 | #[error("auth: {source}")] | ||
15 | Auth { | ||
16 | #[from] | ||
17 | source: AuthError, | ||
18 | }, | ||
19 | |||
20 | #[error("db: {source}")] | 14 | #[error("db: {source}")] |
21 | Db { | 15 | Db { |
22 | #[from] | 16 | #[from] |
@@ -29,6 +23,12 @@ pub enum Error { | |||
29 | source: std::num::ParseIntError, | 23 | source: std::num::ParseIntError, |
30 | }, | 24 | }, |
31 | 25 | ||
26 | #[error("header parse: {source}")] | ||
27 | ParseHeader { | ||
28 | #[from] | ||
29 | source: ToStrError, | ||
30 | }, | ||
31 | |||
32 | #[error("io: {source}")] | 32 | #[error("io: {source}")] |
33 | Io { | 33 | Io { |
34 | #[from] | 34 | #[from] |
@@ -40,7 +40,6 @@ impl IntoResponse for Error { | |||
40 | fn into_response(self) -> Response { | 40 | fn into_response(self) -> Response { |
41 | error!("{}", self.to_string()); | 41 | error!("{}", self.to_string()); |
42 | let (status, error_message) = match self { | 42 | let (status, error_message) = match self { |
43 | Self::Auth { source } => source.get(), | ||
44 | Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), | 43 | Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), |
45 | Self::Db { source } => { | 44 | Self::Db { source } => { |
46 | error!("{source}"); | 45 | error!("{source}"); |
@@ -50,6 +49,10 @@ impl IntoResponse for Error { | |||
50 | error!("{source}"); | 49 | error!("{source}"); |
51 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | 50 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") |
52 | } | 51 | } |
52 | Self::ParseHeader { source } => { | ||
53 | error!("{source}"); | ||
54 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | ||
55 | } | ||
53 | Self::ParseInt { source } => { | 56 | Self::ParseInt { source } => { |
54 | error!("{source}"); | 57 | error!("{source}"); |
55 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | 58 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") |
diff --git a/src/routes/device.rs b/src/routes/device.rs index aa52cf7..5ca574a 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs | |||
@@ -1,18 +1,23 @@ | |||
1 | use std::sync::Arc; | 1 | use crate::auth::auth; |
2 | use crate::db::Device; | ||
3 | use crate::error::Error; | ||
2 | use axum::extract::State; | 4 | use axum::extract::State; |
3 | use axum::Json; | ||
4 | use axum::http::HeaderMap; | 5 | use axum::http::HeaderMap; |
6 | use axum::Json; | ||
5 | use serde::{Deserialize, Serialize}; | 7 | use serde::{Deserialize, Serialize}; |
6 | use serde_json::{json, Value}; | 8 | use serde_json::{json, Value}; |
9 | use std::sync::Arc; | ||
7 | use tracing::{debug, info}; | 10 | use tracing::{debug, info}; |
8 | use crate::auth::auth; | ||
9 | use crate::db::Device; | ||
10 | use crate::error::Error; | ||
11 | 11 | ||
12 | pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { | 12 | pub async fn get( |
13 | State(state): State<Arc<crate::AppState>>, | ||
14 | headers: HeaderMap, | ||
15 | Json(payload): Json<GetDevicePayload>, | ||
16 | ) -> Result<Json<Value>, Error> { | ||
13 | info!("add device {}", payload.id); | 17 | info!("add device {}", payload.id); |
14 | let secret = headers.get("authorization"); | 18 | let secret = headers.get("authorization"); |
15 | if auth(&state.config, secret)? { | 19 | let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); |
20 | if authorized { | ||
16 | let device = sqlx::query_as!( | 21 | let device = sqlx::query_as!( |
17 | Device, | 22 | Device, |
18 | r#" | 23 | r#" |
@@ -21,7 +26,9 @@ pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, | |||
21 | WHERE id = $1; | 26 | WHERE id = $1; |
22 | "#, | 27 | "#, |
23 | payload.id | 28 | payload.id |
24 | ).fetch_one(&state.db).await?; | 29 | ) |
30 | .fetch_one(&state.db) | ||
31 | .await?; | ||
25 | 32 | ||
26 | debug!("got device {:?}", device); | 33 | debug!("got device {:?}", device); |
27 | 34 | ||
@@ -36,10 +43,18 @@ pub struct GetDevicePayload { | |||
36 | id: String, | 43 | id: String, |
37 | } | 44 | } |
38 | 45 | ||
39 | pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { | 46 | pub async fn put( |
40 | info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); | 47 | State(state): State<Arc<crate::AppState>>, |
48 | headers: HeaderMap, | ||
49 | Json(payload): Json<PutDevicePayload>, | ||
50 | ) -> Result<Json<Value>, Error> { | ||
51 | info!( | ||
52 | "add device {} ({}, {}, {})", | ||
53 | payload.id, payload.mac, payload.broadcast_addr, payload.ip | ||
54 | ); | ||
41 | let secret = headers.get("authorization"); | 55 | let secret = headers.get("authorization"); |
42 | if auth(&state.config, secret)? { | 56 | let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); |
57 | if authorized { | ||
43 | sqlx::query!( | 58 | sqlx::query!( |
44 | r#" | 59 | r#" |
45 | INSERT INTO devices (id, mac, broadcast_addr, ip) | 60 | INSERT INTO devices (id, mac, broadcast_addr, ip) |
@@ -49,7 +64,9 @@ pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, | |||
49 | payload.mac, | 64 | payload.mac, |
50 | payload.broadcast_addr, | 65 | payload.broadcast_addr, |
51 | payload.ip | 66 | payload.ip |
52 | ).execute(&state.db).await?; | 67 | ) |
68 | .execute(&state.db) | ||
69 | .await?; | ||
53 | 70 | ||
54 | Ok(Json(json!(PutDeviceResponse { success: true }))) | 71 | Ok(Json(json!(PutDeviceResponse { success: true }))) |
55 | } else { | 72 | } else { |
@@ -62,18 +79,26 @@ pub struct PutDevicePayload { | |||
62 | id: String, | 79 | id: String, |
63 | mac: String, | 80 | mac: String, |
64 | broadcast_addr: String, | 81 | broadcast_addr: String, |
65 | ip: String | 82 | ip: String, |
66 | } | 83 | } |
67 | 84 | ||
68 | #[derive(Serialize)] | 85 | #[derive(Serialize)] |
69 | pub struct PutDeviceResponse { | 86 | pub struct PutDeviceResponse { |
70 | success: bool | 87 | success: bool, |
71 | } | 88 | } |
72 | 89 | ||
73 | pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { | 90 | pub async fn post( |
74 | info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); | 91 | State(state): State<Arc<crate::AppState>>, |
92 | headers: HeaderMap, | ||
93 | Json(payload): Json<PostDevicePayload>, | ||
94 | ) -> Result<Json<Value>, Error> { | ||
95 | info!( | ||
96 | "edit device {} ({}, {}, {})", | ||
97 | payload.id, payload.mac, payload.broadcast_addr, payload.ip | ||
98 | ); | ||
75 | let secret = headers.get("authorization"); | 99 | let secret = headers.get("authorization"); |
76 | if auth(&state.config, secret)? { | 100 | let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); |
101 | if authorized { | ||
77 | let device = sqlx::query_as!( | 102 | let device = sqlx::query_as!( |
78 | Device, | 103 | Device, |
79 | r#" | 104 | r#" |
@@ -85,7 +110,9 @@ pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, | |||
85 | payload.broadcast_addr, | 110 | payload.broadcast_addr, |
86 | payload.ip, | 111 | payload.ip, |
87 | payload.id | 112 | payload.id |
88 | ).fetch_one(&state.db).await?; | 113 | ) |
114 | .fetch_one(&state.db) | ||
115 | .await?; | ||
89 | 116 | ||
90 | Ok(Json(json!(device))) | 117 | Ok(Json(json!(device))) |
91 | } else { | 118 | } else { |
diff --git a/src/routes/start.rs b/src/routes/start.rs index 66b7cb4..ec4f98f 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs | |||
@@ -20,7 +20,7 @@ pub async fn start( | |||
20 | ) -> Result<Json<Value>, Error> { | 20 | ) -> Result<Json<Value>, Error> { |
21 | info!("POST request"); | 21 | info!("POST request"); |
22 | let secret = headers.get("authorization"); | 22 | let secret = headers.get("authorization"); |
23 | let authorized = auth(&state.config, secret)?; | 23 | let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); |
24 | if authorized { | 24 | if authorized { |
25 | let device = sqlx::query_as!( | 25 | let device = sqlx::query_as!( |
26 | Device, | 26 | Device, |