]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/server/auth.rs
proxmox-rest-server: use new ServerAdapter trait instead of callbacks
[proxmox-backup.git] / src / server / auth.rs
index d6c4a66fb525cfcab4c3cc3ce48d7f6752092dc2..2e6beac764f0b9183373e60fe2e13d67f622bf2f 100644 (file)
@@ -1,9 +1,5 @@
 //! Provides authentication primitives for the HTTP server
 
-use std::sync::Arc;
-use std::future::Future;
-use std::pin::Pin;
-
 use anyhow::format_err;
 
 use proxmox::api::UserInformation;
@@ -11,7 +7,7 @@ use proxmox::api::UserInformation;
 use pbs_tools::ticket::{self, Ticket};
 use pbs_config::{token_shadow, CachedUserInfo};
 use pbs_api_types::{Authid, Userid};
-use proxmox_rest_server::{ApiAuth, AuthError, extract_cookie};
+use proxmox_rest_server::{AuthError, extract_cookie};
 
 use crate::auth_helpers::*;
 
@@ -28,111 +24,93 @@ enum AuthData {
     ApiToken(String),
 }
 
-pub struct UserApiAuth {}
-pub fn default_api_auth() -> Arc<UserApiAuth> {
-    Arc::new(UserApiAuth {})
-}
-
-impl UserApiAuth {
-    fn extract_auth_data(headers: &http::HeaderMap) -> Option<AuthData> {
-        if let Some(raw_cookie) = headers.get(header::COOKIE) {
-            if let Ok(cookie) = raw_cookie.to_str() {
-                if let Some(ticket) = extract_cookie(cookie, "PBSAuthCookie") {
-                    let csrf_token = match headers.get("CSRFPreventionToken").map(|v| v.to_str()) {
-                        Some(Ok(v)) => Some(v.to_owned()),
-                        _ => None,
-                    };
-                    return Some(AuthData::User(UserAuthData { ticket, csrf_token }));
-                }
+fn extract_auth_data(headers: &http::HeaderMap) -> Option<AuthData> {
+    if let Some(raw_cookie) = headers.get(header::COOKIE) {
+        if let Ok(cookie) = raw_cookie.to_str() {
+            if let Some(ticket) = extract_cookie(cookie, "PBSAuthCookie") {
+                let csrf_token = match headers.get("CSRFPreventionToken").map(|v| v.to_str()) {
+                    Some(Ok(v)) => Some(v.to_owned()),
+                    _ => None,
+                };
+                return Some(AuthData::User(UserAuthData { ticket, csrf_token }));
             }
         }
+    }
 
-        match headers.get(header::AUTHORIZATION).map(|v| v.to_str()) {
-            Some(Ok(v)) => {
-                if v.starts_with("PBSAPIToken ") || v.starts_with("PBSAPIToken=") {
-                    Some(AuthData::ApiToken(v["PBSAPIToken ".len()..].to_owned()))
-                } else {
-                    None
-                }
+    match headers.get(header::AUTHORIZATION).map(|v| v.to_str()) {
+        Some(Ok(v)) => {
+            if v.starts_with("PBSAPIToken ") || v.starts_with("PBSAPIToken=") {
+                Some(AuthData::ApiToken(v["PBSAPIToken ".len()..].to_owned()))
+            } else {
+                None
             }
-            _ => None,
         }
+        _ => None,
     }
+}
 
-    async fn check_auth_async(
-        &self,
-        headers: &http::HeaderMap,
-        method: &hyper::Method,
-    ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError> {
+pub async fn check_pbs_auth(
+    headers: &http::HeaderMap,
+    method: &hyper::Method,
+) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError> {
 
-        // fixme: make all IO async
+    // fixme: make all IO async
 
-        let user_info = CachedUserInfo::new()?;
+    let user_info = CachedUserInfo::new()?;
 
-        let auth_data = Self::extract_auth_data(headers);
-        match auth_data {
-            Some(AuthData::User(user_auth_data)) => {
-                let ticket = user_auth_data.ticket.clone();
-                let ticket_lifetime = ticket::TICKET_LIFETIME;
+    let auth_data = extract_auth_data(headers);
+    match auth_data {
+        Some(AuthData::User(user_auth_data)) => {
+            let ticket = user_auth_data.ticket.clone();
+            let ticket_lifetime = ticket::TICKET_LIFETIME;
 
-                let userid: Userid = Ticket::<super::ticket::ApiTicket>::parse(&ticket)?
-                    .verify_with_time_frame(public_auth_key(), "PBS", None, -300..ticket_lifetime)?
-                    .require_full()?;
+            let userid: Userid = Ticket::<super::ticket::ApiTicket>::parse(&ticket)?
+                .verify_with_time_frame(public_auth_key(), "PBS", None, -300..ticket_lifetime)?
+                .require_full()?;
 
-                let auth_id = Authid::from(userid.clone());
-                if !user_info.is_active_auth_id(&auth_id) {
-                    return Err(format_err!("user account disabled or expired.").into());
-                }
+            let auth_id = Authid::from(userid.clone());
+            if !user_info.is_active_auth_id(&auth_id) {
+                return Err(format_err!("user account disabled or expired.").into());
+            }
 
-                if method != hyper::Method::GET {
-                    if let Some(csrf_token) = &user_auth_data.csrf_token {
-                        verify_csrf_prevention_token(
-                            csrf_secret(),
-                            &userid,
-                            &csrf_token,
-                            -300,
-                            ticket_lifetime,
-                        )?;
-                    } else {
-                        return Err(format_err!("missing CSRF prevention token").into());
-                    }
+            if method != hyper::Method::GET {
+                if let Some(csrf_token) = &user_auth_data.csrf_token {
+                    verify_csrf_prevention_token(
+                        csrf_secret(),
+                        &userid,
+                        &csrf_token,
+                        -300,
+                        ticket_lifetime,
+                    )?;
+                } else {
+                    return Err(format_err!("missing CSRF prevention token").into());
                 }
+            }
 
-                Ok((auth_id.to_string(), Box::new(user_info)))
+            Ok((auth_id.to_string(), Box::new(user_info)))
+        }
+        Some(AuthData::ApiToken(api_token)) => {
+            let mut parts = api_token.splitn(2, ':');
+            let tokenid = parts
+                .next()
+                .ok_or_else(|| format_err!("failed to split API token header"))?;
+            let tokenid: Authid = tokenid.parse()?;
+
+            if !user_info.is_active_auth_id(&tokenid) {
+                return Err(format_err!("user account or token disabled or expired.").into());
             }
-            Some(AuthData::ApiToken(api_token)) => {
-                let mut parts = api_token.splitn(2, ':');
-                let tokenid = parts
-                    .next()
-                    .ok_or_else(|| format_err!("failed to split API token header"))?;
-                let tokenid: Authid = tokenid.parse()?;
-
-                if !user_info.is_active_auth_id(&tokenid) {
-                    return Err(format_err!("user account or token disabled or expired.").into());
-                }
 
-                let tokensecret = parts
-                    .next()
-                    .ok_or_else(|| format_err!("failed to split API token header"))?;
-                let tokensecret = percent_decode_str(tokensecret)
-                    .decode_utf8()
-                    .map_err(|_| format_err!("failed to decode API token header"))?;
+            let tokensecret = parts
+                .next()
+                .ok_or_else(|| format_err!("failed to split API token header"))?;
+            let tokensecret = percent_decode_str(tokensecret)
+                .decode_utf8()
+                .map_err(|_| format_err!("failed to decode API token header"))?;
 
-                token_shadow::verify_secret(&tokenid, &tokensecret)?;
+            token_shadow::verify_secret(&tokenid, &tokensecret)?;
 
-                Ok((tokenid.to_string(), Box::new(user_info)))
-            }
-            None => Err(AuthError::NoData),
+            Ok((tokenid.to_string(), Box::new(user_info)))
         }
-    }
-}
-
-impl ApiAuth for UserApiAuth {
-    fn check_auth<'a>(
-        &'a self,
-        headers: &'a http::HeaderMap,
-        method: &'a hyper::Method,
-    ) -> Pin<Box<dyn Future<Output = Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError>> + Send + 'a>> {
-        Box::pin(self.check_auth_async(headers, method))
+        None => Err(AuthError::NoData),
     }
 }