From 1da51615bbe5641f91bbee746b36c5f8edb5ac9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:44:37 +0200 Subject: [PATCH 1/5] build: add dependencies --- Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 4aeb4c6..6ec18c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -558,6 +558,17 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2943,16 +2954,21 @@ version = "0.1.0" dependencies = [ "async-std", "chrono", + "cookie", "diesel", "dioxus", "dioxus-logger", "dioxus-query", "dioxus-sdk", "dotenvy", + "http 1.1.0", + "pin-project-lite", "serde", "serde_json", "serde_with", "time", + "tower-layer", + "tower-service", "tracing", "tracing-wasm", "unic-langid-impl", diff --git a/Cargo.toml b/Cargo.toml index b43ab51..a10fdcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,11 @@ time = "0.3.36" dioxus-sdk = { version = "0.5.0", features = ["i18n"] } unic-langid-impl = "0.9.5" voca_rs = "1.15.2" +http = "1.1.0" +pin-project-lite = "0.2.14" +tower-layer = "0.3.3" +tower-service = "0.3.3" +cookie = { version = "0.18.1", features = ["percent-encode"] } [features] default = [] -- 2.47.1 From 38d3eefe17ab3b3951175723f45a317daaa21dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:45:00 +0200 Subject: [PATCH 2/5] feat: add the `AUTH_TOKEN` environment variable --- .env.dev | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.dev b/.env.dev index 5e21dd0..c915cca 100644 --- a/.env.dev +++ b/.env.dev @@ -1,2 +1,3 @@ DATABASE_URL=postgres://app:app@db/todo_baggins LANGUAGE_CODE=en-US +AUTH_TOKEN=my_token -- 2.47.1 From 8220c3fb35fff198b7c98dad15c764762ff2570b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:45:59 +0200 Subject: [PATCH 3/5] feat: create a middleware for authentication --- src/server/middleware/mod.rs | 108 +++++++++++++++++++++++++++++++++++ src/server/mod.rs | 1 + src/server/tasks.rs | 4 ++ 3 files changed, 113 insertions(+) create mode 100644 src/server/middleware/mod.rs diff --git a/src/server/middleware/mod.rs b/src/server/middleware/mod.rs new file mode 100644 index 0000000..708af95 --- /dev/null +++ b/src/server/middleware/mod.rs @@ -0,0 +1,108 @@ +use http::{Request, Response, StatusCode}; +use pin_project_lite::pin_project; +use std::{env, future::Future, pin::Pin, task::{Context, Poll}}; +use cookie::Cookie; +use dotenvy::dotenv; +use http::header::COOKIE; +use tower_layer::Layer; +use tower_service::Service; + +#[derive(Debug, Clone, Copy)] +pub struct AuthLayer {} + +impl AuthLayer { + pub fn new() -> Self { + AuthLayer {} + } +} + +impl<S> Layer<S> for AuthLayer { + type Service = Auth<S>; + + fn layer(&self, inner: S) -> Self::Service { + Auth::new(inner) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Auth<S> { + inner: S, +} + +impl<S> Auth<S> { + pub fn new(inner: S) -> Self { + Self { inner } + } + + pub fn layer() -> AuthLayer { + AuthLayer::new() + } +} + +impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for Auth<S> +where + S: Service<Request<ReqBody>, Response=Response<ResBody>>, + ResBody: Default, +{ + type Response = S::Response; + type Error = S::Error; + type Future = ResponseFuture<S::Future>; + + #[inline] + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request<ReqBody>) -> Self::Future { + let cookies = req.headers() + .get(COOKIE) + .and_then(|header| header.to_str().ok()) + .map(Cookie::split_parse_encoded); + + let token = cookies.and_then( + |cookies| cookies + .filter_map(|cookie| cookie.ok()) + .find(|cookie| cookie.name() == "auth_token") + .map(|cookie| cookie.value().to_string()) + ); + + ResponseFuture { + inner: self.inner.call(req), + token, + } + } +} + +pin_project! { + pub struct ResponseFuture<F> { + #[pin] + inner: F, + #[pin] + token: Option<String>, + } +} + +impl<F, B, E> Future for ResponseFuture<F> +where + F: Future<Output=Result<Response<B>, E>>, + B: Default, +{ + type Output = Result<Response<B>, E>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + dotenv().expect("Could not load environment variables from the .env file."); + + let this = self.project(); + + if !(*this.token).as_ref().is_some_and( + |token| token == env::var("AUTH_TOKEN") + .expect("The environment variable DATABASE_URL has to be set.").as_str() + ) { + let mut res = Response::new(B::default()); + *res.status_mut() = StatusCode::UNAUTHORIZED; + return Poll::Ready(Ok(res)); + } + + this.inner.poll(cx) + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 50ad1c8..512e582 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -3,3 +3,4 @@ pub(crate) mod projects; pub(crate) mod tasks; pub(crate) mod subtasks; pub(crate) mod internationalization; +mod middleware; diff --git a/src/server/tasks.rs b/src/server/tasks.rs index 192726f..7037b8a 100644 --- a/src/server/tasks.rs +++ b/src/server/tasks.rs @@ -14,10 +14,14 @@ use crate::models::subtask::Subtask; use crate::server::subtasks::restore_subtasks_of_task; #[server] +#[middleware(crate::server::middleware::AuthLayer::new())] pub(crate) async fn create_task(new_task: NewTask) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { use crate::schema::tasks; + let headers: http::HeaderMap = extract().await.unwrap(); + todo!(); + new_task.validate() .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; -- 2.47.1 From 4aef1e7ef5051a1da71a2b8ce37f9b2140d1473f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:46:12 +0200 Subject: [PATCH 4/5] refactor: polish a panic message --- src/server/database_connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/database_connection.rs b/src/server/database_connection.rs index fbf5a74..8a6c566 100644 --- a/src/server/database_connection.rs +++ b/src/server/database_connection.rs @@ -4,7 +4,7 @@ use dotenvy::dotenv; use std::env; pub(crate) fn establish_database_connection() -> ConnectionResult<PgConnection> { - dotenv().expect("Could not load environment variables."); + dotenv().expect("Could not load environment variables from the .env file."); let database_url = env::var("DATABASE_URL").expect("The environment variable DATABASE_URL has to be set."); -- 2.47.1 From 482192dda320dd348ad1333866bbe72fa33f3397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Fri, 13 Sep 2024 07:19:17 +0200 Subject: [PATCH 5/5] feat: extract headers in a server action --- src/server/tasks.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/tasks.rs b/src/server/tasks.rs index 7037b8a..7655924 100644 --- a/src/server/tasks.rs +++ b/src/server/tasks.rs @@ -18,9 +18,13 @@ use crate::server::subtasks::restore_subtasks_of_task; pub(crate) async fn create_task(new_task: NewTask) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { use crate::schema::tasks; + + println!("test"); let headers: http::HeaderMap = extract().await.unwrap(); - todo!(); + + dbg!(headers.iter().collect::<Vec<_>>()); + // println!(headers.values().collect()) new_task.validate() .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; -- 2.47.1