From 5cdce86d912a34252d479dd64e8be444949c31b2 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Mon, 11 Nov 2019 22:29:48 +0100 Subject: [PATCH 01/12] Partially reimplement server.rs with warp --- Cargo.lock | 29 ++++ Cargo.toml | 2 + src/configuration.rs | 2 +- src/server.rs | 363 +++++++++++++------------------------------ 4 files changed, 141 insertions(+), 255 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a50d523..7a5f958 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,6 +276,11 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "doc-comment" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dtoa" version = "0.4.4" @@ -645,6 +650,7 @@ version = "0.1.2" dependencies = [ "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -660,6 +666,7 @@ dependencies = [ "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_magic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "warp 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1385,6 +1392,25 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "snafu" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "snafu-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "spin" version = "0.5.2" @@ -1985,6 +2011,7 @@ dependencies = [ "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" @@ -2113,6 +2140,8 @@ dependencies = [ "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41207ca11f96a62cd34e6b7fdf73d322b25ae3848eb9d38302169724bb32cf27" +"checksum snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5e338c8b0577457c9dda8e794b6ad7231c96e25b1b0dd5842d52249020c1c0" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" diff --git a/Cargo.toml b/Cargo.toml index bd9fb4f..6483a22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ chashmap = "*" plugin = "*" tree_magic = "*" byte-unit = "*" +snafu = "*" +bytes = "*" #hyper = "*" #hyper-openssl = "*" diff --git a/src/configuration.rs b/src/configuration.rs index 55df0ca..d00278c 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -80,7 +80,7 @@ pub struct WebUI { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Store { - pub max_limit: i32 + pub max_limit: u64 } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/server.rs b/src/server.rs index 6beee06..1f02a01 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,24 +1,18 @@ use std::io::Read; use std::sync::Arc; -use std::sync::RwLock; use byte_unit::Byte; -use hyper::header::*; +use bytes::Buf; use jsonwebtoken::{decode, Validation}; -use nickel::{*, HttpRouter, Middleware, MiddlewareResult, Nickel, Options, Request, Response, StaticFilesHandler}; -use nickel::hyper::method::Method; -use nickel::status::StatusCode; +use snafu::{ResultExt, Snafu}; +use warp::{self, filters, http::StatusCode, path, Filter, Rejection, Reply}; use crate::configuration::*; use crate::kvstore::KvStore; -use crate::logger::{Logger, LogLevel}; - -pub struct Server { - configuration: Configuration -} +use crate::logger::{LogLevel, Logger}; #[derive(Serialize, Deserialize)] -struct ErrorMessage { +struct JsonMessage { message: String, } @@ -30,180 +24,14 @@ struct Claims { exp: i64, } -#[derive(Serialize, Deserialize)] -struct PatchValue { - operation: String -} - -fn middleware_webui<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> { - res.set(MediaType::Html); - res.send_file("webui/dist/index.html") -} - -fn middleware_logging<'a, D>(request: &mut Request, response: Response<'a, D>) -> MiddlewareResult<'a, D> { - crate::logger::print(&LogLevel::Information, format!("{} {}", request.origin.method, request.origin.uri).as_ref()); - response.next_middleware() -} - -fn middleware_cors<'mw>(_req: &mut Request, mut res: Response<'mw>) -> MiddlewareResult<'mw> { - res.headers_mut().set_raw("Access-Control-Allow-Origin", vec![b"*".to_vec()]); - res.headers_mut().set_raw("Access-Control-Allow-Methods", vec![b"*".to_vec()]); - res.headers_mut().set_raw("Access-Control-Allow-Headers", vec![b"*".to_vec()]); //Origin, Authorization, X-Requested-With, Content-Type, Accept - res.next_middleware() -} - -struct KvStoreMiddleware { - http_verb: hyper::method::Method, - store: Arc>, - configuration: Configuration -} - -impl Middleware for KvStoreMiddleware { - fn invoke<'mw, 'conn>(&self, req: &mut Request<'mw, 'conn, D>, mut res: Response<'mw, D>) -> MiddlewareResult<'mw, D> { - - // Get the request body and retrieve the KV store - let store = &*self.store.write().unwrap(); - let mut buffer = Vec::new(); - let body_size = req.origin.read_to_end(&mut buffer).unwrap(); - - // Set the server response header - res.set(Server(format!("Lucid {}", crate_version!()))); - - // TODO: handle authentication disabled - match req.origin.headers.get::>() { - Some(header) => match decode::(&header.token, self.configuration.authentication.secret_key.as_ref(), &Validation::default()) { - Ok(_bearer) => match self.http_verb { - Method::Head => match req.param("key") { - Some(key) => match &store.get(key.to_string()) { - Some(_) => { - res.set(StatusCode::Ok); - res.send("") - }, - None => { - res.set(StatusCode::NotFound).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "The specified key does not exists.".to_string() }).unwrap()) - } - }, - None => { - res.set(StatusCode::BadRequest).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing key parameter.".to_string() }).unwrap()) - } - }, - Method::Put => { - if body_size == 0 { - res.set(StatusCode::BadRequest).set(MediaType::Json); - return res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing request body.".to_string() }).unwrap()); - } - - match req.param("key") { - Some(key) => if buffer.len() < self.configuration.store.max_limit as usize { - match store.set(key.to_string(), buffer) { - None => { - res.set(StatusCode::Created).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "The specified key was successfully created.".to_string() }).unwrap()) - }, - Some(_) => { - res.set(StatusCode::Ok).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "The specified key was successfully updated.".to_string() }).unwrap()) - } - } - } else { - res.set(StatusCode::BadRequest).set(MediaType::Json); - let max_limit = Byte::from_bytes(self.configuration.store.max_limit as u128); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: format!("The maximum allowed value size is {}.", max_limit.get_appropriate_unit(true)) }).unwrap()) - }, - None => { - res.set(StatusCode::BadRequest).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing key parameter.".to_string() }).unwrap()) - } - } - }, - Method::Get => match req.param("key") { - // TODO: check query string, for getting metadata - - Some(key) => match store.get(key.to_string()) { - Some(value) => { - res.set(StatusCode::Ok).set(MediaType::Txt); - res.send(value) - }, - None => { - // TODO: found a better name / location - if req.param("key").unwrap() == "check-token" { - res.set(StatusCode::Ok).set(MediaType::Json); - return res.send(serde_json::to_string_pretty(&ErrorMessage { message: format!("Lucid {}", crate_version!()) }).unwrap()); - } - res.set(StatusCode::NotFound).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "The specified key does not exists.".to_string() }).unwrap()) - } - }, - None => { - res.set(StatusCode::BadRequest).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing key parameter.".to_string() }).unwrap()) - } - }, - Method::Delete => match req.param("key") { - Some(key) => { - store.drop(key.to_string()); - res.set(StatusCode::Ok); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "The specified key and it's data was successfully deleted.".to_string() }).unwrap()) - }, - None => { - res.set(StatusCode::BadRequest).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing key parameter.".to_string() }).unwrap()) - } - }, - Method::Patch => match req.param("key") { - Some(key) => { - // TODO: Operations to implement: LOCK, UNLOCK, INCREMENT, DECREMENT, EXPIRE - - if let Ok(json_body) = std::str::from_utf8((*buffer).as_ref()) { - match serde_json::from_str::(json_body) { - Ok(patch_value) => { - match patch_value.operation.as_str() { - "lock" | "unlock" => { - let r = store.switch_lock(key.to_string(), true); - println!("{}", r); - } - _ => () - } - }, - Err(e) => { - println!("{}", e); - } - } - } - res.set(StatusCode::Ok); - res.send("") - }, - None => { - res.set(StatusCode::BadRequest).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing key parameter.".to_string() }).unwrap()) - } - }, - _ => { - res.set(StatusCode::MethodNotAllowed).set(MediaType::Json); - res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Method not allowed, maybe in the future :)".to_string() }).unwrap()) - } - }, - Err(_) => { - res.set(StatusCode::InternalServerError).set(MediaType::Json); - return res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Unable to decrypt JWT token.".to_string() }).unwrap()); //, details: Some(e.to_string()) - } - }, - None => { - res.set(StatusCode::Unauthorized).set(MediaType::Json); - return res.send(serde_json::to_string_pretty(&ErrorMessage { message: "Missing JWT token.".to_string() }).unwrap()); - } - } - } +pub struct Server { + configuration: Configuration, } -impl Server -{ - pub fn new() -> Server - { +impl Server { + pub fn new() -> Server { Server { - configuration: Configuration::default() + configuration: Configuration::default(), } } @@ -211,82 +39,109 @@ impl Server self.configuration = configuration; } - fn router_webui(&self) -> nickel::Router { - let mut router = Nickel::router(); - router.get("/", middleware_webui); - router.get("/api/ui/version", middleware!(format!("Lucid Version {}", crate_version!()))); - router + pub fn run(&self) { + let store = Arc::new(KvStore::new()); + let store = warp::any().map(move || store.clone()); + + let api_kv = path!("api" / "kv").and(path::end()).and( + warp::get2() + .and(store.clone()) + .and(warp::query::()) + .and_then(get_key) + .or(warp::put2() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit(self.configuration.store.max_limit)) + .and(warp::body::concat()) + .and_then(put_key)), + ); + let routes = api_kv.recover(process_error); + warp::serve(routes).run(([127, 0, 0, 1], 7021)); } +} - fn router_sse(&self) -> nickel::Router { - let mut router = Nickel::router(); - router.get("/sse/test", middleware! { |_request, mut response| - response.set(StatusCode::BadRequest).set(MediaType::Json); - "lol" - }); - router +#[derive(Debug, Deserialize)] +struct GetKeyParameters { + key: Option, +} +fn get_key(store: Arc, parameters: GetKeyParameters) -> Result { + if let Some(key) = parameters.key { + if let Some(value) = store.get(key) { + Ok(value) + } else { + Err(warp::reject::custom(Error::KeyNotFound)) + } + } else { + Err(warp::reject::custom(Error::MissingParameter { + name: "key".to_string(), + })) } +} - pub fn run(&self) { - let server_options = Options::default() - .thread_count(None) // TODO: [Optimisation] improve this - .output_on_listen(false); - - let mut server = Nickel::with_options(server_options); - - let store = Arc::new(RwLock::new(KvStore::new())); - - server.utilize(middleware_logging); - - // CORS - server.utilize(middleware_cors); - server.options("**", middleware!("")); - - // Web UI - if self.configuration.webui.enabled { - server.utilize(self.router_webui()); - server.utilize(StaticFilesHandler::new("assets/")); - server.utilize(StaticFilesHandler::new("webui/dist")); +#[derive(Debug, Deserialize)] +struct PutKeyParameters { + key: Option, +} +fn put_key( + store: Arc, + parameters: PutKeyParameters, + body: filters::body::FullBody, +) -> Result { + if body.remaining() == 0 { + Err(warp::reject::custom(Error::MissingBody)) + } else { + if let Some(key) = parameters.key { + if let Some(_) = store.set(key, body.bytes().to_vec()) { + Ok(warp::reply::json(&JsonMessage { + message: "The specified key was successfully updated.".to_string(), + })) + } else { + Ok(warp::reply::json(&JsonMessage { + message: "The specified key was successfully created.".to_string(), + })) + } + } else { + Err(warp::reject::custom(Error::MissingParameter { + name: "key".to_string(), + })) } + } +} - // TODO: maybe define if set in the configuration file - // Robots.txt - server.get("/robots.txt", middleware!("User-agent: *\nDisallow: /")); - - // API Endpoints - // TODO: change to server.head() (https://github.com/nickel-org/nickel.rs/issues/444) - server.add_route(Method::Head, "/api/kv/:key", KvStoreMiddleware { http_verb: Method::Head, store: store.clone(), configuration: self.configuration.clone() }); - server.put("/api/kv/:key", KvStoreMiddleware { http_verb: Method::Put, store: store.clone(), configuration: self.configuration.clone() }); - server.get("/api/kv/:key", KvStoreMiddleware { http_verb: Method::Get, store: store.clone(), configuration: self.configuration.clone() }); - server.patch("/api/kv/:key", KvStoreMiddleware { http_verb: Method::Patch, store: store.clone(), configuration: self.configuration.clone() }); - server.delete("/api/kv/:key", KvStoreMiddleware { http_verb: Method::Delete, store: store.clone(), configuration: self.configuration.clone() }); - - // SSE Endpoints - server.utilize(self.router_sse()); - - // TODO: Implement HTTPS (https://github.com/nickel-org/nickel.rs/blob/master/examples/https.rs) - match self.configuration.default.use_ssl { - true => { -// use hyper::Server; -// use hyper_openssl::OpensslServer; -// let ssl = Openssl::with_cert_and_key("examples/assets/self_signed.crt", "examples/assets/key.pem").unwrap(); -// server.listen_https("127.0.0.1:7021", ssl); - }, - false => match server.listen(self.configuration.clone().get_bind_endpoint()) { - Ok(instance) => { - // TODO: move logging for using in https to - // TODO: try using server.log and getting owner - &self.log(LogLevel::Information, format!("Running Lucid server on {endpoint} | PID: {pid}", endpoint = instance.socket(), pid = std::process::id()).as_str(), None); - &self.log(LogLevel::Information, format!("Lucid API Endpoint: http://{endpoint}/api/", endpoint = instance.socket()).as_str(), None); - if self.configuration.webui.enabled { - &self.log(LogLevel::Information, format!("Lucid Web UI Path: http://{endpoint}/", endpoint = instance.socket()).as_str(), None); - } - &self.log(LogLevel::Information, "Use Ctrl+C to stop the server.", None); - } - Err(err) => { - &self.log(LogLevel::Error, "Unable to run Lucid server", Some(Box::leak(err).description())); - } - }, - } +fn process_error(err: Rejection) -> Result { + if let Some(err) = err.find_cause::() { + let code = match err { + Error::MissingBody => StatusCode::BAD_REQUEST, + Error::MissingParameter { .. } => StatusCode::BAD_REQUEST, + Error::KeyNotFound => StatusCode::NOT_FOUND, + }; + let json = warp::reply::json(&JsonMessage { + message: err.to_string(), + }); + Ok(warp::reply::with_status(json, code)) + } else if let Some(_) = err.find_cause::() { + let code = StatusCode::METHOD_NOT_ALLOWED; + let json = warp::reply::json(&JsonMessage { + message: "Method not allowed.".to_string(), + }); + Ok(warp::reply::with_status(json, code)) + } else if let Some(_) = err.find_cause::() { + let code = StatusCode::METHOD_NOT_ALLOWED; + let json = warp::reply::json(&JsonMessage { + message: "Request payload is over {} bytes long.".to_string(), // TODO: format the string + }); + Ok(warp::reply::with_status(json, code)) + } else { + Err(err) } -} \ No newline at end of file +} + +#[derive(Debug, Snafu)] +enum Error { + #[snafu(display("Missing request body."))] + MissingBody, + #[snafu(display("Missing \"{}\" parameter.", name))] + MissingParameter { name: String }, + #[snafu(display("The specified key does not exist."))] + KeyNotFound, +} From a0ac6282f97a8dacb73ab6fee31fd2112629dd01 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 17:03:31 +0100 Subject: [PATCH 02/12] Implement DELETE for /api/kv --- src/server.rs | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/server.rs b/src/server.rs index 1f02a01..0ab8058 100644 --- a/src/server.rs +++ b/src/server.rs @@ -16,14 +16,6 @@ struct JsonMessage { message: String, } -#[derive(Serialize, Deserialize)] -struct Claims { - sub: String, - iss: String, - iat: i64, - exp: i64, -} - pub struct Server { configuration: Configuration, } @@ -51,9 +43,15 @@ impl Server { .or(warp::put2() .and(store.clone()) .and(warp::query::()) - .and(filters::body::content_length_limit(self.configuration.store.max_limit)) + .and(filters::body::content_length_limit( + self.configuration.store.max_limit, + )) .and(warp::body::concat()) - .and_then(put_key)), + .and_then(put_key)) + .or(warp::delete2() + .and(store.clone()) + .and(warp::query::()) + .and_then(delete_key)), ); let routes = api_kv.recover(process_error); warp::serve(routes).run(([127, 0, 0, 1], 7021)); @@ -108,6 +106,30 @@ fn put_key( } } +#[derive(Debug, Deserialize)] +struct DeleteKeyParameters { + key: Option, +} +fn delete_key( + store: Arc, + parameters: DeleteKeyParameters, +) -> Result { + if let Some(key) = parameters.key { + if let Some(_) = store.get(key.clone()) { + (*store).drop(key); + Ok(warp::reply::json(&JsonMessage { + message: "The specified key and it's data was successfully deleted".to_string(), + })) + } else { + Err(warp::reject::custom(Error::KeyNotFound)) + } + } else { + Err(warp::reject::custom(Error::MissingParameter { + name: "key".to_string(), + })) + } +} + fn process_error(err: Rejection) -> Result { if let Some(err) = err.find_cause::() { let code = match err { From 889cbe87d2aa47a444b0a95a99118385948350f8 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 17:07:38 +0100 Subject: [PATCH 03/12] Implement HEAD for /api/kv --- src/server.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 0ab8058..73ef1cb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -51,7 +51,11 @@ impl Server { .or(warp::delete2() .and(store.clone()) .and(warp::query::()) - .and_then(delete_key)), + .and_then(delete_key)) + .or(warp::head() + .and(store.clone()) + .and(warp::query::()) + .and_then(find_key)), ); let routes = api_kv.recover(process_error); warp::serve(routes).run(([127, 0, 0, 1], 7021)); @@ -130,6 +134,24 @@ fn delete_key( } } +#[derive(Debug, Deserialize)] +struct HeadKeyParameters { + key: Option, +} +fn find_key(store: Arc, parameters: HeadKeyParameters) -> Result { + if let Some(key) = parameters.key { + if let Some(_) = store.get(key) { + Ok("") + } else { + Err(warp::reject::custom(Error::KeyNotFound)) + } + } else { + Err(warp::reject::custom(Error::MissingParameter { + name: "key".to_string(), + })) + } +} + fn process_error(err: Rejection) -> Result { if let Some(err) = err.find_cause::() { let code = match err { From a7d782fc1f8ef3a1fb10f6323b9f57cc45ee0777 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 17:10:42 +0100 Subject: [PATCH 04/12] Implement PATCH for /api/kv --- src/server.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 73ef1cb..c931732 100644 --- a/src/server.rs +++ b/src/server.rs @@ -55,7 +55,15 @@ impl Server { .or(warp::head() .and(store.clone()) .and(warp::query::()) - .and_then(find_key)), + .and_then(find_key)) + .or(warp::patch() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit( + self.configuration.store.max_limit, + )) + .and(filters::body::json()) + .and_then(patch_key)), ); let routes = api_kv.recover(process_error); warp::serve(routes).run(([127, 0, 0, 1], 7021)); @@ -152,12 +160,48 @@ fn find_key(store: Arc, parameters: HeadKeyParameters) -> Result, +} +#[derive(Debug, Deserialize)] +struct PatchValue { + operation: String, +} +fn patch_key( + store: Arc, + parameters: PatchKeyParameters, + patch_value: PatchValue, +) -> Result { + if let Some(key) = parameters.key { + if let Some(_) = store.get(key.clone()) { + match patch_value.operation.as_str() { + "lock" | "unlock" => { + let r = store.switch_lock(key.to_string(), true); + println!("{}", r); + Ok("") + } + _ => Err(warp::reject::custom(Error::InvalidOperation { + operation: patch_value.operation, + })), + } + } else { + Err(warp::reject::custom(Error::KeyNotFound)) + } + } else { + Err(warp::reject::custom(Error::MissingParameter { + name: "key".to_string(), + })) + } +} + fn process_error(err: Rejection) -> Result { if let Some(err) = err.find_cause::() { let code = match err { Error::MissingBody => StatusCode::BAD_REQUEST, Error::MissingParameter { .. } => StatusCode::BAD_REQUEST, Error::KeyNotFound => StatusCode::NOT_FOUND, + Error::InvalidOperation { .. } => StatusCode::BAD_REQUEST, }; let json = warp::reply::json(&JsonMessage { message: err.to_string(), @@ -188,4 +232,6 @@ enum Error { MissingParameter { name: String }, #[snafu(display("The specified key does not exist."))] KeyNotFound, + #[snafu(display("Invalid Operation \"{}\".", operation))] + InvalidOperation { operation: String }, } From 8a9eda405aa1fe228cd5d1278f7fb1fecef78d29 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 17:15:44 +0100 Subject: [PATCH 05/12] Use configured bind address --- src/configuration.rs | 10 +++++----- src/server.rs | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index d00278c..fb8d065 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,4 +1,4 @@ -use std::net::Ipv4Addr; +use std::net::{Ipv4Addr, IpAddr}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Configuration { @@ -14,7 +14,7 @@ impl Configuration { pub fn default() -> Configuration { Configuration { default: Base { - bind_address: Ipv4Addr::LOCALHOST.to_string(), + bind_address: IpAddr::from(Ipv4Addr::LOCALHOST), port: 7021, // TODO: change after implementing SSL port_ssl: 7021, use_ssl: false, @@ -48,9 +48,9 @@ impl Configuration { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Base { - pub bind_address: String, - pub port: i32, - pub port_ssl: i32, + pub bind_address: IpAddr, + pub port: u16, + pub port_ssl: u16, pub use_ssl: bool, } diff --git a/src/server.rs b/src/server.rs index c931732..efad8d1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -66,7 +66,10 @@ impl Server { .and_then(patch_key)), ); let routes = api_kv.recover(process_error); - warp::serve(routes).run(([127, 0, 0, 1], 7021)); + warp::serve(routes).run(( + self.configuration.default.bind_address, + self.configuration.default.port, + )); } } From 62d72d12e9c679a279758f7acf63e5cdb112e319 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 21:50:03 +0100 Subject: [PATCH 06/12] Add JWT token decryption --- src/server.rs | 118 +++++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 44 deletions(-) diff --git a/src/server.rs b/src/server.rs index efad8d1..b8ba3a7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,13 +1,14 @@ use std::io::Read; use std::sync::Arc; +use std::sync::RwLock; use byte_unit::Byte; use bytes::Buf; -use jsonwebtoken::{decode, Validation}; +use jsonwebtoken::Validation; use snafu::{ResultExt, Snafu}; use warp::{self, filters, http::StatusCode, path, Filter, Rejection, Reply}; -use crate::configuration::*; +use crate::configuration::{Configuration, Claims}; use crate::kvstore::KvStore; use crate::logger::{LogLevel, Logger}; @@ -17,58 +18,69 @@ struct JsonMessage { } pub struct Server { - configuration: Configuration, + configuration: Arc>, } impl Server { pub fn new() -> Server { Server { - configuration: Configuration::default(), + configuration: Arc::new(RwLock::new(Configuration::default())), } } pub fn configure(&mut self, configuration: Configuration) { - self.configuration = configuration; + *self.configuration.write().unwrap() = configuration; } pub fn run(&self) { let store = Arc::new(KvStore::new()); let store = warp::any().map(move || store.clone()); - let api_kv = path!("api" / "kv").and(path::end()).and( - warp::get2() - .and(store.clone()) - .and(warp::query::()) - .and_then(get_key) - .or(warp::put2() - .and(store.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - self.configuration.store.max_limit, - )) - .and(warp::body::concat()) - .and_then(put_key)) - .or(warp::delete2() - .and(store.clone()) - .and(warp::query::()) - .and_then(delete_key)) - .or(warp::head() - .and(store.clone()) - .and(warp::query::()) - .and_then(find_key)) - .or(warp::patch() + let config = self.configuration.clone(); + let config = warp::any().map(move || config.clone()); + + let auth = warp::header::optional::("authorization").and(config).and_then(verify_auth).untuple_one(); + + let config = self.configuration.read().unwrap(); + + let api_kv = path!("api" / "kv") + .and(path::end()) + .and(auth) + .and( + warp::get2() .and(store.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - self.configuration.store.max_limit, - )) - .and(filters::body::json()) - .and_then(patch_key)), - ); + .and(warp::query::()) + .and_then(get_key) + .or(warp::put2() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit( + config.store.max_limit, + )) + .and(warp::body::concat()) + .and_then(put_key)) + .or(warp::delete2() + .and(store.clone()) + .and(warp::query::()) + .and_then(delete_key)) + .or(warp::head() + .and(store.clone()) + .and(warp::query::()) + .and_then(find_key)) + .or(warp::patch() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit( + config.store.max_limit, + )) + .and(filters::body::json()) + .and_then(patch_key)), + ); + let routes = api_kv.recover(process_error); warp::serve(routes).run(( - self.configuration.default.bind_address, - self.configuration.default.port, + config.default.bind_address, + config.default.port, )); } } @@ -86,7 +98,7 @@ fn get_key(store: Arc, parameters: GetKeyParameters) -> Result, parameters: HeadKeyParameters) -> Result, config: Arc>) -> Result<(), Rejection> { + if let Some(auth_header) = auth_header { + if let Ok(_bearer) = jsonwebtoken::decode::(auth_header.trim_start_matches("Bearer "), config.read().unwrap().authentication.secret_key.as_ref(), &Validation::default()) { + Ok(()) + } else { + Err(warp::reject::custom(Error::InvalidJwtToken)) + } + } else { + Err(warp::reject::custom(Error::MissingAuthHeader)) + } +} + fn process_error(err: Rejection) -> Result { if let Some(err) = err.find_cause::() { let code = match err { Error::MissingBody => StatusCode::BAD_REQUEST, Error::MissingParameter { .. } => StatusCode::BAD_REQUEST, + Error::MissingAuthHeader => StatusCode::UNAUTHORIZED, Error::KeyNotFound => StatusCode::NOT_FOUND, Error::InvalidOperation { .. } => StatusCode::BAD_REQUEST, + Error::InvalidJwtToken => StatusCode::UNAUTHORIZED, }; let json = warp::reply::json(&JsonMessage { message: err.to_string(), @@ -219,7 +245,7 @@ fn process_error(err: Rejection) -> Result { } else if let Some(_) = err.find_cause::() { let code = StatusCode::METHOD_NOT_ALLOWED; let json = warp::reply::json(&JsonMessage { - message: "Request payload is over {} bytes long.".to_string(), // TODO: format the string + message: "Request payload is too long.".to_string(), // TODO: find a way to format the limit into this string }); Ok(warp::reply::with_status(json, code)) } else { @@ -231,10 +257,14 @@ fn process_error(err: Rejection) -> Result { enum Error { #[snafu(display("Missing request body."))] MissingBody, - #[snafu(display("Missing \"{}\" parameter.", name))] - MissingParameter { name: String }, + #[snafu(display("Missing \"{}\" parameter.", parameter))] + MissingParameter { parameter: String }, + #[snafu(display("Missing Authorization header."))] + MissingAuthHeader, #[snafu(display("The specified key does not exist."))] KeyNotFound, #[snafu(display("Invalid Operation \"{}\".", operation))] InvalidOperation { operation: String }, + #[snafu(display("Invalid JWT token in Authorization header."))] + InvalidJwtToken, } From 95da8e457a97dc35f459532dc9fe70be6a2c48e9 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 23:10:38 +0100 Subject: [PATCH 07/12] Add WebUI routing --- src/server.rs | 99 +++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/src/server.rs b/src/server.rs index b8ba3a7..841f153 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,9 +6,9 @@ use byte_unit::Byte; use bytes::Buf; use jsonwebtoken::Validation; use snafu::{ResultExt, Snafu}; -use warp::{self, filters, http::StatusCode, path, Filter, Rejection, Reply}; +use warp::{self, filters, fs, http::StatusCode, path, Filter, Rejection, Reply}; -use crate::configuration::{Configuration, Claims}; +use crate::configuration::{Claims, Configuration}; use crate::kvstore::KvStore; use crate::logger::{LogLevel, Logger}; @@ -39,49 +39,57 @@ impl Server { let config = self.configuration.clone(); let config = warp::any().map(move || config.clone()); - let auth = warp::header::optional::("authorization").and(config).and_then(verify_auth).untuple_one(); + let auth = warp::header::optional::("authorization") + .and(config.clone()) + .and_then(verify_auth) + .untuple_one(); + + let webui_enabled = config + .clone() + .and_then(|config: Arc>| { + if config.read().unwrap().webui.enabled { + Ok(()) + } else { + Err(warp::reject::not_found()) + } + }) + .untuple_one(); let config = self.configuration.read().unwrap(); - let api_kv = path!("api" / "kv") - .and(path::end()) - .and(auth) - .and( - warp::get2() + let api_kv = path!("api" / "kv").and(path::end()).and(auth).and( + warp::get2() + .and(store.clone()) + .and(warp::query::()) + .and_then(get_key) + .or(warp::put2() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit(config.store.max_limit)) + .and(warp::body::concat()) + .and_then(put_key)) + .or(warp::delete2() .and(store.clone()) - .and(warp::query::()) - .and_then(get_key) - .or(warp::put2() - .and(store.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - config.store.max_limit, - )) - .and(warp::body::concat()) - .and_then(put_key)) - .or(warp::delete2() - .and(store.clone()) - .and(warp::query::()) - .and_then(delete_key)) - .or(warp::head() - .and(store.clone()) - .and(warp::query::()) - .and_then(find_key)) - .or(warp::patch() - .and(store.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - config.store.max_limit, - )) - .and(filters::body::json()) - .and_then(patch_key)), - ); + .and(warp::query::()) + .and_then(delete_key)) + .or(warp::head() + .and(store.clone()) + .and(warp::query::()) + .and_then(find_key)) + .or(warp::patch() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit(config.store.max_limit)) + .and(filters::body::json()) + .and_then(patch_key)), + ); + + let webui = fs::dir("assets") + .or(fs::dir("webui/dist")) + .and(webui_enabled); - let routes = api_kv.recover(process_error); - warp::serve(routes).run(( - config.default.bind_address, - config.default.port, - )); + let routes = api_kv.or(webui).recover(process_error); + warp::serve(routes).run((config.default.bind_address, config.default.port)); } } @@ -210,9 +218,16 @@ fn patch_key( } } -fn verify_auth(auth_header: Option, config: Arc>) -> Result<(), Rejection> { +fn verify_auth( + auth_header: Option, + config: Arc>, +) -> Result<(), Rejection> { if let Some(auth_header) = auth_header { - if let Ok(_bearer) = jsonwebtoken::decode::(auth_header.trim_start_matches("Bearer "), config.read().unwrap().authentication.secret_key.as_ref(), &Validation::default()) { + if let Ok(_bearer) = jsonwebtoken::decode::( + auth_header.trim_start_matches("Bearer "), + config.read().unwrap().authentication.secret_key.as_ref(), + &Validation::default(), + ) { Ok(()) } else { Err(warp::reject::custom(Error::InvalidJwtToken)) From 08c82d2ca1d64ef8c9f79f1f3854c1cdd01244db Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 23:26:55 +0100 Subject: [PATCH 08/12] Add robots.txt route --- src/server.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 841f153..3a9a55a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -88,7 +88,11 @@ impl Server { .or(fs::dir("webui/dist")) .and(webui_enabled); - let routes = api_kv.or(webui).recover(process_error); + let robots = warp::path("robots.txt") + .and(path::end()) + .and(warp::get2().map(|| "User-agent: *\nDisallow: /")); + + let routes = api_kv.or(webui).or(robots).recover(process_error); warp::serve(routes).run((config.default.bind_address, config.default.port)); } } From 4371748c5f1423a1a3c67abc558ecf90f59a4c19 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 23:52:23 +0100 Subject: [PATCH 09/12] Separate request and value size limits --- src/configuration.rs | 11 ++++++++++- src/server.rs | 24 ++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index fb8d065..1ca7c04 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -7,7 +7,8 @@ pub struct Configuration { pub persistence: Persistence, pub encryption: Encryption, pub webui: WebUI, - pub store: Store + pub store: Store, + pub http: Http } impl Configuration { @@ -38,6 +39,9 @@ impl Configuration { store: Store { max_limit: 7340032 }, + http: Http { + request_size_limit: 8388608 + }, } } @@ -83,6 +87,11 @@ pub struct Store { pub max_limit: u64 } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Http { + pub request_size_limit: u64 +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Claims { pub sub: String, diff --git a/src/server.rs b/src/server.rs index 3a9a55a..d38ad61 100644 --- a/src/server.rs +++ b/src/server.rs @@ -55,7 +55,7 @@ impl Server { }) .untuple_one(); - let config = self.configuration.read().unwrap(); + let configuration = self.configuration.read().unwrap(); let api_kv = path!("api" / "kv").and(path::end()).and(auth).and( warp::get2() @@ -64,8 +64,11 @@ impl Server { .and_then(get_key) .or(warp::put2() .and(store.clone()) + .and(config.clone()) .and(warp::query::()) - .and(filters::body::content_length_limit(config.store.max_limit)) + .and(filters::body::content_length_limit( + configuration.http.request_size_limit, + )) .and(warp::body::concat()) .and_then(put_key)) .or(warp::delete2() @@ -79,7 +82,9 @@ impl Server { .or(warp::patch() .and(store.clone()) .and(warp::query::()) - .and(filters::body::content_length_limit(config.store.max_limit)) + .and(filters::body::content_length_limit( + configuration.http.request_size_limit, + )) .and(filters::body::json()) .and_then(patch_key)), ); @@ -93,7 +98,10 @@ impl Server { .and(warp::get2().map(|| "User-agent: *\nDisallow: /")); let routes = api_kv.or(webui).or(robots).recover(process_error); - warp::serve(routes).run((config.default.bind_address, config.default.port)); + warp::serve(routes).run(( + configuration.default.bind_address, + configuration.default.port, + )); } } @@ -121,11 +129,16 @@ struct PutKeyParameters { } fn put_key( store: Arc, + config: Arc>, parameters: PutKeyParameters, body: filters::body::FullBody, ) -> Result { if body.remaining() == 0 { Err(warp::reject::custom(Error::MissingBody)) + } else if body.bytes().len() as u64 > config.read().unwrap().store.max_limit { + Err(warp::reject::custom(Error::ValueSizeLimit { + max_limit: config.read().unwrap().store.max_limit, + })) } else { if let Some(key) = parameters.key { if let Some(_) = store.set(key, body.bytes().to_vec()) { @@ -250,6 +263,7 @@ fn process_error(err: Rejection) -> Result { Error::KeyNotFound => StatusCode::NOT_FOUND, Error::InvalidOperation { .. } => StatusCode::BAD_REQUEST, Error::InvalidJwtToken => StatusCode::UNAUTHORIZED, + Error::ValueSizeLimit { .. } => StatusCode::BAD_REQUEST, }; let json = warp::reply::json(&JsonMessage { message: err.to_string(), @@ -286,4 +300,6 @@ enum Error { InvalidOperation { operation: String }, #[snafu(display("Invalid JWT token in Authorization header."))] InvalidJwtToken, + #[snafu(display("The maximum allowed value size is {} bytes.", max_limit))] + ValueSizeLimit { max_limit: u64 }, } From ac68422688d0d15183e6209bb1b9cb311fe6b39b Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Tue, 12 Nov 2019 23:53:15 +0100 Subject: [PATCH 10/12] Remove unused imports --- src/server.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server.rs b/src/server.rs index d38ad61..031812d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,16 +1,13 @@ -use std::io::Read; use std::sync::Arc; use std::sync::RwLock; -use byte_unit::Byte; use bytes::Buf; use jsonwebtoken::Validation; -use snafu::{ResultExt, Snafu}; +use snafu::Snafu; use warp::{self, filters, fs, http::StatusCode, path, Filter, Rejection, Reply}; use crate::configuration::{Claims, Configuration}; use crate::kvstore::KvStore; -use crate::logger::{LogLevel, Logger}; #[derive(Serialize, Deserialize)] struct JsonMessage { From e76f79523339f296e77bcba6de4db1aca03aa41d Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Wed, 13 Nov 2019 00:13:04 +0100 Subject: [PATCH 11/12] Apply Content-Length limit to all api requests --- src/server.rs | 63 +++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/server.rs b/src/server.rs index 031812d..5d1e94b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -54,37 +54,40 @@ impl Server { let configuration = self.configuration.read().unwrap(); - let api_kv = path!("api" / "kv").and(path::end()).and(auth).and( - warp::get2() - .and(store.clone()) - .and(warp::query::()) - .and_then(get_key) - .or(warp::put2() - .and(store.clone()) - .and(config.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - configuration.http.request_size_limit, - )) - .and(warp::body::concat()) - .and_then(put_key)) - .or(warp::delete2() - .and(store.clone()) - .and(warp::query::()) - .and_then(delete_key)) - .or(warp::head() - .and(store.clone()) - .and(warp::query::()) - .and_then(find_key)) - .or(warp::patch() + let api_kv = path!("api" / "kv") + .and(path::end()) + .and(auth) + .and(filters::body::content_length_limit( + configuration.http.request_size_limit, + )) + .and( + warp::get2() .and(store.clone()) - .and(warp::query::()) - .and(filters::body::content_length_limit( - configuration.http.request_size_limit, - )) - .and(filters::body::json()) - .and_then(patch_key)), - ); + .and(warp::query::()) + .and_then(get_key) + .or(warp::put2() + .and(store.clone()) + .and(config.clone()) + .and(warp::query::()) + .and(filters::body::content_length_limit( + configuration.store.max_limit, + )) + .and(warp::body::concat()) + .and_then(put_key)) + .or(warp::delete2() + .and(store.clone()) + .and(warp::query::()) + .and_then(delete_key)) + .or(warp::head() + .and(store.clone()) + .and(warp::query::()) + .and_then(find_key)) + .or(warp::patch() + .and(store.clone()) + .and(warp::query::()) + .and(filters::body::json()) + .and_then(patch_key)), + ); let webui = fs::dir("assets") .or(fs::dir("webui/dist")) From 4fa02af78c09b913eef2c301e73b4b7923d2cf53 Mon Sep 17 00:00:00 2001 From: CephalonRho Date: Wed, 13 Nov 2019 09:03:48 +0100 Subject: [PATCH 12/12] Skip authentication if it's disabled --- src/server.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/server.rs b/src/server.rs index 5d1e94b..d9fa218 100644 --- a/src/server.rs +++ b/src/server.rs @@ -239,18 +239,23 @@ fn verify_auth( auth_header: Option, config: Arc>, ) -> Result<(), Rejection> { - if let Some(auth_header) = auth_header { - if let Ok(_bearer) = jsonwebtoken::decode::( - auth_header.trim_start_matches("Bearer "), - config.read().unwrap().authentication.secret_key.as_ref(), - &Validation::default(), - ) { - Ok(()) + let config = config.read().unwrap(); + if config.authentication.enabled { + if let Some(auth_header) = auth_header { + if let Ok(_bearer) = jsonwebtoken::decode::( + auth_header.trim_start_matches("Bearer "), + config.authentication.secret_key.as_ref(), + &Validation::default(), + ) { + Ok(()) + } else { + Err(warp::reject::custom(Error::InvalidJwtToken)) + } } else { - Err(warp::reject::custom(Error::InvalidJwtToken)) + Err(warp::reject::custom(Error::MissingAuthHeader)) } } else { - Err(warp::reject::custom(Error::MissingAuthHeader)) + Ok(()) } }