From d69e9c0f2b094d0385bec17d7ae2290b2b8bacf6 Mon Sep 17 00:00:00 2001 From: Allan Zhang <6740989+allan2@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:08:58 -0400 Subject: [PATCH 1/3] refactor(pedm): modify `serve` arguments - `serve` arguments are changed - it now takes the `Config` struct instead of a path - pipe name is now part of `Config` rather than a direct argument - `Config` struct is now exported as `devolutions_pedm::Config` - it can be used by server implementations directly - `serve` is now exported as `devolutions_pedm::serve` --- crates/devolutions-pedm/src/api/mod.rs | 9 ++--- crates/devolutions-pedm/src/api/state.rs | 17 +++------ crates/devolutions-pedm/src/config.rs | 44 ++++++++++++++++-------- crates/devolutions-pedm/src/lib.rs | 9 +++-- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/crates/devolutions-pedm/src/api/mod.rs b/crates/devolutions-pedm/src/api/mod.rs index 93f4e18f5..c146c80cf 100644 --- a/crates/devolutions-pedm/src/api/mod.rs +++ b/crates/devolutions-pedm/src/api/mod.rs @@ -12,7 +12,6 @@ use axum::extract::{ConnectInfo, Request}; use axum::middleware::{self, Next}; use axum::response::Response; use axum::{Json, Router}; -use camino::Utf8PathBuf; use futures_util::future::BoxFuture; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server; @@ -40,6 +39,7 @@ use revoke::post_revoke; use state::{AppState, AppStateError}; use status::get_status; +use crate::config::Config; use crate::db::DbError; use crate::error::{Error, ErrorResponse}; use crate::utils::AccountExt; @@ -94,7 +94,7 @@ async fn named_pipe_middleware( Ok(next.run(request).await) } -fn create_pipe(pipe_name: &'static str) -> anyhow::Result { +fn create_pipe(pipe_name: &str) -> anyhow::Result { let pipe = ServerOptions::new().write_dac(true).create(pipe_name)?; let dacl = Acl::new()?.set_entries(&[ @@ -165,8 +165,8 @@ async fn health_check() -> &'static str { "OK" } -pub async fn serve(pipe_name: &'static str, config_path: Option) -> Result<(), ServeError> { - let state = AppState::load(config_path).await?; +pub async fn serve(config: Config) -> Result<(), ServeError> { + let state = AppState::load(&config).await?; // a plain Axum router let hello_router = Router::new().route("/health", axum::routing::get(health_check)); @@ -178,6 +178,7 @@ pub async fn serve(pipe_name: &'static str, config_path: Option) -> let mut make_service = app.into_make_service_with_connect_info::(); + let pipe_name = &config.pipe_name; let mut server = create_pipe(pipe_name)?; // Log the server startup. diff --git a/crates/devolutions-pedm/src/api/state.rs b/crates/devolutions-pedm/src/api/state.rs index bf1be9d7d..2168c715b 100644 --- a/crates/devolutions-pedm/src/api/state.rs +++ b/crates/devolutions-pedm/src/api/state.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use axum::extract::{FromRef, FromRequestParts}; use axum::http::request::Parts; -use camino::Utf8PathBuf; use hyper::StatusCode; use parking_lot::RwLock; use tracing::info; @@ -41,18 +40,12 @@ pub(crate) struct AppState { } impl AppState { - pub(crate) async fn load(config_path: Option) -> Result { - let config = if let Some(path) = config_path { - Config::load_from_path(&path) - } else { - Config::load_from_default_path() - }?; - + pub(crate) async fn load(config: &Config) -> Result { let db: Arc = match config.db { #[cfg(feature = "libsql")] DbBackend::Libsql => { #[expect(clippy::unwrap_used)] - let c = config.libsql.unwrap(); // already checked by `Config::validate` at the end of the load function + let c = config.libsql.as_ref().unwrap(); // already checked by `Config::validate` at the end of the load function let db_obj = libsql::Builder::new_local(&c.path) .build() .await @@ -64,15 +57,15 @@ impl AppState { #[cfg(feature = "postgres")] DbBackend::Postgres => { #[expect(clippy::unwrap_used)] - let c = config.postgres.unwrap(); // already checked by `Config::validate` at the end of the load function + let c = config.postgres.as_ref().unwrap(); // already checked by `Config::validate` at the end of the load function let mut pg_config = tokio_postgres::Config::new(); pg_config.host(&c.host); pg_config.dbname(&c.dbname); if let Some(n) = c.port { pg_config.port(n); } - pg_config.user(c.user); - if let Some(s) = c.password { + pg_config.user(&c.user); + if let Some(s) = &c.password { pg_config.password(s); } pg_config.ssl_mode(SslMode::Disable); diff --git a/crates/devolutions-pedm/src/config.rs b/crates/devolutions-pedm/src/config.rs index ec3f4dc09..fac1f7d44 100644 --- a/crates/devolutions-pedm/src/config.rs +++ b/crates/devolutions-pedm/src/config.rs @@ -7,32 +7,46 @@ use tracing::info; use crate::data_dir; +/// Specifies the default pipe name. +/// +/// This is a workaround for `serde(default)` not taking a raw string literal or escaped backslashes. +fn default_pipe_name() -> String { + "\\\\.\\pipe\\DevolutionsPEDM".to_owned() +} + /// The application config. #[derive(Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub(crate) struct Config { +pub struct Config { /// The selected database backend. /// /// Only one can be active at a given time. - pub(crate) db: DbBackend, - pub(crate) postgres: Option, - pub(crate) libsql: Option, + pub db: DbBackend, + pub postgres: Option, + pub libsql: Option, + /// Specify the pipe name, if desired. + /// + /// Backslashes must be escaped, like "\\\\.\\pipe\\foo". + /// This field is intentionally omitted from the example configuration. + #[serde(default = "default_pipe_name")] + pub pipe_name: String, } impl Config { /// Creates a new config with the default values for a new setup. - fn standard() -> Self { + pub fn standard() -> Self { Self { db: DbBackend::default(), postgres: None, libsql: Some(LibsqlConfig { path: data_dir().join("pedm.sqlite"), }), + pipe_name: default_pipe_name(), } } /// Loads the config file from the specified path. - pub(crate) fn load_from_path(path: &Utf8Path) -> Result { + pub fn load_from_path(path: &Utf8Path) -> Result { match fs::read_to_string(path) { Ok(s) => { info!("Loading config from {path}"); @@ -51,7 +65,7 @@ impl Config { } /// Loads the config file from the default path. - pub(crate) fn load_from_default_path() -> Result { + pub fn load_from_default_path() -> Result { let path = data_dir().join("config.toml"); Self::load_from_path(&path) } @@ -91,20 +105,20 @@ impl fmt::Display for DbBackend { #[derive(Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub(crate) struct LibsqlConfig { +pub struct LibsqlConfig { /// The path to the SQLite database file. - pub(crate) path: Utf8PathBuf, + pub path: Utf8PathBuf, } // TODO: SSL support #[derive(Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub(crate) struct PgConfig { - pub(crate) host: String, - pub(crate) dbname: String, - pub(crate) port: Option, // 5432 if omitted - pub(crate) user: String, - pub(crate) password: Option, +pub struct PgConfig { + pub host: String, + pub dbname: String, + pub port: Option, // 5432 if omitted + pub user: String, + pub password: Option, } #[derive(Debug)] diff --git a/crates/devolutions-pedm/src/lib.rs b/crates/devolutions-pedm/src/lib.rs index 0f8a2408e..d14ee4a9a 100644 --- a/crates/devolutions-pedm/src/lib.rs +++ b/crates/devolutions-pedm/src/lib.rs @@ -1,11 +1,12 @@ use async_trait::async_trait; use camino::Utf8PathBuf; +pub use config::Config; use devolutions_gateway_task::{ShutdownSignal, Task}; cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { - pub mod api; + mod api; mod db; mod config; mod elevations; @@ -14,8 +15,10 @@ cfg_if::cfg_if! { mod log; mod policy; mod utils; - use tokio::select; + pub use api::serve; + + use tokio::select; use tracing::error; } } @@ -39,7 +42,7 @@ impl Task for PedmTask { cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { select! { - res = api::serve(r"\\.\pipe\DevolutionsPEDM", None) => { + res = serve(Config::standard()) => { if let Err(error) = &res { error!(%error, "Named pipe server got error"); } From c75a0dfc14214a0569171e8c8650eb4cda9708d8 Mon Sep 17 00:00:00 2001 From: Allan Zhang <6740989+allan2@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:33:05 -0400 Subject: [PATCH 2/3] Fix Linux build --- crates/devolutions-pedm/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/devolutions-pedm/src/lib.rs b/crates/devolutions-pedm/src/lib.rs index d14ee4a9a..d3c10f8ef 100644 --- a/crates/devolutions-pedm/src/lib.rs +++ b/crates/devolutions-pedm/src/lib.rs @@ -1,14 +1,16 @@ use async_trait::async_trait; use camino::Utf8PathBuf; -pub use config::Config; use devolutions_gateway_task::{ShutdownSignal, Task}; +mod config; +pub use config::Config; + cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { mod api; mod db; - mod config; + mod elevations; mod elevator; mod error; From f53a93cd9b3d0b3b4d43bf77661078c07fa75f07 Mon Sep 17 00:00:00 2001 From: Allan Zhang <6740989+allan2@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:39:32 -0400 Subject: [PATCH 3/3] Fix api module publicity This commit makes it public again. It is used by the generate-openapi tool in the `devolutions_pedm::api::openapi` call. --- crates/devolutions-pedm/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/devolutions-pedm/src/lib.rs b/crates/devolutions-pedm/src/lib.rs index d3c10f8ef..30b5ab05d 100644 --- a/crates/devolutions-pedm/src/lib.rs +++ b/crates/devolutions-pedm/src/lib.rs @@ -8,7 +8,7 @@ pub use config::Config; cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { - mod api; + pub mod api; mod db; mod elevations;