From c80251eccab0f3f14a15d7dd97eb2772f1100473 Mon Sep 17 00:00:00 2001 From: Stefan Reiter Date: Wed, 31 Mar 2021 12:21:51 +0200 Subject: [PATCH] server/rest: add ApiAuth trait to make user auth generic This allows switching the base user identification/authentication method in the rest server. Will initially be used for single file restore VMs, where authentication is based on a ticket file, not the PBS user backend (PAM/local). To avoid putting generic types into the RestServer type for this, we merge the two calls "extract_auth_data" and "check_auth" into a single one, which can use whatever type it wants internally. Signed-off-by: Stefan Reiter --- src/server/config.rs | 13 ++++++++++--- src/server/rest.rs | 34 ++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/server/config.rs b/src/server/config.rs index 9094fa80..ad378b0a 100644 --- a/src/server/config.rs +++ b/src/server/config.rs @@ -13,6 +13,7 @@ use proxmox::api::{ApiMethod, Router, RpcEnvironmentType}; use proxmox::tools::fs::{create_path, CreateOptions}; use crate::tools::{FileLogger, FileLogOptions}; +use super::auth::ApiAuth; pub struct ApiConfig { basedir: PathBuf, @@ -23,11 +24,16 @@ pub struct ApiConfig { template_files: RwLock>, request_log: Option>>, pub enable_tape_ui: bool, + pub api_auth: Arc, } impl ApiConfig { - - pub fn new>(basedir: B, router: &'static Router, env_type: RpcEnvironmentType) -> Result { + pub fn new>( + basedir: B, + router: &'static Router, + env_type: RpcEnvironmentType, + api_auth: Arc, + ) -> Result { Ok(Self { basedir: basedir.into(), router, @@ -37,7 +43,8 @@ impl ApiConfig { template_files: RwLock::new(HashMap::new()), request_log: None, enable_tape_ui: false, - }) + api_auth, + }) } pub fn find_method( diff --git a/src/server/rest.rs b/src/server/rest.rs index 13d379c7..c482bab2 100644 --- a/src/server/rest.rs +++ b/src/server/rest.rs @@ -30,10 +30,10 @@ use proxmox::api::{ }; use proxmox::http_err; +use super::auth::AuthError; use super::environment::RestEnvironment; use super::formatter::*; use super::ApiConfig; -use super::auth::{check_auth, extract_auth_data}; use crate::api2::types::{Authid, Userid}; use crate::auth_helpers::*; @@ -678,6 +678,7 @@ async fn handle_request( rpcenv.set_client_ip(Some(*peer)); let user_info = CachedUserInfo::new()?; + let auth = &api.api_auth; let delay_unauth_time = std::time::Instant::now() + std::time::Duration::from_millis(3000); let access_forbidden_time = std::time::Instant::now() + std::time::Duration::from_millis(500); @@ -703,13 +704,15 @@ async fn handle_request( } if auth_required { - let auth_result = match extract_auth_data(&parts.headers) { - Some(auth_data) => check_auth(&method, &auth_data, &user_info), - None => Err(format_err!("no authentication credentials provided.")), - }; - match auth_result { + match auth.check_auth(&parts.headers, &method, &user_info) { Ok(authid) => rpcenv.set_auth_id(Some(authid.to_string())), - Err(err) => { + Err(auth_err) => { + let err = match auth_err { + AuthError::Generic(err) => err, + AuthError::NoData => { + format_err!("no authentication credentials provided.") + } + }; let peer = peer.ip(); auth_logger()?.log(format!( "authentication failure; rhost={} msg={}", @@ -772,9 +775,9 @@ async fn handle_request( if comp_len == 0 { let language = extract_lang_header(&parts.headers); - if let Some(auth_data) = extract_auth_data(&parts.headers) { - match check_auth(&method, &auth_data, &user_info) { - Ok(auth_id) if !auth_id.is_token() => { + match auth.check_auth(&parts.headers, &method, &user_info) { + Ok(auth_id) => { + if !auth_id.is_token() { let userid = auth_id.user(); let new_csrf_token = assemble_csrf_prevention_token(csrf_secret(), userid); return Ok(get_index( @@ -785,14 +788,13 @@ async fn handle_request( parts, )); } - _ => { - tokio::time::sleep_until(Instant::from_std(delay_unauth_time)).await; - return Ok(get_index(None, None, language, &api, parts)); - } } - } else { - return Ok(get_index(None, None, language, &api, parts)); + Err(AuthError::Generic(_)) => { + tokio::time::sleep_until(Instant::from_std(delay_unauth_time)).await; + } + Err(AuthError::NoData) => {} } + return Ok(get_index(None, None, language, &api, parts)); } else { let filename = api.find_alias(&components); let compression = extract_compression_method(&parts.headers);