diff --git a/crates/common/src/auth/mod.rs b/crates/common/src/auth/mod.rs index 35521082..5424f92b 100644 --- a/crates/common/src/auth/mod.rs +++ b/crates/common/src/auth/mod.rs @@ -7,7 +7,8 @@ use std::{net::IpAddr, sync::Arc}; use directory::{ - Directory, Permission, Permissions, Principal, QueryBy, core::secret::verify_secret_hash, + Directory, Permission, Permissions, Principal, QueryBy, Type, + backend::internal::lookup::DirectoryStore, core::secret::verify_secret_hash, }; use jmap_proto::types::collection::Collection; use mail_send::Credentials; @@ -62,6 +63,7 @@ pub struct AuthRequest<'x> { session_id: u64, remote_ip: IpAddr, return_member_of: bool, + allow_api_access: bool, directory: Option<&'x Directory>, } @@ -124,48 +126,62 @@ impl Server { }; // Then check if the credentials match the fallback admin or master user - match ( - &self.core.jmap.fallback_admin, - &self.core.jmap.master_user, - &req.credentials, - ) { - (Some((fallback_admin, fallback_pass)), _, Credentials::Plain { username, secret }) - if username == fallback_admin => - { - if verify_secret_hash(fallback_pass, secret).await? { - trc::event!( - Auth(trc::AuthEvent::Success), - AccountName = username.clone(), - SpanId = req.session_id, - ); - - return Ok(Principal::fallback_admin(fallback_pass)); - } - } - (_, Some((master_user, master_pass)), Credentials::Plain { username, secret }) - if username.ends_with(master_user) => - { - if verify_secret_hash(master_pass, secret).await? { - let username = username.strip_suffix(master_user).unwrap(); - let username = username.strip_suffix('%').unwrap_or(username); - - if let Some(principal) = directory - .query(QueryBy::Name(username), req.return_member_of) - .await? - { + if let Credentials::Plain { username, secret } = &req.credentials { + match (&self.core.jmap.fallback_admin, &self.core.jmap.master_user) { + (Some((fallback_admin, fallback_pass)), _) if username == fallback_admin => { + if verify_secret_hash(fallback_pass, secret).await? { trc::event!( Auth(trc::AuthEvent::Success), - AccountName = username.to_string(), + AccountName = username.clone(), SpanId = req.session_id, - AccountId = principal.id(), - Type = principal.typ().as_str(), ); - return Ok(principal); + return Ok(Principal::fallback_admin(fallback_pass)); + } + } + (_, Some((master_user, master_pass))) if username.ends_with(master_user) => { + if verify_secret_hash(master_pass, secret).await? { + let username = username.strip_suffix(master_user).unwrap(); + let username = username.strip_suffix('%').unwrap_or(username); + + if let Some(principal) = directory + .query(QueryBy::Name(username), req.return_member_of) + .await? + { + trc::event!( + Auth(trc::AuthEvent::Success), + AccountName = username.to_string(), + SpanId = req.session_id, + AccountId = principal.id(), + Type = principal.typ().as_str(), + ); + + return Ok(principal); + } + } + } + _ => { + // Validate API credentials + if req.allow_api_access { + if let Ok(Some(principal)) = self + .store() + .query(QueryBy::Credentials(&req.credentials), req.return_member_of) + .await + { + if principal.typ == Type::ApiKey { + trc::event!( + Auth(trc::AuthEvent::Success), + AccountName = principal.name().to_string(), + AccountId = principal.id(), + SpanId = req.session_id, + ); + + return Ok(principal); + } + } } } } - _ => {} } if let Err(err) = result { @@ -205,6 +221,7 @@ impl<'x> AuthRequest<'x> { remote_ip, return_member_of: true, directory: None, + allow_api_access: false, } } @@ -233,6 +250,11 @@ impl<'x> AuthRequest<'x> { self.directory = Some(directory); self } + + pub fn with_api_access(mut self, allow_api_access: bool) -> Self { + self.allow_api_access = allow_api_access; + self + } } impl CacheItemWeight for AccessToken { diff --git a/crates/http/src/auth/authenticate.rs b/crates/http/src/auth/authenticate.rs index e0a17c1e..5b6aff4a 100644 --- a/crates/http/src/auth/authenticate.rs +++ b/crates/http/src/auth/authenticate.rs @@ -43,6 +43,9 @@ impl Authenticator for Server { .is_http_authenticated_request_allowed(&access_token) .await .map(|in_flight| (in_flight, access_token)); + } else { + // If the revision is not valid, remove the cached credentials + self.inner.cache.http_auth.remove(token); } } @@ -81,11 +84,14 @@ impl Authenticator for Server { // Authenticate let access_token = self - .authenticate(&AuthRequest::from_credentials( - credentials, - session.session_id, - session.remote_ip, - )) + .authenticate( + &AuthRequest::from_credentials( + credentials, + session.session_id, + session.remote_ip, + ) + .with_api_access(allow_api_access), + ) .await?; // Cache credentials @@ -153,8 +159,8 @@ fn decode_plain_auth(token: &str) -> Option> { fn decode_bearer_token(token: &str, allow_api_access: bool) -> Option> { if allow_api_access { - if let Some(token) = token.strip_prefix("api_") { - return decode_plain_auth(token); + if let Some(token) = token.strip_prefix("api_").and_then(decode_plain_auth) { + return Some(token); } }