diff --git a/crates/ezidam/Cargo.toml b/crates/ezidam/Cargo.toml index 3450b7b..ae07f1e 100644 --- a/crates/ezidam/Cargo.toml +++ b/crates/ezidam/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -rocket = { version = "=0.5.0-rc.3", features = ["json"] } -rocket_db_pools = { version = "=0.1.0-rc.3", features = ["sqlx_sqlite"] } -rocket_dyn_templates = { version = "=0.1.0-rc.3", features = ["tera"] } +rocket = { version = "=0.5.0-rc.4", features = ["json"] } +rocket_db_pools = { version = "=0.1.0-rc.4", features = ["sqlx_sqlite"] } +rocket_dyn_templates = { version = "=0.1.0-rc.4", features = ["tera"] } infer = { version = "0.13", default-features = false } erased-serde = "0.3" url = { workspace = true } diff --git a/crates/ezidam/src/guards/access_token.rs b/crates/ezidam/src/guards/access_token.rs index 476d2d2..0756af9 100644 --- a/crates/ezidam/src/guards/access_token.rs +++ b/crates/ezidam/src/guards/access_token.rs @@ -35,7 +35,7 @@ impl AccessToken { }, Err(e) => match e { Outcome::Success(s) => Ok(Some(Self(s))), - Outcome::Failure(e) => Err(BearerAuthError::Jwt(e.1)), + Outcome::Error(e) => Err(BearerAuthError::Jwt(e.1)), Outcome::Forward(_) => Ok(None), }, } @@ -50,15 +50,15 @@ impl<'r> FromRequest<'r> for AccessToken { let keys: Vec<_> = request.headers().get("Authorization").collect(); match keys.len() { - 0 => Outcome::Forward(()), + 0 => Outcome::Forward(Status::Unauthorized), 1 => match AccessToken::from_bearer(keys[0], request).await { Ok(access_token) => match access_token { Some(access_token) => Outcome::Success(access_token), - None => Outcome::Forward(()), + None => Outcome::Forward(Status::Unauthorized), }, - Err(e) => Outcome::Failure((Status::Unauthorized, e)), + Err(e) => Outcome::Error((Status::Unauthorized, e)), }, - _ => Outcome::Failure((Status::BadRequest, BearerAuthError::BadCount)), + _ => Outcome::Error((Status::BadRequest, BearerAuthError::BadCount)), } } } diff --git a/crates/ezidam/src/guards/basic_auth.rs b/crates/ezidam/src/guards/basic_auth.rs index a8a4d2b..cccb549 100644 --- a/crates/ezidam/src/guards/basic_auth.rs +++ b/crates/ezidam/src/guards/basic_auth.rs @@ -56,12 +56,12 @@ impl<'r> FromRequest<'r> for BasicAuth { let keys: Vec<_> = request.headers().get("Authorization").collect(); match keys.len() { - 0 => Outcome::Forward(()), + 0 => Outcome::Forward(Status::BadRequest), 1 => match BasicAuth::from_base64(keys[0]) { Ok(auth_header) => Outcome::Success(auth_header), - Err(e) => Outcome::Failure((Status::BadRequest, e)), + Err(e) => Outcome::Error((Status::BadRequest, e)), }, - _ => Outcome::Failure((Status::BadRequest, BasicAuthError::BadCount)), + _ => Outcome::Error((Status::BadRequest, BasicAuthError::BadCount)), } } } diff --git a/crates/ezidam/src/guards/completed_setup.rs b/crates/ezidam/src/guards/completed_setup.rs index 057eb35..2a38e15 100644 --- a/crates/ezidam/src/guards/completed_setup.rs +++ b/crates/ezidam/src/guards/completed_setup.rs @@ -19,16 +19,16 @@ impl<'r> FromRequest<'r> for CompletedSetup { async fn from_request(request: &'r Request<'_>) -> Outcome { let db = match request.guard::<&Database>().await { Outcome::Success(database) => database, - Outcome::Failure(e) => return Outcome::Failure((e.0, Error::GetDatabase)), + Outcome::Error(e) => return Outcome::Error((e.0, Error::GetDatabase)), Outcome::Forward(f) => return Outcome::Forward(f), }; match User::get_initial_admin(&**db).await { Ok(initial_admin) => match initial_admin { Some(_) => Outcome::Success(CompletedSetup), - None => Outcome::Forward(()), + None => Outcome::Forward(Status::PermanentRedirect), }, - Err(e) => Outcome::Failure((Status::InternalServerError, Error::Request(e))), + Err(e) => Outcome::Error((Status::InternalServerError, Error::Request(e))), } } } diff --git a/crates/ezidam/src/guards/jwt.rs b/crates/ezidam/src/guards/jwt.rs index ddc5f57..83fdb97 100644 --- a/crates/ezidam/src/guards/jwt.rs +++ b/crates/ezidam/src/guards/jwt.rs @@ -16,6 +16,7 @@ use rocket::time::Duration; use rocket::tokio::task; use rocket::Request; use settings::Settings; +use std::ops::DerefMut; use users::User; mod admin; @@ -78,14 +79,14 @@ pub async fn validate_jwt( // Get database let db = match request.guard::<&Database>().await { Outcome::Success(database) => database, - Outcome::Failure(e) => return Err(Outcome::Failure((e.0, Error::GetDatabase))), + Outcome::Error(e) => return Err(Outcome::Error((e.0, Error::GetDatabase))), Outcome::Forward(f) => return Err(Outcome::Forward(f)), }; let mut transaction = match db.begin().await { Ok(transaction) => transaction, Err(_e) => { - return Err(Outcome::Failure(( + return Err(Outcome::Error(( Status::InternalServerError, Error::StartTransaction, ))); @@ -93,10 +94,10 @@ pub async fn validate_jwt( }; // Get keys - let keys = match Key::get_all(&mut transaction, Some(false)).await { + let keys = match Key::get_all(transaction.deref_mut(), Some(false)).await { Ok(keys) => keys, Err(e) => { - return Err(Outcome::Failure(( + return Err(Outcome::Error(( Status::InternalServerError, Error::Keys(e), ))) @@ -105,10 +106,10 @@ pub async fn validate_jwt( if let Some(specific_user) = specific_user { // Get settings - let settings = match Settings::get(&mut transaction).await { + let settings = match Settings::get(transaction.deref_mut()).await { Ok(settings) => settings, Err(e) => { - return Err(Outcome::Failure(( + return Err(Outcome::Error(( Status::InternalServerError, Error::GetSettings(e), ))); @@ -119,7 +120,7 @@ pub async fn validate_jwt( let first_admin = match settings.first_admin() { Some(home_page) => UserID(home_page.to_string()), None => { - return Err(Outcome::Failure(( + return Err(Outcome::Error(( Status::InternalServerError, Error::FirstAdminNotSet, ))); @@ -133,7 +134,7 @@ pub async fn validate_jwt( } if let Err(_e) = transaction.commit().await { - return Err(Outcome::Failure(( + return Err(Outcome::Error(( Status::InternalServerError, Error::CommitTransaction, ))); @@ -191,11 +192,11 @@ pub async fn validate_jwt( // Return jwt claims Ok(claims) } - Err(_e) => Err(Outcome::Forward(())), + Err(_e) => Err(Outcome::Forward(Status::Unauthorized)), }, Err(e) => { // Failed to run blocking task - Err(Outcome::Failure(( + Err(Outcome::Error(( Status::InternalServerError, Error::BlockingTask(e.to_string()), ))) @@ -211,92 +212,92 @@ pub async fn use_refresh_token( // Get database let db = match request.guard::<&Database>().await { Outcome::Success(database) => database, - Outcome::Failure(e) => return Outcome::Failure((e.0, Error::GetDatabase)), + Outcome::Error(e) => return Outcome::Error((e.0, Error::GetDatabase)), Outcome::Forward(f) => return Outcome::Forward(f), }; // Get cookies let cookie_jar = match request.guard::<&CookieJar>().await { Outcome::Success(cookie_jar) => cookie_jar, - Outcome::Failure(e) => return Outcome::Failure((e.0, Error::GetCookies)), + Outcome::Error(e) => return Outcome::Error((e.0, Error::GetCookies)), Outcome::Forward(f) => return Outcome::Forward(f), }; let mut transaction = match db.begin().await { Ok(transaction) => transaction, Err(_e) => { - return Outcome::Failure((Status::InternalServerError, Error::StartTransaction)); + return Outcome::Error((Status::InternalServerError, Error::StartTransaction)); } }; - let refresh_token = match RefreshToken::get_one(&mut transaction, &refresh).await { + let refresh_token = match RefreshToken::get_one(transaction.deref_mut(), &refresh).await { Ok(refresh_token) => match refresh_token { Some(refresh_token) => refresh_token, - None => return Outcome::Forward(()), + None => return Outcome::Forward(Status::Unauthorized), }, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::GetRefreshToken(e))); + return Outcome::Error((Status::InternalServerError, Error::GetRefreshToken(e))); } }; - let user = match User::get_one_from_refresh_token(&mut transaction, &refresh).await { + let user = match User::get_one_from_refresh_token(transaction.deref_mut(), &refresh).await { Ok(user) => match user { Some(user) => user, None => { - return Outcome::Failure((Status::InternalServerError, Error::UserNotFound)); + return Outcome::Error((Status::InternalServerError, Error::UserNotFound)); } }, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::GetUser(e))); + return Outcome::Error((Status::InternalServerError, Error::GetUser(e))); } }; // make sure that `get_admin` is respected, dont generate token for unwanted users! if let Some(get_admin) = get_admin { if user.is_admin() != get_admin { - return Outcome::Forward(()); + return Outcome::Forward(Status::Forbidden); } } if refresh_token.has_been_used() { // Revoke all tokens for user if let Err(e) = - RefreshToken::revoke_all_for_user(&mut transaction, refresh_token.user()).await + RefreshToken::revoke_all_for_user(transaction.deref_mut(), refresh_token.user()).await { - return Outcome::Failure((Status::InternalServerError, Error::RevokeRefreshTokens(e))); + return Outcome::Error((Status::InternalServerError, Error::RevokeRefreshTokens(e))); } if let Err(_e) = transaction.commit().await { - return Outcome::Failure((Status::InternalServerError, Error::CommitTransaction)); + return Outcome::Error((Status::InternalServerError, Error::CommitTransaction)); } - return Outcome::Forward(()); + return Outcome::Forward(Status::Unauthorized); } if refresh_token.is_revoked() { - return Outcome::Forward(()); + return Outcome::Forward(Status::Unauthorized); } if refresh_token.has_expired() { - return Outcome::Forward(()); + return Outcome::Forward(Status::Unauthorized); } - if let Err(e) = refresh_token.use_token(&mut transaction).await { - return Outcome::Failure((Status::InternalServerError, Error::MarkRefreshTokenUsed(e))); + if let Err(e) = refresh_token.use_token(transaction.deref_mut()).await { + return Outcome::Error((Status::InternalServerError, Error::MarkRefreshTokenUsed(e))); } // Get base url - let settings = match Settings::get(&mut transaction).await { + let settings = match Settings::get(transaction.deref_mut()).await { Ok(settings) => settings, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::GetSettings(e))); + return Outcome::Error((Status::InternalServerError, Error::GetSettings(e))); } }; let home_page = match settings.url().map(String::from) { Some(home_page) => home_page, None => { - return Outcome::Failure((Status::InternalServerError, Error::ServerUrlNotSet)); + return Outcome::Error((Status::InternalServerError, Error::ServerUrlNotSet)); } }; @@ -304,7 +305,7 @@ pub async fn use_refresh_token( let new_refresh_token = match task::spawn_blocking(SecretString::default).await { Ok(new_refresh_token) => new_refresh_token, Err(e) => { - return Outcome::Failure(( + return Outcome::Error(( Status::InternalServerError, Error::BlockingTask(e.to_string()), )); @@ -315,13 +316,13 @@ pub async fn use_refresh_token( let ip_address = match request.client_ip() { Some(ip) => ip.to_string(), None => { - return Outcome::Failure((Status::BadRequest, Error::UnknownIp)); + return Outcome::Error((Status::BadRequest, Error::UnknownIp)); } }; // Insert refresh token in database if let Err(e) = RefreshToken::insert( - &mut transaction, + transaction.deref_mut(), new_refresh_token.as_ref(), ip_address, user.id(), @@ -330,7 +331,7 @@ pub async fn use_refresh_token( ) .await { - return Outcome::Failure((Status::InternalServerError, Error::SaveRefreshToken(e))); + return Outcome::Error((Status::InternalServerError, Error::SaveRefreshToken(e))); } // Add refresh token as a cookie @@ -345,24 +346,21 @@ pub async fn use_refresh_token( cookie_jar.add(cookie); // Get latest key from database - let key = match Key::get_most_recent(&mut transaction).await { + let key = match Key::get_most_recent(transaction.deref_mut()).await { Ok(key) => match key { Some(key) => key, None => { - return Outcome::Failure(( - Status::InternalServerError, - Error::MostRecentKeyNotFound, - )); + return Outcome::Error((Status::InternalServerError, Error::MostRecentKeyNotFound)); } }, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::GetKey(e))); + return Outcome::Error((Status::InternalServerError, Error::GetKey(e))); } }; // Make sure key has not been revoked if key.is_revoked() { - return Outcome::Failure((Status::InternalServerError, Error::MostRecentKeyRevoked)); + return Outcome::Error((Status::InternalServerError, Error::MostRecentKeyRevoked)); } // Import private key @@ -373,11 +371,11 @@ pub async fn use_refresh_token( Ok(private_key) => match private_key { Ok(private_key) => private_key, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::ImportKey(e))); + return Outcome::Error((Status::InternalServerError, Error::ImportKey(e))); } }, Err(e) => { - return Outcome::Failure(( + return Outcome::Error(( Status::InternalServerError, Error::BlockingTask(e.to_string()), )); @@ -385,13 +383,13 @@ pub async fn use_refresh_token( }; // Get user roles - let roles = match Permission::get_all(&mut transaction, Some(user.id()), None).await { + let roles = match Permission::get_all(transaction.deref_mut(), Some(user.id()), None).await { Ok(roles) => roles .into_iter() .map(|role| role.role().to_string()) .collect(), Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::GetPermissions(e))); + return Outcome::Error((Status::InternalServerError, Error::GetPermissions(e))); } }; @@ -403,7 +401,7 @@ pub async fn use_refresh_token( { Ok(jwt) => jwt, Err(e) => { - return Outcome::Failure((Status::InternalServerError, Error::SignJwt(e))); + return Outcome::Error((Status::InternalServerError, Error::SignJwt(e))); } }; @@ -416,7 +414,7 @@ pub async fn use_refresh_token( cookie_jar.add(cookie); if let Err(_e) = transaction.commit().await { - return Outcome::Failure((Status::InternalServerError, Error::CommitTransaction)); + return Outcome::Error((Status::InternalServerError, Error::CommitTransaction)); } Outcome::Success(jwt_claims) @@ -436,7 +434,7 @@ pub async fn use_access_token_or_refresh_token( match validate_jwt(access, request, get_admin, specific_user).await { Ok(jwt_claims) => match jwt_claims { Some(jwt_claims) => Outcome::Success(jwt_claims), - None => Outcome::Forward(()), + None => Outcome::Forward(Status::Unauthorized), }, Err(e) => e, } @@ -447,7 +445,7 @@ pub async fn use_access_token_or_refresh_token( } (None, None) => { // Nothing to do - Outcome::Forward(()) + Outcome::Forward(Status::Unauthorized) } } } diff --git a/crates/ezidam/src/guards/jwt/admin_not_current.rs b/crates/ezidam/src/guards/jwt/admin_not_current.rs index 6ce90c8..6145a11 100644 --- a/crates/ezidam/src/guards/jwt/admin_not_current.rs +++ b/crates/ezidam/src/guards/jwt/admin_not_current.rs @@ -2,6 +2,7 @@ use super::Error; use crate::guards::{use_access_token_or_refresh_token, SpecificUser}; use crate::id::RocketUserID; use jwt::JwtClaims; +use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; use rocket::Request; @@ -30,7 +31,7 @@ impl<'r> FromRequest<'r> for JwtAdminNotCurrent { ret } { Some(user_id) => user_id, - None => return Outcome::Forward(()), + None => return Outcome::Forward(Status::Forbidden), }; // Don't allow first admin @@ -49,10 +50,10 @@ impl<'r> FromRequest<'r> for JwtAdminNotCurrent { if success.0.subject != user_id.0 .0 { Outcome::Success(success) } else { - Outcome::Forward(()) + Outcome::Forward(Status::Forbidden) } } - Outcome::Failure(failure) => Outcome::Failure(failure), + Outcome::Error(failure) => Outcome::Error(failure), Outcome::Forward(forward) => Outcome::Forward(forward), } } diff --git a/crates/ezidam/src/guards/need_setup.rs b/crates/ezidam/src/guards/need_setup.rs index 953c50b..5f39172 100644 --- a/crates/ezidam/src/guards/need_setup.rs +++ b/crates/ezidam/src/guards/need_setup.rs @@ -19,16 +19,16 @@ impl<'r> FromRequest<'r> for NeedSetup { async fn from_request(request: &'r Request<'_>) -> Outcome { let db = match request.guard::<&Database>().await { Outcome::Success(database) => database, - Outcome::Failure(e) => return Outcome::Failure((e.0, Error::GetDatabase)), + Outcome::Error(e) => return Outcome::Error((e.0, Error::GetDatabase)), Outcome::Forward(f) => return Outcome::Forward(f), }; match User::get_initial_admin(&**db).await { Ok(initial_admin) => match initial_admin { - Some(_) => Outcome::Forward(()), + Some(_) => Outcome::Forward(Status::PermanentRedirect), None => Outcome::Success(NeedSetup), }, - Err(e) => Outcome::Failure((Status::InternalServerError, Error::Request(e))), + Err(e) => Outcome::Error((Status::InternalServerError, Error::Request(e))), } } } diff --git a/crates/ezidam/src/guards/refresh_token.rs b/crates/ezidam/src/guards/refresh_token.rs index c92eac4..8c95876 100644 --- a/crates/ezidam/src/guards/refresh_token.rs +++ b/crates/ezidam/src/guards/refresh_token.rs @@ -1,4 +1,5 @@ use crate::tokens::REFRESH_TOKEN_COOKIE_NAME; +use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; use rocket::Request; @@ -26,7 +27,7 @@ impl<'r> FromRequest<'r> for RefreshToken { async fn from_request(request: &'r Request<'_>) -> Outcome { match get_refresh_token_from_cookie(request) { Some(refresh_token) => Outcome::Success(Self(refresh_token)), - None => Outcome::Forward(()), + None => Outcome::Forward(Status::Unauthorized), } } } diff --git a/crates/ezidam/src/guards/totp_request.rs b/crates/ezidam/src/guards/totp_request.rs index 16cd50f..31fc6cb 100644 --- a/crates/ezidam/src/guards/totp_request.rs +++ b/crates/ezidam/src/guards/totp_request.rs @@ -1,3 +1,4 @@ +use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; use rocket::Request; use users::totp_login_request::{TOTP_REQUEST_COOKIE_NAME, TOTP_REQUEST_LEN}; @@ -16,10 +17,10 @@ impl<'r> FromRequest<'r> for TotpRequest { if value.len() == TOTP_REQUEST_LEN { Outcome::Success(Self(value.to_string())) } else { - Outcome::Forward(()) + Outcome::Forward(Status::BadRequest) } } - None => Outcome::Forward(()), + None => Outcome::Forward(Status::BadRequest), } } } diff --git a/crates/ezidam/src/routes.rs b/crates/ezidam/src/routes.rs index 03c6050..c5372c8 100644 --- a/crates/ezidam/src/routes.rs +++ b/crates/ezidam/src/routes.rs @@ -27,6 +27,7 @@ pub(self) mod prelude { pub use rocket_db_pools::sqlx::Acquire; pub use rocket_db_pools::Connection; pub use rocket_dyn_templates::Template; + pub use std::ops::DerefMut; pub type Result = std::result::Result; } diff --git a/crates/ezidam/src/routes/admin/apps.rs b/crates/ezidam/src/routes/admin/apps.rs index 89c8fd6..976377c 100644 --- a/crates/ezidam/src/routes/admin/apps.rs +++ b/crates/ezidam/src/routes/admin/apps.rs @@ -10,7 +10,9 @@ pub async fn admin_apps_list( admin: JwtAdmin, flash: Option>, ) -> Result