diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 11e02c2..680b6ff 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -55,7 +55,7 @@ jobs: cache-to: type=local,dest=/tmp/.buildx-cache labels: | org.opencontainers.image.title=${{ github.event.repository.name }} - org.opencontainers.image.description='${{ github.event.repository.description }}' + org.opencontainers.image.description=${{ github.event.repository.description }} org.opencontainers.image.url=${{ github.event.repository.html_url }} org.opencontainers.image.source=${{ github.event.repository.clone_url }} org.opencontainers.image.version=${{ steps.prep.outputs.version }} diff --git a/Cargo.lock b/Cargo.lock index 02866d9..9608ebe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,11 +276,10 @@ version = "0.3.0" dependencies = [ "anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "askama 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.18 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "headers 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)", @@ -329,7 +328,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -350,32 +348,11 @@ name = "futures-core" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures-executor" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "futures-io" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures-macro" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "futures-sink" version = "0.3.5" @@ -385,27 +362,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "futures-task" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "futures-util" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -744,11 +711,6 @@ dependencies = [ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "once_cell" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "opaque-debug" version = "0.2.3" @@ -840,16 +802,6 @@ dependencies = [ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro-nested" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "proc-macro2" version = "1.0.18" @@ -1581,9 +1533,7 @@ dependencies = [ "checksum futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" "checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" "checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" -"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" "checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" -"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" "checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" "checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" "checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" @@ -1625,7 +1575,6 @@ dependencies = [ "checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" "checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" "checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum parking_lot 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" "checksum parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" @@ -1637,8 +1586,6 @@ dependencies = [ "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" "checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" -"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" -"checksum proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" "checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" "checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" diff --git a/Cargo.toml b/Cargo.toml index ef3fbbf..2c962a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,11 +17,10 @@ license = "MIT" [dependencies] anyhow = "1.0.32" askama = "0.10.3" -base64 = "0.12.3" bincode = "1.3.1" bytes = "0.5.6" chrono = { version = "0.4.18", features = ["serde"] } -futures = "0.3.5" +headers = "0.3.2" rand = "0.7.3" rust-argon2 = "0.8.2" serde = { version = "1.0.116", features = ["derive"] } diff --git a/src/auth.rs b/src/auth.rs index dee611d..ba096b2 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -4,56 +4,49 @@ use crate::{ reject::TryExt, }; use anyhow::Result; +use headers::authorization::{Basic, Credentials}; use rand::Rng; use sled::Db; use tokio::task; -use warp::{Filter, Rejection}; +use warp::{http::HeaderValue, Filter, Rejection}; pub fn optional( db: &'static Db, config: &'static Config, ) -> impl Filter,), Error = Rejection> + Copy + Send + Sync + 'static { - warp::header::optional("Authorization").and_then(move |header| async move { - match header { - Some(h) => match user(h, db, config).await { + warp::header::value("Authorization") + .and_then(move |header| async move { + match user(header, db, config) { Ok(u) => Ok(Some(u)), Err(e) => Err(e), - }, - None => Ok(None), - } - }) + } + }) + .or(warp::any().and_then(|| async move { Result::<_, Rejection>::Ok(None) })) + .unify() } pub fn required( db: &'static Db, config: &'static Config, ) -> impl Filter + Copy + Send + Sync + 'static { - warp::header::header("Authorization").and_then(move |header| user(header, db, config)) + warp::header::value("Authorization") + .and_then(move |header| async move { user(header, db, config) }) } #[tracing::instrument(level = "debug", skip(db))] -async fn user(header: String, db: &Db, config: &Config) -> Result { - if &header[..5] != "Basic" { - return Err(crate::reject::unauthorized()); - } +fn user(header: HeaderValue, db: &Db, config: &Config) -> Result { + let credentials = Basic::decode(&header).or_400()?; - let decoded = base64::decode(&header[6..]).or_401()?; - - let (user, password) = { - let mut split = None; - for (i, b) in decoded.iter().copied().enumerate() { - if b == b':' { - split = Some(i); - } - } - let split = split.or_401()?; - - let (u, p) = (&decoded[..split], &decoded[(split + 1)..]); - (std::str::from_utf8(u).or_401()?, p) - }; - - let user = crate::db::user(user, db).or_500()?.or_401()?; - if !verify(&user.password_hash, password, &config.password).or_500()? { + let user = crate::db::user(credentials.username(), db) + .or_500()? + .or_401()?; + if !verify( + &user.password_hash, + credentials.password().as_bytes(), + &config.password, + ) + .or_500()? + { return Err(crate::reject::unauthorized()); } diff --git a/src/reject.rs b/src/reject.rs index 74658ce..8c0f044 100644 --- a/src/reject.rs +++ b/src/reject.rs @@ -7,10 +7,11 @@ use warp::{ #[derive(Debug, Clone)] enum FiliteRejection { - NotFound, + BadRequest, Unauthorized, - InternalServerError, + NotFound, Conflict, + InternalServerError, Custom(String, StatusCode), } @@ -18,8 +19,8 @@ impl Reject for FiliteRejection {} impl Reply for FiliteRejection { fn into_response(self) -> Response { match self { - Self::NotFound => { - warp::reply::with_status("Not Found", StatusCode::NOT_FOUND).into_response() + Self::BadRequest => { + warp::reply::with_status("Bad Request", StatusCode::BAD_REQUEST).into_response() } Self::Unauthorized => warp::reply::with_status( warp::reply::with_header( @@ -30,13 +31,17 @@ impl Reply for FiliteRejection { StatusCode::UNAUTHORIZED, ) .into_response(), - Self::InternalServerError => { - warp::reply::with_status("Internal Server Error", StatusCode::INTERNAL_SERVER_ERROR) - .into_response() + Self::NotFound => { + warp::reply::with_status("Not Found", StatusCode::NOT_FOUND).into_response() } Self::Conflict => { warp::reply::with_status("Conflict", StatusCode::CONFLICT).into_response() } + Self::InternalServerError => { + warp::reply::with_status("Internal Server Error", StatusCode::INTERNAL_SERVER_ERROR) + .into_response() + } + Self::Custom(reply, status) => warp::reply::with_status(reply, status).into_response(), } } @@ -53,24 +58,37 @@ pub fn custom(reply: T, status: StatusCode) -> Rejection { } pub trait TryExt { - fn or_404(self) -> Result; + fn or_400(self) -> Result; fn or_401(self) -> Result; - fn or_500(self) -> Result; + fn or_404(self) -> Result; fn or_409(self) -> Result; + + fn or_500(self) -> Result; } impl TryExt for Result { + fn or_400(self) -> Result { + self.map_err(|e| { + tracing::info!("{}", e); + warp::reject::custom(FiliteRejection::BadRequest) + }) + } + fn or_401(self) -> Result { + self.map_err(|e| { + tracing::info!("{}", e); + warp::reject::custom(FiliteRejection::Unauthorized) + }) + } fn or_404(self) -> Result { self.map_err(|e| { tracing::info!("{}", e); warp::reject::custom(FiliteRejection::NotFound) }) } - - fn or_401(self) -> Result { + fn or_409(self) -> Result { self.map_err(|e| { tracing::info!("{}", e); - warp::reject::custom(FiliteRejection::Unauthorized) + warp::reject::custom(FiliteRejection::Conflict) }) } @@ -80,31 +98,25 @@ impl TryExt for Result { warp::reject::custom(FiliteRejection::InternalServerError) }) } - - fn or_409(self) -> Result { - self.map_err(|e| { - tracing::info!("{}", e); - warp::reject::custom(FiliteRejection::Conflict) - }) - } } impl TryExt for Option { + fn or_400(self) -> Result { + self.ok_or_else(|| warp::reject::custom(FiliteRejection::BadRequest)) + } + fn or_401(self) -> Result { + self.ok_or_else(|| warp::reject::custom(FiliteRejection::Unauthorized)) + } fn or_404(self) -> Result { self.ok_or_else(|| warp::reject::custom(FiliteRejection::NotFound)) } - - fn or_401(self) -> Result { - self.ok_or_else(|| warp::reject::custom(FiliteRejection::Unauthorized)) + fn or_409(self) -> Result { + self.ok_or_else(|| warp::reject::custom(FiliteRejection::Conflict)) } fn or_500(self) -> Result { self.ok_or_else(|| warp::reject::custom(FiliteRejection::InternalServerError)) } - - fn or_409(self) -> Result { - self.ok_or_else(|| warp::reject::custom(FiliteRejection::Conflict)) - } } #[tracing::instrument(level = "debug")] diff --git a/src/routes.rs b/src/routes.rs index 4a3a9b7..09591b6 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -5,10 +5,10 @@ use crate::{ }; use bytes::Bytes; use sled::Db; -use warp::reply::Response; use warp::{ http::{StatusCode, Uri}, - Filter, Rejection, Reply, + reply::{Reply, Response}, + Filter, Rejection, }; pub fn handler(