From 2b79df8e4290c6d7f2800c7086cf815aae54bf83 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Wed, 16 Apr 2025 10:46:19 +0200 Subject: [PATCH 1/2] no funny business w/ get params --- Cargo.lock | 1 + Cargo.toml | 1 + src/tera/mod.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3f3426..ea63be2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2544,6 +2544,7 @@ name = "rot" version = "0.1.0" dependencies = [ "argon2", + "base64", "chrono", "chrono-tz 0.10.3", "csv", diff --git a/Cargo.toml b/Cargo.toml index 4a16639..921b158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ job_scheduler_ng = "2.0" ureq = { version = "3.0", features = ["json"] } regex = "1.10" urlencoding = "2.1" +base64 = "0.22" [target.'cfg(not(windows))'.dependencies] openssl = { version = "0.10", features = [ "vendored" ] } diff --git a/src/tera/mod.rs b/src/tera/mod.rs index 1396a76..44d0890 100644 --- a/src/tera/mod.rs +++ b/src/tera/mod.rs @@ -9,7 +9,7 @@ use rocket::{ get, http::{Cookie, Status}, post, - request::FlashMessage, + request::{FlashMessage, FromRequest, Outcome}, response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, @@ -123,9 +123,57 @@ async fn wikiauth(db: &State, login: Form>) -> String "FAIL".into() } -#[get("/?&")] -async fn nextcloud_auth(db: &State, username: String, password: String) -> Status { - if let Ok(user) = User::login(db, &username, &password).await { +struct BasicAuth { + username: String, + password: String, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for BasicAuth { + type Error = (); + + async fn from_request(request: &'r Request<'_>) -> Outcome { + // Get the Authorization header + let auth_header = match request.headers().get_one("Authorization") { + Some(h) => h, + None => return Outcome::Failure((Status::Unauthorized, ())), + }; + + // Check if it's a Basic auth header + if !auth_header.starts_with("Basic ") { + return Outcome::Failure((Status::Unauthorized, ())); + } + + // Decode the base64 credentials + let credentials = match BASE64.decode(auth_header[6..].as_bytes()) { + Ok(c) => c, + Err(_) => return Outcome::Failure((Status::Unauthorized, ())), + }; + + // Convert to UTF-8 string + let credentials_str = match str::from_utf8(&credentials) { + Ok(s) => s, + Err(_) => return Outcome::Failure((Status::Unauthorized, ())), + }; + + // Split into username and password + let mut parts = credentials_str.splitn(2, ':'); + let username = match parts.next() { + Some(u) => u.to_string(), + None => return Outcome::Failure((Status::Unauthorized, ())), + }; + let password = match parts.next() { + Some(p) => p.to_string(), + None => return Outcome::Failure((Status::Unauthorized, ())), + }; + + Outcome::Success(BasicAuth { username, password }) + } +} + +#[get("/")] +async fn nextcloud_auth(db: &State, auth: BasicAuth) -> Status { + if let Ok(user) = User::login(db, &auth.username, &auth.password).await { if user.has_role(db, "admin").await { return Status::Ok; } -- 2.47.2 From dc2ee38aa024816b1f9892d1cab333e0e496ae14 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Wed, 16 Apr 2025 10:56:57 +0200 Subject: [PATCH 2/2] no funny business w/ get params --- src/tera/mod.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tera/mod.rs b/src/tera/mod.rs index 44d0890..44cba1a 100644 --- a/src/tera/mod.rs +++ b/src/tera/mod.rs @@ -30,6 +30,7 @@ use crate::{ }, SCHECKBUCH, }; +use base64::alphabet::STANDARD; pub(crate) mod admin; mod auth; @@ -136,35 +137,35 @@ impl<'r> FromRequest<'r> for BasicAuth { // Get the Authorization header let auth_header = match request.headers().get_one("Authorization") { Some(h) => h, - None => return Outcome::Failure((Status::Unauthorized, ())), + None => return Outcome::Error((Status::Unauthorized, ())), }; // Check if it's a Basic auth header if !auth_header.starts_with("Basic ") { - return Outcome::Failure((Status::Unauthorized, ())); + return Outcome::Error((Status::Unauthorized, ())); } // Decode the base64 credentials - let credentials = match BASE64.decode(auth_header[6..].as_bytes()) { + let credentials = match base64::decode(&auth_header[6..]) { Ok(c) => c, - Err(_) => return Outcome::Failure((Status::Unauthorized, ())), + Err(_) => return Outcome::Error((Status::Unauthorized, ())), }; // Convert to UTF-8 string - let credentials_str = match str::from_utf8(&credentials) { + let credentials_str = match std::str::from_utf8(&credentials) { Ok(s) => s, - Err(_) => return Outcome::Failure((Status::Unauthorized, ())), + Err(_) => return Outcome::Error((Status::Unauthorized, ())), }; // Split into username and password let mut parts = credentials_str.splitn(2, ':'); let username = match parts.next() { Some(u) => u.to_string(), - None => return Outcome::Failure((Status::Unauthorized, ())), + None => return Outcome::Error((Status::Unauthorized, ())), }; let password = match parts.next() { Some(p) => p.to_string(), - None => return Outcome::Failure((Status::Unauthorized, ())), + None => return Outcome::Error((Status::Unauthorized, ())), }; Outcome::Success(BasicAuth { username, password }) -- 2.47.2