From d898412f31f2ae2d9e0d68435c81c43eedb76bff Mon Sep 17 00:00:00 2001
From: David Huss <dh@atoav.com>
Date: Fri, 25 Nov 2022 14:04:45 +0100
Subject: [PATCH] Add basic http authentification

---
 Cargo.lock                   | 87 +++++++++++++++++++++---------------
 Cargo.toml                   |  4 +-
 README.md                    |  4 +-
 examples/default-config.toml |  3 ++
 src/bin/sms-gateway.rs       | 33 ++++++++++++--
 src/config.rs                | 19 +++++++-
 src/errors.rs                |  4 ++
 7 files changed, 111 insertions(+), 43 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index ba2ffa8..80d4a2a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -182,6 +182,21 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "actix-web-httpauth"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dda62cf04bc3a9ad2ea8f314f721951cfdb4cdacec4e984d20e77c7bb170991"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "base64",
+ "futures-core",
+ "futures-util",
+ "log",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "adler"
 version = "1.0.2"
@@ -201,9 +216,9 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.19"
+version = "0.7.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
 dependencies = [
  "memchr",
 ]
@@ -312,9 +327,9 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
 
 [[package]]
 name = "bytes"
-version = "1.2.1"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
 
 [[package]]
 name = "bytestring"
@@ -327,9 +342,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.76"
+version = "1.0.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
 dependencies = [
  "jobserver",
 ]
