make get_index and ApiConfig property (callback)

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Dietmar Maurer 2021-09-21 07:58:47 +02:00 committed by Thomas Lamprecht
parent f533d16ef6
commit 7fa9a37c7c
5 changed files with 142 additions and 80 deletions

View File

@ -5,7 +5,9 @@ use std::fs::metadata;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use anyhow::{bail, Error, format_err}; use anyhow::{bail, Error, format_err};
use hyper::Method; use hyper::{Method, Body, Response};
use hyper::http::request::Parts;
use handlebars::Handlebars; use handlebars::Handlebars;
use serde::Serialize; use serde::Serialize;
@ -14,6 +16,8 @@ use proxmox::tools::fs::{create_path, CreateOptions};
use crate::{ApiAuth, FileLogger, FileLogOptions, CommandoSocket}; use crate::{ApiAuth, FileLogger, FileLogOptions, CommandoSocket};
pub type GetIndexFn = fn(Option<String>, Option<String>, &ApiConfig, Parts) -> Response<Body>;
pub struct ApiConfig { pub struct ApiConfig {
basedir: PathBuf, basedir: PathBuf,
router: &'static Router, router: &'static Router,
@ -23,6 +27,7 @@ pub struct ApiConfig {
template_files: RwLock<HashMap<String, (SystemTime, PathBuf)>>, template_files: RwLock<HashMap<String, (SystemTime, PathBuf)>>,
request_log: Option<Arc<Mutex<FileLogger>>>, request_log: Option<Arc<Mutex<FileLogger>>>,
pub api_auth: Arc<dyn ApiAuth + Send + Sync>, pub api_auth: Arc<dyn ApiAuth + Send + Sync>,
get_index_fn: GetIndexFn,
} }
impl ApiConfig { impl ApiConfig {
@ -31,6 +36,7 @@ impl ApiConfig {
router: &'static Router, router: &'static Router,
env_type: RpcEnvironmentType, env_type: RpcEnvironmentType,
api_auth: Arc<dyn ApiAuth + Send + Sync>, api_auth: Arc<dyn ApiAuth + Send + Sync>,
get_index_fn: GetIndexFn,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
basedir: basedir.into(), basedir: basedir.into(),
@ -41,9 +47,19 @@ impl ApiConfig {
template_files: RwLock::new(HashMap::new()), template_files: RwLock::new(HashMap::new()),
request_log: None, request_log: None,
api_auth, api_auth,
get_index_fn,
}) })
} }
pub fn get_index(
&self,
auth_id: Option<String>,
language: Option<String>,
parts: Parts,
) -> Response<Body> {
(self.get_index_fn)(auth_id, language, self, parts)
}
pub fn find_method( pub fn find_method(
&self, &self,
components: &[&str], components: &[&str],

View File

@ -1,5 +1,9 @@
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use futures::*; use futures::*;
use http::request::Parts;
use http::Response;
use hyper::{Body, StatusCode};
use hyper::header;
use proxmox::try_block; use proxmox::try_block;
use proxmox::api::RpcEnvironmentType; use proxmox::api::RpcEnvironmentType;
@ -27,6 +31,22 @@ fn main() {
} }
} }
fn get_index(
_auth_id: Option<String>,
_language: Option<String>,
_api: &ApiConfig,
_parts: Parts,
) -> Response<Body> {
let index = "<center><h1>Proxmox Backup API Server</h1></center>";
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/html")
.body(index.into())
.unwrap()
}
async fn run() -> Result<(), Error> { async fn run() -> Result<(), Error> {
if let Err(err) = syslog::init( if let Err(err) = syslog::init(
syslog::Facility::LOG_DAEMON, syslog::Facility::LOG_DAEMON,
@ -65,6 +85,7 @@ async fn run() -> Result<(), Error> {
&proxmox_backup::api2::ROUTER, &proxmox_backup::api2::ROUTER,
RpcEnvironmentType::PRIVILEGED, RpcEnvironmentType::PRIVILEGED,
default_api_auth(), default_api_auth(),
get_index,
)?; )?;
let backup_user = pbs_config::backup_user()?; let backup_user = pbs_config::backup_user()?;

View File

@ -4,10 +4,15 @@ use std::os::unix::io::AsRawFd;
use anyhow::{bail, format_err, Error}; use anyhow::{bail, format_err, Error};
use futures::*; use futures::*;
use http::request::Parts;
use http::Response;
use hyper::{Body, StatusCode};
use hyper::header;
use url::form_urlencoded;
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype}; use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
use tokio_stream::wrappers::ReceiverStream; use tokio_stream::wrappers::ReceiverStream;
use serde_json::Value; use serde_json::{json, Value};
use proxmox::try_block; use proxmox::try_block;
use proxmox::api::RpcEnvironmentType; use proxmox::api::RpcEnvironmentType;
@ -73,6 +78,79 @@ fn main() -> Result<(), Error> {
pbs_runtime::main(run()) pbs_runtime::main(run())
} }
fn get_index(
auth_id: Option<String>,
language: Option<String>,
api: &ApiConfig,
parts: Parts,
) -> Response<Body> {
let (userid, csrf_token) = match auth_id {
Some(auth_id) => {
let auth_id = auth_id.parse::<Authid>();
match auth_id {
Ok(auth_id) if !auth_id.is_token() => {
let userid = auth_id.user().clone();
let new_csrf_token = assemble_csrf_prevention_token(csrf_secret(), &userid);
(Some(userid), Some(new_csrf_token))
}
_ => (None, None)
}
}
None => (None, None),
};
let nodename = proxmox::tools::nodename();
let user = userid.as_ref().map(|u| u.as_str()).unwrap_or("");
let csrf_token = csrf_token.unwrap_or_else(|| String::from(""));
let mut debug = false;
let mut template_file = "index";
if let Some(query_str) = parts.uri.query() {
for (k, v) in form_urlencoded::parse(query_str.as_bytes()).into_owned() {
if k == "debug" && v != "0" && v != "false" {
debug = true;
} else if k == "console" {
template_file = "console";
}
}
}
let mut lang = String::from("");
if let Some(language) = language {
if Path::new(&format!("/usr/share/pbs-i18n/pbs-lang-{}.js", language)).exists() {
lang = language;
}
}
let data = json!({
"NodeName": nodename,
"UserName": user,
"CSRFPreventionToken": csrf_token,
"language": lang,
"debug": debug,
});
let (ct, index) = match api.render_template(template_file, &data) {
Ok(index) => ("text/html", index),
Err(err) => ("text/plain", format!("Error rendering template: {}", err)),
};
let mut resp = Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, ct)
.body(index.into())
.unwrap();
if let Some(userid) = userid {
resp.extensions_mut().insert(Authid::from((userid, None)));
}
resp
}
async fn run() -> Result<(), Error> { async fn run() -> Result<(), Error> {
if let Err(err) = syslog::init( if let Err(err) = syslog::init(
syslog::Facility::LOG_DAEMON, syslog::Facility::LOG_DAEMON,
@ -93,6 +171,7 @@ async fn run() -> Result<(), Error> {
&proxmox_backup::api2::ROUTER, &proxmox_backup::api2::ROUTER,
RpcEnvironmentType::PUBLIC, RpcEnvironmentType::PUBLIC,
default_api_auth(), default_api_auth(),
get_index,
)?; )?;
config.add_alias("novnc", "/usr/share/novnc-pve"); config.add_alias("novnc", "/usr/share/novnc-pve");

View File

@ -13,6 +13,10 @@ use lazy_static::lazy_static;
use log::{error, info}; use log::{error, info};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream; use tokio_stream::wrappers::ReceiverStream;
use http::request::Parts;
use http::Response;
use hyper::{Body, StatusCode};
use hyper::header;
use proxmox::api::RpcEnvironmentType; use proxmox::api::RpcEnvironmentType;
@ -89,13 +93,29 @@ fn setup_system_env() -> Result<(), Error> {
Ok(()) Ok(())
} }
fn get_index(
_auth_id: Option<String>,
_language: Option<String>,
_api: &ApiConfig,
_parts: Parts,
) -> Response<Body> {
let index = "<center><h1>Proxmox Backup Restore Daemon/h1></center>";
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/html")
.body(index.into())
.unwrap()
}
async fn run() -> Result<(), Error> { async fn run() -> Result<(), Error> {
watchdog_init(); watchdog_init();
let auth_config = Arc::new( let auth_config = Arc::new(
auth::ticket_auth().map_err(|err| format_err!("reading ticket file failed: {}", err))?, auth::ticket_auth().map_err(|err| format_err!("reading ticket file failed: {}", err))?,
); );
let config = ApiConfig::new("", &ROUTER, RpcEnvironmentType::PUBLIC, auth_config)?; let config = ApiConfig::new("", &ROUTER, RpcEnvironmentType::PUBLIC, auth_config, get_index)?;
let rest_server = RestServer::new(config); let rest_server = RestServer::new(config);
let vsock_fd = get_vsock_fd()?; let vsock_fd = get_vsock_fd()?;

View File

@ -15,7 +15,7 @@ use hyper::http::request::Parts;
use hyper::{Body, Request, Response, StatusCode}; use hyper::{Body, Request, Response, StatusCode};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serde_json::{json, Value}; use serde_json::Value;
use tokio::fs::File; use tokio::fs::File;
use tokio::time::Instant; use tokio::time::Instant;
use url::form_urlencoded; use url::form_urlencoded;
@ -42,8 +42,6 @@ use proxmox_rest_server::formatter::*;
use pbs_config::CachedUserInfo; use pbs_config::CachedUserInfo;
use crate::auth_helpers::*;
extern "C" { extern "C" {
fn tzset(); fn tzset();
} }
@ -468,78 +466,6 @@ pub async fn handle_api_request<Env: RpcEnvironment, S: 'static + BuildHasher +
Ok(resp) Ok(resp)
} }
fn get_index(
auth_id: Option<String>,
language: Option<String>,
api: &Arc<ApiConfig>,
parts: Parts,
) -> Response<Body> {
let (userid, csrf_token) = match auth_id {
Some(auth_id) => {
let auth_id = auth_id.parse::<Authid>();
match auth_id {
Ok(auth_id) if !auth_id.is_token() => {
let userid = auth_id.user().clone();
let new_csrf_token = assemble_csrf_prevention_token(csrf_secret(), &userid);
(Some(userid), Some(new_csrf_token))
}
_ => (None, None)
}
}
None => (None, None),
};
let nodename = proxmox::tools::nodename();
let user = userid.as_ref().map(|u| u.as_str()).unwrap_or("");
let csrf_token = csrf_token.unwrap_or_else(|| String::from(""));
let mut debug = false;
let mut template_file = "index";
if let Some(query_str) = parts.uri.query() {
for (k, v) in form_urlencoded::parse(query_str.as_bytes()).into_owned() {
if k == "debug" && v != "0" && v != "false" {
debug = true;
} else if k == "console" {
template_file = "console";
}
}
}
let mut lang = String::from("");
if let Some(language) = language {
if Path::new(&format!("/usr/share/pbs-i18n/pbs-lang-{}.js", language)).exists() {
lang = language;
}
}
let data = json!({
"NodeName": nodename,
"UserName": user,
"CSRFPreventionToken": csrf_token,
"language": lang,
"debug": debug,
});
let (ct, index) = match api.render_template(template_file, &data) {
Ok(index) => ("text/html", index),
Err(err) => ("text/plain", format!("Error rendering template: {}", err)),
};
let mut resp = Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, ct)
.body(index.into())
.unwrap();
if let Some(userid) = userid {
resp.extensions_mut().insert(Authid::from((userid, None)));
}
resp
}
fn extension_to_content_type(filename: &Path) -> (&'static str, bool) { fn extension_to_content_type(filename: &Path) -> (&'static str, bool) {
if let Some(ext) = filename.extension().and_then(|osstr| osstr.to_str()) { if let Some(ext) = filename.extension().and_then(|osstr| osstr.to_str()) {
@ -802,14 +728,14 @@ async fn handle_request(
let language = extract_lang_header(&parts.headers); let language = extract_lang_header(&parts.headers);
match auth.check_auth(&parts.headers, &method) { match auth.check_auth(&parts.headers, &method) {
Ok(auth_id) => { Ok(auth_id) => {
return Ok(get_index(Some(auth_id), language, &api, parts)); return Ok(api.get_index(Some(auth_id), language, parts));
} }
Err(AuthError::Generic(_)) => { Err(AuthError::Generic(_)) => {
tokio::time::sleep_until(Instant::from_std(delay_unauth_time)).await; tokio::time::sleep_until(Instant::from_std(delay_unauth_time)).await;
} }
Err(AuthError::NoData) => {} Err(AuthError::NoData) => {}
} }
return Ok(get_index(None, language, &api, parts)); return Ok(api.get_index(None, language, parts));
} else { } else {
let filename = api.find_alias(&components); let filename = api.find_alias(&components);
let compression = extract_compression_method(&parts.headers); let compression = extract_compression_method(&parts.headers);