Prevent users from seeing proxy config in web interface

This commit is contained in:
Brian Picciano 2023-07-18 19:00:37 +02:00
parent 87c779ebb6
commit ccd2285b11
3 changed files with 63 additions and 15 deletions

View File

@ -6,6 +6,8 @@ use crate::util;
use std::sync; use std::sync;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
pub type GetSettingsResult = domain::store::GetResult;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum GetSettingsError { pub enum GetSettingsError {
#[error("not found")] #[error("not found")]
@ -140,7 +142,7 @@ pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyEr
//#[mockall::automock] //#[mockall::automock]
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert { pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
fn get_settings(&self, domain: &domain::Name) -> Result<domain::Settings, GetSettingsError>; fn get_settings(&self, domain: &domain::Name) -> Result<GetSettingsResult, GetSettingsError>;
fn get_file<'store>( fn get_file<'store>(
&'store self, &'store self,
@ -235,7 +237,7 @@ impl ManagerImpl {
} }
impl Manager for ManagerImpl { impl Manager for ManagerImpl {
fn get_settings(&self, domain: &domain::Name) -> Result<domain::Settings, GetSettingsError> { fn get_settings(&self, domain: &domain::Name) -> Result<GetSettingsResult, GetSettingsError> {
Ok(self.domain_store.get(domain)?) Ok(self.domain_store.get(domain)?)
} }
@ -244,13 +246,13 @@ impl Manager for ManagerImpl {
domain: &domain::Name, domain: &domain::Name,
path: &str, path: &str,
) -> Result<util::BoxByteStream, GetFileError> { ) -> Result<util::BoxByteStream, GetFileError> {
let config = self.domain_store.get(domain)?; let settings = self.domain_store.get(domain)?.settings;
if let origin::Descr::Proxy { .. } = config.origin_descr { if let origin::Descr::Proxy { .. } = settings.origin_descr {
return Err(unexpected::Error::from("origin is proxy, can't serve file").into()); return Err(unexpected::Error::from("origin is proxy, can't serve file").into());
} }
let f = self.origin_store.get_file(&config.origin_descr, path)?; let f = self.origin_store.get_file(&settings.origin_descr, path)?;
Ok(f) Ok(f)
} }

View File

@ -3,6 +3,13 @@ use std::{collections, fs, io, path, str::FromStr};
use crate::domain; use crate::domain;
use crate::error::unexpected::{self, Intoable, Mappable}; use crate::error::unexpected::{self, Intoable, Mappable};
#[derive(Debug, PartialEq)]
pub struct GetResult {
pub settings: domain::Settings,
pub builtin: bool,
pub public: bool,
}
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum GetError { pub enum GetError {
#[error("not found")] #[error("not found")]
@ -23,7 +30,7 @@ pub enum SetError {
#[mockall::automock] #[mockall::automock]
pub trait Store { pub trait Store {
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError>; fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError>;
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError>; fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError>;
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>; fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>;
} }
@ -50,7 +57,7 @@ impl FSStore {
} }
impl Store for FSStore { impl Store for FSStore {
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError> { fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError> {
let path = self.settings_file_path(domain); let path = self.settings_file_path(domain);
let settings_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() { let settings_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => GetError::NotFound, io::ErrorKind::NotFound => GetError::NotFound,
@ -61,7 +68,12 @@ impl Store for FSStore {
let settings = serde_json::from_reader(settings_file) let settings = serde_json::from_reader(settings_file)
.map_unexpected_while(|| format!("json parsing {}", path.display()))?; .map_unexpected_while(|| format!("json parsing {}", path.display()))?;
Ok(settings)
Ok(GetResult {
settings,
public: true,
builtin: false,
})
} }
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError> { fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError> {
@ -115,9 +127,13 @@ impl<S: Store> StoreWithBuiltin<S> {
} }
impl<S: Store> Store for StoreWithBuiltin<S> { impl<S: Store> Store for StoreWithBuiltin<S> {
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError> { fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError> {
if let Some(domain) = self.domains.get(domain) { if let Some(domain) = self.domains.get(domain) {
return Ok(domain.settings.clone()); return Ok(GetResult {
settings: domain.settings.clone(),
public: domain.public,
builtin: true,
});
} }
self.inner.get(domain) self.inner.get(domain)
} }
@ -165,26 +181,39 @@ mod tests {
url: "bar".to_string(), url: "bar".to_string(),
branch_name: "baz".to_string(), branch_name: "baz".to_string(),
}, },
serve_protocols: vec![domain::SettingsServeProtocol::Http],
}; };
assert!(matches!( assert!(matches!(
store.get(&domain), store.get(&domain),
Err::<domain::Settings, GetError>(GetError::NotFound) Err::<GetResult, GetError>(GetError::NotFound)
)); ));
store.set(&domain, &settings).expect("set"); store.set(&domain, &settings).expect("set");
assert_eq!(settings, store.get(&domain).expect("settings retrieved")); assert_eq!(
GetResult {
settings,
public: true,
builtin: false,
},
store.get(&domain).expect("settings retrieved")
);
let new_settings = domain::Settings { let new_settings = domain::Settings {
origin_descr: Descr::Git { origin_descr: Descr::Git {
url: "BAR".to_string(), url: "BAR".to_string(),
branch_name: "BAZ".to_string(), branch_name: "BAZ".to_string(),
}, },
serve_protocols: vec![],
}; };
store.set(&domain, &new_settings).expect("set"); store.set(&domain, &new_settings).expect("set");
assert_eq!( assert_eq!(
new_settings, GetResult {
settings: new_settings,
public: true,
builtin: false,
},
store.get(&domain).expect("settings retrieved") store.get(&domain).expect("settings retrieved")
); );
} }

View File

@ -166,7 +166,7 @@ impl<'svc> Service {
req_is_https: bool, req_is_https: bool,
) -> Response<Body> { ) -> Response<Body> {
let settings = match self.domain_manager.get_settings(&domain) { let settings = match self.domain_manager.get_settings(&domain) {
Ok(settings) => settings, Ok(domain::manager::GetSettingsResult { settings, .. }) => settings,
Err(domain::manager::GetSettingsError::NotFound) => { Err(domain::manager::GetSettingsError::NotFound) => {
return self.render_error_page(404, "Domain not found"); return self.render_error_page(404, "Domain not found");
} }
@ -266,7 +266,24 @@ impl<'svc> Service {
} }
let settings = match self.domain_manager.get_settings(&args.domain) { let settings = match self.domain_manager.get_settings(&args.domain) {
Ok(settings) => Some(settings), Ok(domain::manager::GetSettingsResult {
settings,
public,
builtin,
}) => match (public, builtin) {
(false, _) => None,
(true, false) => Some(settings),
(true, true) => {
return self.render_error_page(
403,
format!(
"Settings for domain {} cannot be viewed or modified",
args.domain
)
.as_str(),
)
}
},
Err(domain::manager::GetSettingsError::NotFound) => None, Err(domain::manager::GetSettingsError::NotFound) => None,
Err(domain::manager::GetSettingsError::Unexpected(e)) => { Err(domain::manager::GetSettingsError::Unexpected(e)) => {
return self.internal_error( return self.internal_error(