@@ -428,9 +443,9 @@ dependencies = [
 
 [[package]]
 name = "cxx"
-version = "1.0.81"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
+checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -440,9 +455,9 @@ dependencies = [
 
 [[package]]
 name = "cxx-build"
-version = "1.0.81"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
+checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -455,15 +470,15 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.81"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
+checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.81"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
+checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -485,9 +500,9 @@ dependencies = [
 
 [[package]]
 name = "digest"
-version = "0.10.5"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
 dependencies = [
  "block-buffer",
  "crypto-common",
@@ -526,9 +541,9 @@ dependencies = [
 
 [[package]]
 name = "flate2"
-version = "1.0.24"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -780,9 +795,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.9.1"
+version = "1.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -905,9 +920,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.5.4"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
 dependencies = [
  "adler",
 ]
@@ -979,9 +994,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
 
 [[package]]
 name = "openssl"
-version = "0.10.42"
+version = "0.10.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
+checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376"
 dependencies = [
  "bitflags",
  "cfg-if",
@@ -1020,9 +1035,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.77"
+version = "0.9.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
+checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132"
 dependencies = [
  "autocfg",
  "cc",
@@ -1176,9 +1191,9 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.12"
+version = "0.11.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
+checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
 dependencies = [
  "base64",
  "bytes",
@@ -1299,9 +1314,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.87"
+version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
 dependencies = [
  "itoa",
  "ryu",
@@ -1367,10 +1382,11 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "sms-gateway"
-version = "0.4.4"
+version = "0.5.0"
 dependencies = [
  "actix-rt",
  "actix-web",
+ "actix-web-httpauth",
  "build-time",
  "chrono",
  "derive_more",
@@ -1379,6 +1395,7 @@ dependencies = [
  "log",
  "once_cell",
  "openssl",
+ "rand",
  "regex",
  "reqwest",
  "serde",
@@ -1517,9 +1534,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.21.2"
+version = "1.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3"
 dependencies = [
  "autocfg",
  "bytes",
@@ -1915,9 +1932,9 @@ dependencies = [
 
 [[package]]
 name = "zstd-sys"
-version = "2.0.1+zstd.1.5.2"
+version = "2.0.3+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
+checksum = "44ccf97612ac95f3ccb89b2d7346b345e52f1c3019be4984f0455fb4ba991f8a"
 dependencies = [
  "cc",
  "libc",
diff --git a/Cargo.toml b/Cargo.toml
index adc8dd0..0f61038 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
 name = "sms-gateway"
 description = "A http frontend for SMS Server Tools 3"
 authors = ["David Huss <david.huss@hfbk-hamburg.de>"]
-version = "0.4.4"
+version = "0.5.0"
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -27,3 +27,5 @@ reqwest = { version = "0.11", features = ["json", "blocking"] }
 # Needed for cross compilation
 openssl = { version = "0.10.29", features = ["vendored"] }
 serde_regex = "1.1.0"
+actix-web-httpauth = "0.8.0"
+rand = "0.8.5"
diff --git a/README.md b/README.md
index 20da9d4..7d580e8 100644
--- a/README.md
+++ b/README.md
@@ -17,10 +17,10 @@ A http frontend for smstools
 
 ## Usage
 
-Send a message via post request (IP and Port need to match your configuration):
+Send a message via post request (IP, Port, username and password need to match your configuration):
 
 ```bash
-curl -i -d '{"number": "+4915785315089", "text": "Hello from an SMS"}' -H 'Content-Type: application/json' -X POST http://127.0.0.1:4242
+curl -i --user "admin:<PASSWORD>" -d '{"number": "+4915785315089", "text": "Hello from an SMS"}' -H 'Content-Type: application/json' -X POST http://127.0.0.1:4242
 ```
 
 ## Building
diff --git a/examples/default-config.toml b/examples/default-config.toml
index f915874..8f0574c 100644
--- a/examples/default-config.toml
+++ b/examples/default-config.toml
@@ -6,6 +6,9 @@ level = "info"
 [server]
 host = "127.0.0.1"
 port = 4242
+# Use this user and password in your requests (basic http-authentification)
+user = "admin"
+password = "9a3a2f736affdc7a9ec2c3c7c34aa2f9b9e433d506136bb8401e33a3169a5caa"
 
 [sms]
 config_path = "/etc/smsd.conf"
diff --git a/src/bin/sms-gateway.rs b/src/bin/sms-gateway.rs
index de19096..c87733f 100644
--- a/src/bin/sms-gateway.rs
+++ b/src/bin/sms-gateway.rs
@@ -11,11 +11,18 @@ use actix_web::{
     web, 
     post, 
     App, 
-    HttpResponse, 
-    HttpServer, 
+    HttpResponse,
+    HttpServer,
     Responder,
+    Error,
     middleware::Logger,
-    web::{Json, Data}
+    web::{Json, Data},
+    dev::ServiceRequest, 
+};
+
+use actix_web_httpauth::{
+    extractors::basic::BasicAuth, 
+    middleware::HttpAuthentication
 };
 
 use actix_rt::{
@@ -55,7 +62,25 @@ async fn home() -> impl Responder {
 }
 
 
+/// Validates http=requests
+async fn validator(
+    req: ServiceRequest,
+    credentials: BasicAuth,
+) -> Result<ServiceRequest, (Error, ServiceRequest)> {
+    info!("Authoriziation via {:?}", &CONFIG.server.password);
+    match credentials.password() == Some(&CONFIG.server.password) && credentials.user_id() == &CONFIG.server.user {
+        true => Ok(req),
+        false => {
+            match credentials.password() {
+                Some(pw) => error!("Authentification failed with credentials: \"{}:{}\", check the configuration file for working credentials", credentials.user_id(), pw),
+                None => error!("Authentification failed for user without credentials: \"{}:\", check the configuration file for working credentials", credentials.user_id()),
+            }
+            
+            return Err((Error::from(SmsError::AuthError), req));
+        }
 
+    }
+}
 
 
 
@@ -164,11 +189,13 @@ async fn main() -> std::io::Result<()> {
     let history_data = Data::new(Mutex::new(History::new()));
 
     HttpServer::new(move || {
+        let auth = HttpAuthentication::basic(validator);
         App::new()
             .app_data(
                 Data::clone(&history_data)
             )
             .wrap(Logger::default())
+            .wrap(auth)
             .service(home)
             .service(sms_send)
             .service(eventhandler)
diff --git a/src/config.rs b/src/config.rs
index 558abe4..a6fc2f8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -9,6 +9,11 @@ use toml;
 use chrono::Duration;
 use regex::Regex;
 use serde_regex;
+use rand::{
+    thread_rng, 
+    Rng,
+    distributions::Alphanumeric
+};
 
 pub use log::{info, warn, debug, error};
 
@@ -23,7 +28,9 @@ pub struct Log {
 #[allow(unused)]
 pub struct Server {
     pub host: String,
-    pub port: u16
+    pub port: u16,
+    pub user: String,
+    pub password: String
 }
 
 #[derive(Debug, Deserialize, Serialize)]
@@ -194,7 +201,7 @@ fn check_paths(config: &Config) {
 
 impl Config {
     pub fn new(name: &str) -> Self {
-        let default_config: Config = match toml::from_str(include_str!("../examples/default-config.toml")) {
+        let mut default_config: Config = match toml::from_str(include_str!("../examples/default-config.toml")) {
             Ok(c) => c,
             Err(e) => {
                 eprintln!("Error: Could not deserialize default configuration from \"config/default.toml\": {}", e);
@@ -202,6 +209,14 @@ impl Config {
             }
         };
 
+        // Generate a fresh new secret for each new config
+        let rand_string: String = thread_rng()
+            .sample_iter(&Alphanumeric)
+            .take(32)
+            .map(char::from)
+            .collect();
+        default_config.server.password = rand_string;
+
         // Read an existing config (or create the folder and a default config)
         let mut config = ensure_config(name, default_config);
         env_logger::init_from_env(env_logger::Env::default().default_filter_or(&config.log.level));
diff --git a/src/errors.rs b/src/errors.rs
index 9523fdd..f3d1450 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -29,6 +29,8 @@ pub enum SmsError {
   SendFailed,
   #[error("Sending SMS failed, because there was no sent SMS within the given timeout")]
   SendTimeOut,
+  #[error("Failed to authenticate user")]
+  AuthError,
 }
 
 impl SmsError {
@@ -46,6 +48,7 @@ impl SmsError {
             SmsError::InBlackList(_) => "Forbidden".to_string(),
             SmsError::SendFailed => "Service Unavailable".to_string(),
             SmsError::SendTimeOut => "Gateway Timeout".to_string(),
+            SmsError::AuthError=> "Forbidden".to_string(),
         }
     }
 }
@@ -76,6 +79,7 @@ impl ResponseError for SmsError {
             SmsError::InBlackList(_) => StatusCode::FORBIDDEN,
             SmsError::SendFailed => StatusCode::SERVICE_UNAVAILABLE,
             SmsError::SendTimeOut => StatusCode::GATEWAY_TIMEOUT,
+            SmsError::AuthError => StatusCode::FORBIDDEN,
         }
     }
 }
-- 
GitLab