Use headers crate for basic auth

This commit is contained in:
Raphaël Thériault 2020-10-01 16:54:30 -04:00
parent 6da60a96b6
commit daef6e9b94
6 changed files with 66 additions and 115 deletions

View File

@ -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 }}

55
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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<Extract = (Option<User>,), 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<Extract = (User,), Error = Rejection> + 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<User, Rejection> {
if &header[..5] != "Basic" {
return Err(crate::reject::unauthorized());
}
fn user(header: HeaderValue, db: &Db, config: &Config) -> Result<User, Rejection> {
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());
}

View File

@ -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<T: ToString>(reply: T, status: StatusCode) -> Rejection {
}
pub trait TryExt<T> {
fn or_404(self) -> Result<T, Rejection>;
fn or_400(self) -> Result<T, Rejection>;
fn or_401(self) -> Result<T, Rejection>;
fn or_500(self) -> Result<T, Rejection>;
fn or_404(self) -> Result<T, Rejection>;
fn or_409(self) -> Result<T, Rejection>;
fn or_500(self) -> Result<T, Rejection>;
}
impl<T, E: Display> TryExt<T> for Result<T, E> {
fn or_400(self) -> Result<T, Rejection> {
self.map_err(|e| {
tracing::info!("{}", e);
warp::reject::custom(FiliteRejection::BadRequest)
})
}
fn or_401(self) -> Result<T, Rejection> {
self.map_err(|e| {
tracing::info!("{}", e);
warp::reject::custom(FiliteRejection::Unauthorized)
})
}
fn or_404(self) -> Result<T, Rejection> {
self.map_err(|e| {
tracing::info!("{}", e);
warp::reject::custom(FiliteRejection::NotFound)
})
}
fn or_401(self) -> Result<T, Rejection> {
fn or_409(self) -> Result<T, Rejection> {
self.map_err(|e| {
tracing::info!("{}", e);
warp::reject::custom(FiliteRejection::Unauthorized)
warp::reject::custom(FiliteRejection::Conflict)
})
}
@ -80,31 +98,25 @@ impl<T, E: Display> TryExt<T> for Result<T, E> {
warp::reject::custom(FiliteRejection::InternalServerError)
})
}
fn or_409(self) -> Result<T, Rejection> {
self.map_err(|e| {
tracing::info!("{}", e);
warp::reject::custom(FiliteRejection::Conflict)
})
}
}
impl<T> TryExt<T> for Option<T> {
fn or_400(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::BadRequest))
}
fn or_401(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::Unauthorized))
}
fn or_404(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::NotFound))
}
fn or_401(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::Unauthorized))
fn or_409(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::Conflict))
}
fn or_500(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::InternalServerError))
}
fn or_409(self) -> Result<T, Rejection> {
self.ok_or_else(|| warp::reject::custom(FiliteRejection::Conflict))
}
}
#[tracing::instrument(level = "debug")]

View File

@ -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(