From 17f628739ff6463a439724e11d3fd8a49d638387 Mon Sep 17 00:00:00 2001
From: Josef Rokos
Date: Fri, 22 Sep 2023 21:26:13 +0200
Subject: [PATCH] Implemented user authentication.
---
Cargo.lock | 197 ++++++++++++++++++++++++------
Cargo.toml | 8 +-
src/app.rs | 217 +++------------------------------
src/backend/auth_middleware.rs | 71 +++++++++++
src/backend/company.rs | 16 ++-
src/backend/data.rs | 38 ++++--
src/backend/mod.rs | 18 +++
src/backend/user.rs | 102 ++++++++++++++++
src/components/admin_portal.rs | 140 +++++++++++++++++++++
src/components/header.rs | 38 ++++++
src/components/mod.rs | 2 +
src/components/server_err.rs | 3 +-
src/lib.rs | 1 -
src/locales/mod.rs | 3 +-
src/main.rs | 2 +
src/pages/company_edit.rs | 2 +-
src/pages/company_info.rs | 28 +++--
src/pages/home_page.rs | 11 --
src/pages/login.rs | 74 +++++++++++
src/pages/mod.rs | 2 +
src/pages/public.rs | 8 ++
src/server_fn.rs | 33 -----
src/validator.rs | 16 +--
23 files changed, 704 insertions(+), 326 deletions(-)
create mode 100644 src/backend/auth_middleware.rs
create mode 100644 src/backend/user.rs
create mode 100644 src/components/admin_portal.rs
create mode 100644 src/components/header.rs
create mode 100644 src/pages/login.rs
create mode 100644 src/pages/public.rs
delete mode 100644 src/server_fn.rs
diff --git a/Cargo.lock b/Cargo.lock
index 0a7b568..24e2380 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -44,9 +44,9 @@ dependencies = [
[[package]]
name = "actix-http"
-version = "3.3.1"
+version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74"
+checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9"
dependencies = [
"actix-codec",
"actix-rt",
@@ -54,7 +54,7 @@ dependencies = [
"actix-utils",
"ahash 0.8.3",
"base64 0.21.2",
- "bitflags 1.3.2",
+ "bitflags 2.4.0",
"brotli",
"bytes",
"bytestring",
@@ -127,7 +127,7 @@ dependencies = [
"futures-util",
"mio",
"num_cpus",
- "socket2",
+ "socket2 0.4.9",
"tokio",
"tracing",
]
@@ -145,9 +145,9 @@ dependencies = [
[[package]]
name = "actix-session"
-version = "0.7.2"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87"
+checksum = "2e6a28f813a6671e1847d005cad0be36ae4d016287690f765c303379837c13d6"
dependencies = [
"actix-service",
"actix-utils",
@@ -172,9 +172,9 @@ dependencies = [
[[package]]
name = "actix-web"
-version = "4.3.1"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96"
+checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9"
dependencies = [
"actix-codec",
"actix-http",
@@ -185,7 +185,7 @@ dependencies = [
"actix-service",
"actix-utils",
"actix-web-codegen",
- "ahash 0.7.6",
+ "ahash 0.8.3",
"bytes",
"bytestring",
"cfg-if",
@@ -194,7 +194,6 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
- "http",
"itoa",
"language-tags",
"log",
@@ -206,7 +205,7 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"smallvec",
- "socket2",
+ "socket2 0.5.4",
"time 0.3.22",
"url",
]
@@ -246,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
- "cipher",
+ "cipher 0.4.4",
"cpufeatures",
]
@@ -258,7 +257,7 @@ checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237"
dependencies = [
"aead",
"aes",
- "cipher",
+ "cipher 0.4.4",
"ctr",
"ghash",
"subtle",
@@ -466,6 +465,15 @@ dependencies = [
"wyz",
]
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -475,6 +483,17 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "blowfish"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b"
+dependencies = [
+ "byteorder",
+ "cipher 0.2.5",
+ "opaque-debug",
+]
+
[[package]]
name = "borsh"
version = "0.10.3"
@@ -685,6 +704,15 @@ dependencies = [
"half",
]
+[[package]]
+name = "cipher"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "cipher"
version = "0.4.4"
@@ -786,10 +814,10 @@ dependencies = [
"aes-gcm",
"base64 0.20.0",
"hkdf",
- "hmac",
+ "hmac 0.12.1",
"percent-encoding",
"rand",
- "sha2",
+ "sha2 0.10.7",
"subtle",
"time 0.3.22",
"version_check",
@@ -864,13 +892,23 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "crypto-mac"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
- "cipher",
+ "cipher 0.4.4",
]
[[package]]
@@ -943,13 +981,22 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "block-buffer",
+ "block-buffer 0.10.4",
"const-oid",
"crypto-common",
"subtle",
@@ -1342,7 +1389,17 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
dependencies = [
- "hmac",
+ "hmac 0.12.1",
+]
+
+[[package]]
+name = "hmac"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
]
[[package]]
@@ -1351,7 +1408,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -1429,7 +1486,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2",
+ "socket2 0.4.9",
"tokio",
"tower-service",
"tracing",
@@ -1906,13 +1963,24 @@ dependencies = [
"hashbrown 0.14.0",
]
+[[package]]
+name = "md-5"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
+dependencies = [
+ "block-buffer 0.9.0",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
[[package]]
name = "md-5"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
dependencies = [
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -2162,7 +2230,7 @@ checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0"
dependencies = [
"once_cell",
"pest",
- "sha2",
+ "sha2 0.10.7",
]
[[package]]
@@ -2338,6 +2406,21 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "pwhash"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "419a3ad8fa9f9d445e69d9b185a24878ae6e6f55c96e4512f4a0e28cd3bc5c56"
+dependencies = [
+ "blowfish",
+ "byteorder",
+ "hmac 0.10.1",
+ "md-5 0.9.1",
+ "rand",
+ "sha-1",
+ "sha2 0.9.9",
+]
+
[[package]]
name = "quote"
version = "1.0.28"
@@ -2474,11 +2557,13 @@ dependencies = [
"cfg-if",
"chrono",
"console_error_panic_hook",
+ "futures-util",
"lazy_static",
"leptos",
"leptos_actix",
"leptos_meta",
"leptos_router",
+ "pwhash",
"rust_decimal",
"serde",
"sqlx",
@@ -2550,7 +2635,7 @@ checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8"
dependencies = [
"byteorder",
"const-oid",
- "digest",
+ "digest 0.10.7",
"num-bigint-dig",
"num-integer",
"num-iter",
@@ -2846,6 +2931,19 @@ dependencies = [
"syn 2.0.28",
]
+[[package]]
+name = "sha-1"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
[[package]]
name = "sha1"
version = "0.10.5"
@@ -2854,7 +2952,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if",
"cpufeatures",
- "digest",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
]
[[package]]
@@ -2865,7 +2976,7 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -2883,7 +2994,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
dependencies = [
- "digest",
+ "digest 0.10.7",
"rand_core",
]
@@ -2928,6 +3039,16 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "socket2"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
[[package]]
name = "spin"
version = "0.5.2"
@@ -3011,7 +3132,7 @@ dependencies = [
"rustls-pemfile",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.7",
"smallvec",
"sqlformat",
"thiserror",
@@ -3050,7 +3171,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.7",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
@@ -3074,7 +3195,7 @@ dependencies = [
"bytes",
"chrono",
"crc",
- "digest",
+ "digest 0.10.7",
"dotenvy",
"either",
"futures-channel",
@@ -3084,10 +3205,10 @@ dependencies = [
"generic-array",
"hex",
"hkdf",
- "hmac",
+ "hmac 0.12.1",
"itoa",
"log",
- "md-5",
+ "md-5 0.10.5",
"memchr",
"once_cell",
"percent-encoding",
@@ -3096,7 +3217,7 @@ dependencies = [
"rust_decimal",
"serde",
"sha1",
- "sha2",
+ "sha2 0.10.7",
"smallvec",
"sqlx-core",
"stringprep",
@@ -3125,11 +3246,11 @@ dependencies = [
"futures-util",
"hex",
"hkdf",
- "hmac",
+ "hmac 0.12.1",
"home",
"itoa",
"log",
- "md-5",
+ "md-5 0.10.5",
"memchr",
"num-bigint",
"once_cell",
@@ -3138,7 +3259,7 @@ dependencies = [
"serde",
"serde_json",
"sha1",
- "sha2",
+ "sha2 0.10.7",
"smallvec",
"sqlx-core",
"stringprep",
@@ -3188,9 +3309,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
-version = "2.5.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
@@ -3331,7 +3452,7 @@ dependencies = [
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "socket2",
+ "socket2 0.4.9",
"windows-sys",
]
diff --git a/Cargo.toml b/Cargo.toml
index 8197603..a3dcd2c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,9 +7,9 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
-actix-files = { version = "0.6", optional = true }
-actix-web = { version = "4", optional = true, features = ["macros"] }
-actix-session = { version = "0.7.2", optional = true, features = ["cookie-session"] }
+actix-files = { version = "0.6.2", optional = true }
+actix-web = { version = "4.4.0", optional = true, features = ["macros"] }
+actix-session = { version = "0.8.0", optional = true, features = ["cookie-session"] }
console_error_panic_hook = "0.1"
cfg-if = "1"
leptos = { version = "0.5.0-rc1" }
@@ -25,6 +25,8 @@ sqlx = { version = "0.7.1", optional = true, features = ["runtime-tokio-rustls",
rust_decimal = "1.31.0"
uuid = {version = "1.4.1", features = ["v4"]}
validator = {version = "0.16.1", features = ["derive"]}
+pwhash = "1.0.0"
+futures-util = "0.3.28"
[features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
diff --git a/src/app.rs b/src/app.rs
index 2b18bf0..0552ba5 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,9 +1,13 @@
-use crate::locales::{init_locales, trl};
+use crate::locales::init_locales;
use crate::pages::home_page::HomePage;
use crate::pages::settings::Settings;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
+use crate::components::admin_portal::AdminPortal;
+use crate::components::header::Header;
+use crate::pages::login::Login;
+use crate::pages::public::Public;
#[component]
pub fn App() -> impl IntoView {
@@ -12,210 +16,25 @@ pub fn App() -> impl IntoView {
init_locales();
view! {
-
-
-
+
- //
-
-
-
-
- //
-
-
- //
-
-
-
-
- //
-
-
-
-
- //
-
-
- //
-
- //
-
-
-
- //
-
- //
-
-
-
-
- }/>
- }/>
-
-
-
-
-
-
-
-
-
- /*
- // injects a stylesheet into the document
- // id=leptos means cargo-leptos will hot-reload this stylesheet
-
-
-
-
- // sets the document title
-
-
-
-

"Rezervator admin"
-
-
-
-
-
- // content for this welcome page
-
- }/>
+ }/>
+ }/>
+
+
+
+ }/>
+
+
+
+ }/>
-
-
*/
}
}
diff --git a/src/backend/auth_middleware.rs b/src/backend/auth_middleware.rs
new file mode 100644
index 0000000..416291c
--- /dev/null
+++ b/src/backend/auth_middleware.rs
@@ -0,0 +1,71 @@
+use cfg_if::cfg_if;
+cfg_if! { if #[cfg(feature = "ssr")] {
+
+ use std::future::{Ready, ready};
+ use actix_session::SessionExt;
+ use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform, forward_ready};
+ use actix_web::Error;
+ use actix_web::HttpResponse;
+ use actix_web::body::EitherBody;
+ use futures_util::future::LocalBoxFuture;
+ use actix_web::http::header::LOCATION;
+ use crate::backend::data::User;
+
+ pub struct Authentication;
+
+ impl Transform for Authentication
+ where
+ S: Service, Error = Error>,
+ S::Future: 'static,
+ B: 'static,
+ {
+ type Response = ServiceResponse>;
+ type Error = Error;
+ type Transform = AuthenticationMiddleware;
+ type InitError = ();
+ type Future = Ready>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ ready(Ok(AuthenticationMiddleware { service }))
+ }
+ }
+ pub struct AuthenticationMiddleware {
+ service: S,
+ }
+
+ impl Service for AuthenticationMiddleware
+ where
+ S: Service, Error = Error>,
+ S::Future: 'static,
+ B: 'static,
+ {
+ type Response = ServiceResponse>;
+ type Error = Error;
+ type Future = LocalBoxFuture<'static, Result>;
+
+ forward_ready!(service);
+
+ fn call(&self, req: ServiceRequest) -> Self::Future {
+ let session = req.get_session();
+ let authenticate_pass = !req.path().starts_with("/admin") ||
+ session.get::("user").unwrap_or(None).is_some();
+
+ if authenticate_pass {
+ let res = self.service.call(req);
+
+ Box::pin(async move {
+ res.await.map(ServiceResponse::map_into_left_body)
+ })
+ } else {
+ let (request, _pl) = req.into_parts();
+
+ let response = HttpResponse::Found()
+ .insert_header((LOCATION, "/login"))
+ .finish()
+ .map_into_right_body();
+
+ Box::pin(async { Ok(ServiceResponse::new(request, response)) })
+ }
+ }
+ }
+}}
diff --git a/src/backend/company.rs b/src/backend/company.rs
index a17c3e3..08d4b8d 100644
--- a/src/backend/company.rs
+++ b/src/backend/company.rs
@@ -1,11 +1,14 @@
-use crate::backend::data::Company;
use leptos::*;
+use crate::backend::data::{ApiResponse, Company};
#[server(GetCompany, "/api", "Url", "get_company")]
-pub async fn get_company() -> Result {
+pub async fn get_company() -> Result, ServerFnError> {
use crate::backend::AppData;
use actix_web::web::Data;
use leptos_actix::extract;
+ use crate::perm_check;
+
+ perm_check!(is_logged_in);
let pool = extract(|data: Data| async move { data.db_pool().clone() }).await?;
@@ -13,14 +16,17 @@ pub async fn get_company() -> Result {
.fetch_one(&pool)
.await?;
- Ok(cmp)
+ Ok(ApiResponse::Data(cmp))
}
#[server(UpdateCompany, "/api", "Url", "update_company")]
-pub async fn update_company(company: Company) -> Result<(), ServerFnError> {
+pub async fn update_company(company: Company) -> Result, ServerFnError> {
use crate::backend::AppData;
use actix_web::web::Data;
use leptos_actix::extract;
+ use crate::perm_check;
+
+ perm_check!(is_admin);
let pool = extract(|data: Data| async move { data.db_pool().clone() }).await?;
@@ -36,5 +42,5 @@ pub async fn update_company(company: Company) -> Result<(), ServerFnError> {
.execute(&pool)
.await?;
- Ok(())
+ Ok(ApiResponse::Data(()))
}
diff --git a/src/backend/data.rs b/src/backend/data.rs
index 73fc0c3..280c64c 100644
--- a/src/backend/data.rs
+++ b/src/backend/data.rs
@@ -1,9 +1,15 @@
-use chrono::{NaiveDate, NaiveTime, Weekday};
-use rust_decimal::Decimal;
+//use chrono::{NaiveDate, NaiveTime, Weekday};
+//use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
-use uuid::Uuid;
+//use uuid::Uuid;
use validator::Validate;
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub enum ApiResponse {
+ Data(T),
+ Error(String)
+}
+
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct Company {
@@ -24,17 +30,25 @@ impl Company {
}
}
+#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
+#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct User {
- id: u16,
- login: String,
- password: String,
- full_name: String,
- email: String,
- admin: bool,
- get_emails: bool,
+ id: i32,
+ pub login: String,
+ pub password: String,
+ pub full_name: Option,
+ pub email: Option,
+ pub admin: bool,
+ pub get_emails: bool,
+}
+
+impl User {
+ pub fn id(&self) -> i32 {
+ self.id
+ }
}
-pub struct Property {
+/*pub struct Property {
id: u16,
name: String,
description: String,
@@ -92,4 +106,4 @@ pub struct ReservationSum {
customer: Customer,
price: Decimal,
state: ReservationState,
-}
\ No newline at end of file
+}*/
diff --git a/src/backend/mod.rs b/src/backend/mod.rs
index ea4d85e..a5e50de 100644
--- a/src/backend/mod.rs
+++ b/src/backend/mod.rs
@@ -2,6 +2,24 @@ use cfg_if::cfg_if;
pub mod data;
pub mod company;
+pub mod user;
+pub mod auth_middleware;
+
+#[macro_export]
+macro_rules! perm_check {
+ ($check:ident) => {
+ use crate::backend::user::$check;
+ use actix_web::http::StatusCode;
+ use leptos_actix::ResponseOptions;
+
+ if !$check().await {
+ let response = expect_context::();
+ response.set_status(StatusCode::FORBIDDEN);
+
+ return Ok(ApiResponse::Error("Forbidden".to_string()))
+ }
+ }
+}
cfg_if!{
if #[cfg(feature = "ssr")] {
diff --git a/src/backend/user.rs b/src/backend/user.rs
new file mode 100644
index 0000000..61f552f
--- /dev/null
+++ b/src/backend/user.rs
@@ -0,0 +1,102 @@
+use cfg_if::cfg_if;
+use leptos::*;
+
+cfg_if! { if #[cfg(feature = "ssr")] {
+ use sqlx::{query_as, Error, PgPool, query};
+ use actix_session::*;
+ use leptos_actix::{extract, redirect};
+ use crate::backend::data::User;
+
+ pub async fn has_admin_user(pool: &PgPool) -> Result {
+ let count: (i64,) = query_as(r#"SELECT COUNT(id) FROM "user" WHERE admin = $1"#)
+ .bind(true)
+ .fetch_one(pool)
+ .await?;
+
+ Ok(count.0 > 0)
+ }
+
+ pub async fn create_admin(pool: &PgPool) -> Result<(), Error> {
+ if !has_admin_user(pool).await? {
+ let pwd = pwhash::bcrypt::hash("admin");
+ query(r#"INSERT INTO "user"(login, password, full_name, admin) VALUES($1, $2, $3, $4)"#)
+ .bind("admin")
+ .bind(pwd.unwrap())
+ .bind("Admin User")
+ .bind(true)
+ .execute(pool).await?;
+ }
+
+ Ok(())
+ }
+
+ pub async fn user_from_login(pool: &PgPool, login: &str) -> Result {
+ let usr = query_as::<_, User>(r#"SELECT * FROM "user" WHERE login=$1"#)
+ .bind(login)
+ .fetch_one(pool).await?;
+ Ok(usr)
+ }
+
+ pub async fn logged_in_user() -> Option {
+ extract(|session: Session| async move {
+ session.get::("user").unwrap_or(None)
+ }).await.unwrap_or(None)
+ }
+
+ pub async fn is_logged_in() -> bool {
+ logged_in_user().await.is_some()
+ }
+
+ pub async fn is_admin() -> bool {
+ if let Some(user) = logged_in_user().await {
+ user.admin
+ } else {
+ false
+ }
+ }
+}}
+
+#[server(Login, "/api")]
+pub async fn login(username: String, password: String) -> Result<(), ServerFnError> {
+ use crate::backend::AppData;
+ use actix_session::*;
+ use actix_web::web::Data;
+ use leptos_actix::extract;
+
+ let pool = extract(|data: Data| async move { data.db_pool().clone() }).await?;
+
+ let user = user_from_login(&pool, &username).await?;
+
+ if pwhash::bcrypt::verify(password, &user.password) {
+ extract(|session: Session| async move {
+ let _ = session.insert("user", user);
+ })
+ .await?;
+
+ redirect("/admin");
+ return Ok(());
+ }
+
+ Err(ServerFnError::ServerError("Bad login".to_string()))
+}
+
+#[server]
+pub async fn logout() -> Result<(), ServerFnError> {
+ extract(|session: Session| async move {
+ session.clear();
+ }).await?;
+
+ redirect("/login");
+
+ Ok(())
+}
+
+#[server]
+pub async fn auth_check() -> Result {
+ Ok(is_logged_in().await)
+}
+
+#[server]
+pub async fn admin_check() -> Result {
+ Ok(is_admin().await)
+}
\ No newline at end of file
diff --git a/src/components/admin_portal.rs b/src/components/admin_portal.rs
new file mode 100644
index 0000000..494c1a5
--- /dev/null
+++ b/src/components/admin_portal.rs
@@ -0,0 +1,140 @@
+use leptos::*;
+use crate::locales::trl;
+
+#[component]
+pub fn AdminPortal(children: Children) -> impl IntoView {
+ view! {
+
+
+ //
+
+
+ //
+
+ //
+
+
+
+ //
+
+ //
+
+ {children()}
+
+
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/components/header.rs b/src/components/header.rs
new file mode 100644
index 0000000..d6676ac
--- /dev/null
+++ b/src/components/header.rs
@@ -0,0 +1,38 @@
+use leptos::*;
+use leptos_meta::*;
+
+#[component]
+pub fn Header() -> impl IntoView {
+ view! {
+
+
+
+
+ //
+
+
+
+
+ //
+
+
+ //
+
+
+
+
+ //
+
+ }
+}
\ No newline at end of file
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 8c68b2a..d9381a8 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -1,4 +1,6 @@
pub mod modal_box;
pub mod server_err;
pub mod validation_err;
+pub mod header;
+pub mod admin_portal;
diff --git a/src/components/server_err.rs b/src/components/server_err.rs
index e4476b2..c4d9794 100644
--- a/src/components/server_err.rs
+++ b/src/components/server_err.rs
@@ -1,9 +1,10 @@
use crate::components::modal_box::DialogOpener;
use leptos::*;
+use crate::backend::data::ApiResponse;
#[component]
pub fn ServerErr(
- result: RwSignal