Add authentication layer
This commit is contained in:
		
							
								
								
									
										214
									
								
								central_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										214
									
								
								central_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -44,6 +44,21 @@ dependencies = [
 | 
			
		||||
 "tracing",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-cors"
 | 
			
		||||
version = "0.7.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "actix-utils",
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "derive_more",
 | 
			
		||||
 "futures-util",
 | 
			
		||||
 "log",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "smallvec",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-http"
 | 
			
		||||
version = "3.8.0"
 | 
			
		||||
@@ -56,7 +71,7 @@ dependencies = [
 | 
			
		||||
 "actix-tls",
 | 
			
		||||
 "actix-utils",
 | 
			
		||||
 "ahash",
 | 
			
		||||
 "base64",
 | 
			
		||||
 "base64 0.22.1",
 | 
			
		||||
 "bitflags 2.6.0",
 | 
			
		||||
 "brotli",
 | 
			
		||||
 "bytes",
 | 
			
		||||
@@ -84,6 +99,22 @@ dependencies = [
 | 
			
		||||
 "zstd",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-identity"
 | 
			
		||||
version = "0.7.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f2c99b7a5614b72a78f04aa2021e5370fc1aef2475fffeffc0c1266b99007062"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "actix-service",
 | 
			
		||||
 "actix-session",
 | 
			
		||||
 "actix-utils",
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "derive_more",
 | 
			
		||||
 "futures-core",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "tracing",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-macros"
 | 
			
		||||
version = "0.2.4"
 | 
			
		||||
@@ -94,6 +125,17 @@ dependencies = [
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-remote-ip"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7629b357d4705cf3f1e31f989f48ecd56027112f7d52dcf06dd96ee197065f8e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "futures-util",
 | 
			
		||||
 "log",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-router"
 | 
			
		||||
version = "0.5.3"
 | 
			
		||||
@@ -147,6 +189,22 @@ dependencies = [
 | 
			
		||||
 "pin-project-lite",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-session"
 | 
			
		||||
version = "0.9.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b671404ec72194d8af58c2bdaf51e3c477a0595056bd5010148405870dda8df2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "actix-service",
 | 
			
		||||
 "actix-utils",
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "derive_more",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "tracing",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "actix-tls"
 | 
			
		||||
version = "3.4.0"
 | 
			
		||||
@@ -256,6 +314,41 @@ version = "1.0.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "aead"
 | 
			
		||||
version = "0.5.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "generic-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "aes"
 | 
			
		||||
version = "0.8.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cipher",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "aes-gcm"
 | 
			
		||||
version = "0.10.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "aead",
 | 
			
		||||
 "aes",
 | 
			
		||||
 "cipher",
 | 
			
		||||
 "ctr",
 | 
			
		||||
 "ghash",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ahash"
 | 
			
		||||
version = "0.8.11"
 | 
			
		||||
@@ -395,6 +488,12 @@ dependencies = [
 | 
			
		||||
 "rustc-demangle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "base64"
 | 
			
		||||
version = "0.20.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "base64"
 | 
			
		||||
version = "0.22.1"
 | 
			
		||||
@@ -480,6 +579,10 @@ name = "central_backend"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "actix",
 | 
			
		||||
 "actix-cors",
 | 
			
		||||
 "actix-identity",
 | 
			
		||||
 "actix-remote-ip",
 | 
			
		||||
 "actix-session",
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "asn1",
 | 
			
		||||
@@ -487,6 +590,7 @@ dependencies = [
 | 
			
		||||
 "env_logger",
 | 
			
		||||
 "foreign-types-shared",
 | 
			
		||||
 "futures",
 | 
			
		||||
 "futures-util",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "log",
 | 
			
		||||
@@ -505,6 +609,16 @@ version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cipher"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "inout",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap"
 | 
			
		||||
version = "4.5.7"
 | 
			
		||||
@@ -563,7 +677,14 @@ version = "0.16.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "aes-gcm",
 | 
			
		||||
 "base64 0.20.0",
 | 
			
		||||
 "hkdf",
 | 
			
		||||
 "hmac",
 | 
			
		||||
 "percent-encoding",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "subtle",
 | 
			
		||||
 "time",
 | 
			
		||||
 "version_check",
 | 
			
		||||
]
 | 
			
		||||
@@ -624,9 +745,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "generic-array",
 | 
			
		||||
 "rand_core",
 | 
			
		||||
 "typenum",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ctr"
 | 
			
		||||
version = "0.9.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cipher",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "deranged"
 | 
			
		||||
version = "0.3.11"
 | 
			
		||||
@@ -657,6 +788,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "block-buffer",
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -863,6 +995,16 @@ dependencies = [
 | 
			
		||||
 "wasi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ghash"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "opaque-debug",
 | 
			
		||||
 "polyval",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "gimli"
 | 
			
		||||
version = "0.29.0"
 | 
			
		||||
@@ -919,6 +1061,24 @@ version = "0.5.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hkdf"
 | 
			
		||||
version = "0.12.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "hmac",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hmac"
 | 
			
		||||
version = "0.12.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "http"
 | 
			
		||||
version = "0.2.12"
 | 
			
		||||
@@ -1081,6 +1241,15 @@ dependencies = [
 | 
			
		||||
 "hashbrown",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "inout"
 | 
			
		||||
version = "0.1.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "generic-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ipnet"
 | 
			
		||||
version = "2.9.0"
 | 
			
		||||
@@ -1245,6 +1414,12 @@ version = "1.19.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "opaque-debug"
 | 
			
		||||
version = "0.3.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "openssl"
 | 
			
		||||
version = "0.10.64"
 | 
			
		||||
@@ -1362,6 +1537,18 @@ version = "0.3.30"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "polyval"
 | 
			
		||||
version = "0.6.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "opaque-debug",
 | 
			
		||||
 "universal-hash",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "powerfmt"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
@@ -1472,7 +1659,7 @@ version = "0.12.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base64",
 | 
			
		||||
 "base64 0.22.1",
 | 
			
		||||
 "bytes",
 | 
			
		||||
 "encoding_rs",
 | 
			
		||||
 "futures-core",
 | 
			
		||||
@@ -1571,7 +1758,7 @@ version = "2.1.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base64",
 | 
			
		||||
 "base64 0.22.1",
 | 
			
		||||
 "rustls-pki-types",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -1696,6 +1883,17 @@ dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sha2"
 | 
			
		||||
version = "0.10.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "signal-hook-registry"
 | 
			
		||||
version = "1.4.2"
 | 
			
		||||
@@ -2007,6 +2205,16 @@ dependencies = [
 | 
			
		||||
 "tinyvec",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "universal-hash"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "untrusted"
 | 
			
		||||
version = "0.9.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,4 +21,9 @@ serde = { version = "1.0.203", features = ["derive"] }
 | 
			
		||||
reqwest = "0.12.5"
 | 
			
		||||
serde_json = "1.0.118"
 | 
			
		||||
rand = "0.8.5"
 | 
			
		||||
actix = "0.13.5"
 | 
			
		||||
actix = "0.13.5"
 | 
			
		||||
actix-identity = "0.7.1"
 | 
			
		||||
actix-session = { version = "0.9.0", features = ["cookie-session"] }
 | 
			
		||||
actix-cors = "0.7.0"
 | 
			
		||||
actix-remote-ip = "0.1.0"
 | 
			
		||||
futures-util = "0.3.30"
 | 
			
		||||
@@ -23,6 +23,32 @@ pub enum ConsumptionBackend {
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
#[command(version, about, long_about = None)]
 | 
			
		||||
pub struct AppConfig {
 | 
			
		||||
    /// Proxy IP, might end with a star "*"
 | 
			
		||||
    #[clap(short, long, env)]
 | 
			
		||||
    pub proxy_ip: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Secret key, used to sign some resources. Must be randomly generated
 | 
			
		||||
    #[clap(short = 'S', long, env, default_value = "")]
 | 
			
		||||
    secret: String,
 | 
			
		||||
 | 
			
		||||
    /// Specify whether the cookie should be transmitted only over secure connections
 | 
			
		||||
    ///
 | 
			
		||||
    /// This should be always true when running in production mode
 | 
			
		||||
    #[clap(long, env)]
 | 
			
		||||
    pub cookie_secure: bool,
 | 
			
		||||
 | 
			
		||||
    /// Unsecure : for development, bypass authentication
 | 
			
		||||
    #[clap(long, env)]
 | 
			
		||||
    pub unsecure_disable_login: bool,
 | 
			
		||||
 | 
			
		||||
    /// Admin username
 | 
			
		||||
    #[clap(long, env, default_value = "admin")]
 | 
			
		||||
    pub admin_username: String,
 | 
			
		||||
 | 
			
		||||
    /// Admin password
 | 
			
		||||
    #[clap(long, env, default_value = "admin")]
 | 
			
		||||
    pub admin_password: String,
 | 
			
		||||
 | 
			
		||||
    /// The port the server will listen to (using HTTPS)
 | 
			
		||||
    #[arg(short, long, env, default_value = "0.0.0.0:8443")]
 | 
			
		||||
    pub listen_address: String,
 | 
			
		||||
@@ -56,6 +82,21 @@ impl AppConfig {
 | 
			
		||||
        &ARGS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get app secret
 | 
			
		||||
    pub fn secret(&self) -> &str {
 | 
			
		||||
        let mut secret = self.secret.as_str();
 | 
			
		||||
 | 
			
		||||
        if cfg!(debug_assertions) && secret.is_empty() {
 | 
			
		||||
            secret = "DEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEY";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if secret.is_empty() {
 | 
			
		||||
            panic!("SECRET is undefined or too short (min 64 chars)!")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        secret
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// URL for unsecure connections
 | 
			
		||||
    pub fn unsecure_origin(&self) -> String {
 | 
			
		||||
        format!(
 | 
			
		||||
@@ -74,6 +115,23 @@ impl AppConfig {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get auth cookie domain
 | 
			
		||||
    pub fn cookie_domain(&self) -> Option<String> {
 | 
			
		||||
        if cfg!(debug_assertions) {
 | 
			
		||||
            let domain = self.secure_origin().split_once("://")?.1.to_string();
 | 
			
		||||
            Some(
 | 
			
		||||
                domain
 | 
			
		||||
                    .split_once(':')
 | 
			
		||||
                    .map(|s| s.0)
 | 
			
		||||
                    .unwrap_or(&domain)
 | 
			
		||||
                    .to_string(),
 | 
			
		||||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            // In release mode, the web app is hosted on the same origin as the API
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get storage path
 | 
			
		||||
    pub fn storage_path(&self) -> PathBuf {
 | 
			
		||||
        Path::new(&self.storage).to_path_buf()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,20 @@
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
/// Name of the cookie that contains session information
 | 
			
		||||
pub const SESSION_COOKIE_NAME: &str = "X-session-cookie";
 | 
			
		||||
 | 
			
		||||
/// Energy refresh operations interval
 | 
			
		||||
pub const ENERGY_REFRESH_INTERVAL: Duration = Duration::from_secs(30);
 | 
			
		||||
 | 
			
		||||
/// Fallback value to use if production cannot be fetched
 | 
			
		||||
pub const FALLBACK_PRODUCTION_VALUE: i32 = 5000;
 | 
			
		||||
 | 
			
		||||
/// Maximum session duration after inactivity, in seconds
 | 
			
		||||
pub const MAX_INACTIVITY_DURATION: u64 = 3600;
 | 
			
		||||
 | 
			
		||||
/// Maximum session duration (1 day)
 | 
			
		||||
pub const MAX_SESSION_DURATION: u64 = 3600 * 24;
 | 
			
		||||
 | 
			
		||||
/// List of routes that do not require authentication
 | 
			
		||||
pub const ROUTES_WITHOUT_AUTH: [&str; 2] =
 | 
			
		||||
    ["/web_api/server/config", "/web_api/auth/password_auth"];
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ use actix::Actor;
 | 
			
		||||
use central_backend::app_config::AppConfig;
 | 
			
		||||
use central_backend::crypto::pki;
 | 
			
		||||
use central_backend::energy::energy_actor::EnergyActor;
 | 
			
		||||
use central_backend::server::{secure_server, unsecure_server};
 | 
			
		||||
use central_backend::server::servers;
 | 
			
		||||
use central_backend::utils::files_utils::create_directory_if_missing;
 | 
			
		||||
use futures::future;
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +30,8 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
        .expect("Failed to initialize energy actor!")
 | 
			
		||||
        .start();
 | 
			
		||||
 | 
			
		||||
    let s1 = secure_server(actor);
 | 
			
		||||
    let s2 = unsecure_server();
 | 
			
		||||
    let s1 = servers::secure_server(actor);
 | 
			
		||||
    let s2 = servers::unsecure_server();
 | 
			
		||||
    future::try_join(s1, s2)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Failed to start servers!");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										97
									
								
								central_backend/src/server/auth_middleware.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								central_backend/src/server/auth_middleware.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
use actix_identity::Identity;
 | 
			
		||||
use std::future::{ready, Ready};
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::constants;
 | 
			
		||||
use actix_web::body::EitherBody;
 | 
			
		||||
use actix_web::dev::Payload;
 | 
			
		||||
use actix_web::{
 | 
			
		||||
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
 | 
			
		||||
    Error, FromRequest, HttpResponse,
 | 
			
		||||
};
 | 
			
		||||
use futures_util::future::LocalBoxFuture;
 | 
			
		||||
 | 
			
		||||
// There are two steps in middleware processing.
 | 
			
		||||
// 1. Middleware initialization, middleware factory gets called with
 | 
			
		||||
//    next service in chain as parameter.
 | 
			
		||||
// 2. Middleware's call method gets called with normal request.
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
pub struct AuthChecker;
 | 
			
		||||
 | 
			
		||||
// Middleware factory is `Transform` trait
 | 
			
		||||
// `S` - type of the next service
 | 
			
		||||
// `B` - type of response's body
 | 
			
		||||
impl<S, B> Transform<S, ServiceRequest> for AuthChecker
 | 
			
		||||
where
 | 
			
		||||
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
 | 
			
		||||
    S::Future: 'static,
 | 
			
		||||
    B: 'static,
 | 
			
		||||
{
 | 
			
		||||
    type Response = ServiceResponse<EitherBody<B>>;
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
    type Transform = AuthMiddleware<S>;
 | 
			
		||||
    type InitError = ();
 | 
			
		||||
    type Future = Ready<Result<Self::Transform, Self::InitError>>;
 | 
			
		||||
 | 
			
		||||
    fn new_transform(&self, service: S) -> Self::Future {
 | 
			
		||||
        ready(Ok(AuthMiddleware {
 | 
			
		||||
            service: Rc::new(service),
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct AuthMiddleware<S> {
 | 
			
		||||
    service: Rc<S>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
 | 
			
		||||
where
 | 
			
		||||
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
 | 
			
		||||
    S::Future: 'static,
 | 
			
		||||
    B: 'static,
 | 
			
		||||
{
 | 
			
		||||
    type Response = ServiceResponse<EitherBody<B>>;
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
 | 
			
		||||
 | 
			
		||||
    forward_ready!(service);
 | 
			
		||||
 | 
			
		||||
    fn call(&self, req: ServiceRequest) -> Self::Future {
 | 
			
		||||
        let service = Rc::clone(&self.service);
 | 
			
		||||
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            // Check if no authentication is required
 | 
			
		||||
            if constants::ROUTES_WITHOUT_AUTH.contains(&req.path())
 | 
			
		||||
                || !req.path().starts_with("/web_api/")
 | 
			
		||||
            {
 | 
			
		||||
                log::trace!("No authentication is required")
 | 
			
		||||
            }
 | 
			
		||||
            // Dev only, check for auto login
 | 
			
		||||
            else if AppConfig::get().unsecure_disable_login {
 | 
			
		||||
                log::trace!("Authentication is disabled")
 | 
			
		||||
            }
 | 
			
		||||
            // Check cookie authentication
 | 
			
		||||
            else {
 | 
			
		||||
                let identity: Option<Identity> =
 | 
			
		||||
                    Identity::from_request(req.request(), &mut Payload::None)
 | 
			
		||||
                        .into_inner()
 | 
			
		||||
                        .ok();
 | 
			
		||||
 | 
			
		||||
                if identity.is_none() {
 | 
			
		||||
                    log::error!(
 | 
			
		||||
                        "Missing identity information in request, user is not authenticated!"
 | 
			
		||||
                    );
 | 
			
		||||
                    return Ok(req
 | 
			
		||||
                        .into_response(HttpResponse::PreconditionFailed().finish())
 | 
			
		||||
                        .map_into_right_body());
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            service
 | 
			
		||||
                .call(req)
 | 
			
		||||
                .await
 | 
			
		||||
                .map(ServiceResponse::map_into_left_body)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -91,9 +91,22 @@ impl From<actix::MailboxError> for HttpErr {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<actix_identity::error::GetIdentityError> for HttpErr {
 | 
			
		||||
    fn from(value: actix_identity::error::GetIdentityError) -> Self {
 | 
			
		||||
        HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<actix_identity::error::LoginError> for HttpErr {
 | 
			
		||||
    fn from(value: actix_identity::error::LoginError) -> Self {
 | 
			
		||||
        HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<HttpResponse> for HttpErr {
 | 
			
		||||
    fn from(value: HttpResponse) -> Self {
 | 
			
		||||
        HttpErr::HTTPResponse(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type HttpResult = Result<HttpResponse, HttpErr>;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,70 +1,11 @@
 | 
			
		||||
use actix_web::middleware::Logger;
 | 
			
		||||
use actix_web::{web, App, HttpServer};
 | 
			
		||||
use openssl::ssl::{SslAcceptor, SslMethod};
 | 
			
		||||
use actix_web::web;
 | 
			
		||||
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::crypto::pki;
 | 
			
		||||
use crate::energy::energy_actor::EnergyActorAddr;
 | 
			
		||||
 | 
			
		||||
pub mod auth_middleware;
 | 
			
		||||
pub mod custom_error;
 | 
			
		||||
pub mod energy_controller;
 | 
			
		||||
pub mod pki_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
pub mod servers;
 | 
			
		||||
pub mod unsecure_server;
 | 
			
		||||
pub mod web_api;
 | 
			
		||||
 | 
			
		||||
pub type WebEnergyActor = web::Data<EnergyActorAddr>;
 | 
			
		||||
 | 
			
		||||
/// Start unsecure (HTTP) server
 | 
			
		||||
pub async fn unsecure_server() -> anyhow::Result<()> {
 | 
			
		||||
    log::info!(
 | 
			
		||||
        "Unsecure server starting to listen on {} for {}",
 | 
			
		||||
        AppConfig::get().unsecure_listen_address,
 | 
			
		||||
        AppConfig::get().unsecure_origin()
 | 
			
		||||
    );
 | 
			
		||||
    HttpServer::new(|| {
 | 
			
		||||
        App::new()
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .route("/", web::get().to(server_controller::unsecure_home))
 | 
			
		||||
            .route("/pki/{file}", web::get().to(pki_controller::serve_pki_file))
 | 
			
		||||
    })
 | 
			
		||||
    .bind(&AppConfig::get().unsecure_listen_address)?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start secure (HTTPS) server
 | 
			
		||||
pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> {
 | 
			
		||||
    let web_ca = pki::CertData::load_web_ca()?;
 | 
			
		||||
    let server_cert = pki::CertData::load_server()?;
 | 
			
		||||
 | 
			
		||||
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
 | 
			
		||||
    builder.set_private_key(&server_cert.key)?;
 | 
			
		||||
    builder.set_certificate(&server_cert.cert)?;
 | 
			
		||||
    builder.add_extra_chain_cert(web_ca.cert)?;
 | 
			
		||||
 | 
			
		||||
    log::info!(
 | 
			
		||||
        "Secure server starting to listen on {} for {}",
 | 
			
		||||
        AppConfig::get().listen_address,
 | 
			
		||||
        AppConfig::get().secure_origin()
 | 
			
		||||
    );
 | 
			
		||||
    HttpServer::new(move || {
 | 
			
		||||
        App::new()
 | 
			
		||||
            .app_data(web::Data::new(energy_actor.clone()))
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .route("/", web::get().to(server_controller::secure_home))
 | 
			
		||||
            .route(
 | 
			
		||||
                "/api/energy/curr_consumption",
 | 
			
		||||
                web::get().to(energy_controller::curr_consumption),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/api/energy/cached_consumption",
 | 
			
		||||
                web::get().to(energy_controller::cached_consumption),
 | 
			
		||||
            )
 | 
			
		||||
    })
 | 
			
		||||
    .bind_openssl(&AppConfig::get().listen_address, builder)?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								central_backend/src/server/servers.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								central_backend/src/server/servers.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::constants;
 | 
			
		||||
use crate::crypto::pki;
 | 
			
		||||
use crate::energy::energy_actor::EnergyActorAddr;
 | 
			
		||||
use crate::server::auth_middleware::AuthChecker;
 | 
			
		||||
use crate::server::unsecure_server::*;
 | 
			
		||||
use crate::server::web_api::*;
 | 
			
		||||
use actix_cors::Cors;
 | 
			
		||||
use actix_identity::config::LogoutBehaviour;
 | 
			
		||||
use actix_identity::IdentityMiddleware;
 | 
			
		||||
use actix_remote_ip::RemoteIPConfig;
 | 
			
		||||
use actix_session::storage::CookieSessionStore;
 | 
			
		||||
use actix_session::SessionMiddleware;
 | 
			
		||||
use actix_web::cookie::{Key, SameSite};
 | 
			
		||||
use actix_web::middleware::Logger;
 | 
			
		||||
use actix_web::{web, App, HttpServer};
 | 
			
		||||
use openssl::ssl::{SslAcceptor, SslMethod};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
/// Start unsecure (HTTP) server
 | 
			
		||||
pub async fn unsecure_server() -> anyhow::Result<()> {
 | 
			
		||||
    log::info!(
 | 
			
		||||
        "Unsecure server starting to listen on {} for {}",
 | 
			
		||||
        AppConfig::get().unsecure_listen_address,
 | 
			
		||||
        AppConfig::get().unsecure_origin()
 | 
			
		||||
    );
 | 
			
		||||
    HttpServer::new(|| {
 | 
			
		||||
        App::new()
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .route(
 | 
			
		||||
                "/",
 | 
			
		||||
                web::get().to(unsecure_server_controller::unsecure_home),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/pki/{file}",
 | 
			
		||||
                web::get().to(unsecure_pki_controller::serve_pki_file),
 | 
			
		||||
            )
 | 
			
		||||
    })
 | 
			
		||||
    .bind(&AppConfig::get().unsecure_listen_address)?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start secure (HTTPS) server
 | 
			
		||||
pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> {
 | 
			
		||||
    let web_ca = pki::CertData::load_web_ca()?;
 | 
			
		||||
    let server_cert = pki::CertData::load_server()?;
 | 
			
		||||
 | 
			
		||||
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
 | 
			
		||||
    builder.set_private_key(&server_cert.key)?;
 | 
			
		||||
    builder.set_certificate(&server_cert.cert)?;
 | 
			
		||||
    builder.add_extra_chain_cert(web_ca.cert)?;
 | 
			
		||||
 | 
			
		||||
    log::info!(
 | 
			
		||||
        "Secure server starting to listen on {} for {}",
 | 
			
		||||
        AppConfig::get().listen_address,
 | 
			
		||||
        AppConfig::get().secure_origin()
 | 
			
		||||
    );
 | 
			
		||||
    HttpServer::new(move || {
 | 
			
		||||
        let session_mw = SessionMiddleware::builder(
 | 
			
		||||
            CookieSessionStore::default(),
 | 
			
		||||
            Key::from(AppConfig::get().secret().as_bytes()),
 | 
			
		||||
        )
 | 
			
		||||
        .cookie_name(constants::SESSION_COOKIE_NAME.to_string())
 | 
			
		||||
        .cookie_secure(AppConfig::get().cookie_secure)
 | 
			
		||||
        .cookie_same_site(SameSite::Strict)
 | 
			
		||||
        .cookie_domain(AppConfig::get().cookie_domain())
 | 
			
		||||
        .cookie_http_only(true)
 | 
			
		||||
        .build();
 | 
			
		||||
 | 
			
		||||
        let identity_middleware = IdentityMiddleware::builder()
 | 
			
		||||
            .logout_behaviour(LogoutBehaviour::PurgeSession)
 | 
			
		||||
            .visit_deadline(Some(Duration::from_secs(
 | 
			
		||||
                constants::MAX_INACTIVITY_DURATION,
 | 
			
		||||
            )))
 | 
			
		||||
            .login_deadline(Some(Duration::from_secs(constants::MAX_SESSION_DURATION)))
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        let mut cors = Cors::default()
 | 
			
		||||
            .allowed_origin(&AppConfig::get().secure_origin())
 | 
			
		||||
            .allowed_methods(vec!["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
 | 
			
		||||
            .allowed_header("X-Auth-Token")
 | 
			
		||||
            .allow_any_header()
 | 
			
		||||
            .supports_credentials()
 | 
			
		||||
            .max_age(3600);
 | 
			
		||||
 | 
			
		||||
        if cfg!(debug_assertions) {
 | 
			
		||||
            cors = cors.allow_any_origin();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        App::new()
 | 
			
		||||
            .app_data(web::Data::new(energy_actor.clone()))
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .wrap(AuthChecker)
 | 
			
		||||
            .wrap(identity_middleware)
 | 
			
		||||
            .wrap(session_mw)
 | 
			
		||||
            .wrap(cors)
 | 
			
		||||
            .app_data(web::Data::new(RemoteIPConfig {
 | 
			
		||||
                proxy: AppConfig::get().proxy_ip.clone(),
 | 
			
		||||
            }))
 | 
			
		||||
            .route("/", web::get().to(server_controller::secure_home))
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/server/config",
 | 
			
		||||
                web::get().to(server_controller::config),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/auth/password_auth",
 | 
			
		||||
                web::post().to(auth_controller::password_auth),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/auth/info",
 | 
			
		||||
                web::get().to(auth_controller::auth_info),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/auth/sign_out",
 | 
			
		||||
                web::get().to(auth_controller::sign_out),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/energy/curr_consumption",
 | 
			
		||||
                web::get().to(energy_controller::curr_consumption),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/web_api/energy/cached_consumption",
 | 
			
		||||
                web::get().to(energy_controller::cached_consumption),
 | 
			
		||||
            )
 | 
			
		||||
    })
 | 
			
		||||
    .bind_openssl(&AppConfig::get().listen_address, builder)?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								central_backend/src/server/unsecure_server/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								central_backend/src/server/unsecure_server/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
pub mod unsecure_pki_controller;
 | 
			
		||||
pub mod unsecure_server_controller;
 | 
			
		||||
@@ -5,9 +5,3 @@ pub async fn unsecure_home() -> HttpResponse {
 | 
			
		||||
        .content_type("text/plain")
 | 
			
		||||
        .body("SolarEnergy unsecure central backend")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn secure_home() -> HttpResponse {
 | 
			
		||||
    HttpResponse::Ok()
 | 
			
		||||
        .content_type("text/plain")
 | 
			
		||||
        .body("SolarEnergy secure central backend")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								central_backend/src/server/web_api/auth_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								central_backend/src/server/web_api/auth_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::server::custom_error::HttpResult;
 | 
			
		||||
use actix_identity::Identity;
 | 
			
		||||
use actix_remote_ip::RemoteIP;
 | 
			
		||||
use actix_web::{web, HttpMessage, HttpRequest, HttpResponse};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct AuthRequest {
 | 
			
		||||
    user: String,
 | 
			
		||||
    password: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Perform password authentication
 | 
			
		||||
pub async fn password_auth(
 | 
			
		||||
    r: web::Json<AuthRequest>,
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    remote_ip: RemoteIP,
 | 
			
		||||
) -> HttpResult {
 | 
			
		||||
    if r.user != AppConfig::get().admin_username || r.password != AppConfig::get().admin_password {
 | 
			
		||||
        log::error!("Failed login attempt from {}!", remote_ip.0.to_string());
 | 
			
		||||
        return Ok(HttpResponse::Unauthorized().json("Invalid credentials!"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Successful login attempt from {}!", remote_ip.0.to_string());
 | 
			
		||||
    Identity::login(&request.extensions(), r.user.to_string())?;
 | 
			
		||||
    Ok(HttpResponse::Ok().finish())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
struct AuthInfo {
 | 
			
		||||
    id: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get current user information
 | 
			
		||||
pub async fn auth_info(id: Option<Identity>) -> HttpResult {
 | 
			
		||||
    if AppConfig::get().unsecure_disable_login {
 | 
			
		||||
        return Ok(HttpResponse::Ok().json(AuthInfo {
 | 
			
		||||
            id: "auto login".to_string(),
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(AuthInfo {
 | 
			
		||||
        id: id.unwrap().id()?,
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Sign out user
 | 
			
		||||
pub async fn sign_out(id: Identity) -> HttpResult {
 | 
			
		||||
    id.logout();
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::NoContent().finish())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								central_backend/src/server/web_api/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								central_backend/src/server/web_api/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod energy_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
							
								
								
									
										25
									
								
								central_backend/src/server/web_api/server_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								central_backend/src/server/web_api/server_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use actix_web::HttpResponse;
 | 
			
		||||
 | 
			
		||||
pub async fn secure_home() -> HttpResponse {
 | 
			
		||||
    HttpResponse::Ok()
 | 
			
		||||
        .content_type("text/plain")
 | 
			
		||||
        .body("SolarEnergy secure central backend")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
struct ServerConfig {
 | 
			
		||||
    auth_disabled: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ServerConfig {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            auth_disabled: AppConfig::get().unsecure_disable_login,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn config() -> HttpResponse {
 | 
			
		||||
    HttpResponse::Ok().json(ServerConfig::default())
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user