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