diff --git a/src/bin/scheduler/setup/rcc.rs b/src/bin/scheduler/setup/rcc.rs index 7035c320..0a08268d 100644 --- a/src/bin/scheduler/setup/rcc.rs +++ b/src/bin/scheduler/setup/rcc.rs @@ -1,4 +1,9 @@ +use super::api::{self, run_steps}; +#[cfg(windows)] +use super::api::{skip, SetupStep, StepWithPlans}; use super::plans_by_sessions; +#[cfg(windows)] +use super::windows_permissions::run_icacls_command; use crate::internal_config::{sort_plans_by_grouping, GlobalConfig, Plan}; use crate::logging::log_and_return_error; #[cfg(windows)] @@ -18,7 +23,9 @@ pub fn setup( global_config: &GlobalConfig, plans: Vec, ) -> Result<(Vec, Vec), Cancelled> { - let (rcc_plans, mut system_plans): (Vec, Vec) = plans + let (surviving_plans, failures) = run_setup_steps(global_config, plans); + + let (rcc_plans, mut system_plans): (Vec, Vec) = surviving_plans .into_iter() .partition(|plan| matches!(plan.environment, Environment::Rcc(_))); @@ -27,16 +34,7 @@ pub fn setup( return Ok((system_plans, vec![])); } - #[cfg(windows)] - let (surviving_rcc_plans, rcc_file_permissions_failures) = { - use super::windows_permissions::adjust_rcc_file_permissions; - adjust_rcc_file_permissions(&global_config.rcc_config, rcc_plans) - }; - #[cfg(unix)] - let (surviving_rcc_plans, rcc_file_permissions_failures) = (rcc_plans, vec![]); - - let (surviving_rcc_plans, further_rcc_setup_failures) = - rcc_setup(global_config, surviving_rcc_plans)?; + let (surviving_rcc_plans, further_rcc_setup_failures) = rcc_setup(global_config, rcc_plans)?; let mut surviving_plans = vec![]; surviving_plans.extend(surviving_rcc_plans); @@ -44,7 +42,7 @@ pub fn setup( sort_plans_by_grouping(&mut surviving_plans); Ok(( surviving_plans, - rcc_file_permissions_failures + failures .into_iter() .chain(further_rcc_setup_failures) .collect(), @@ -55,6 +53,116 @@ pub fn rcc_setup_working_directory(working_directory: &Utf8Path) -> Utf8PathBuf working_directory.join("rcc_setup") } +fn run_setup_steps(config: &GlobalConfig, mut plans: Vec) -> (Vec, Vec) { + // needed to avoid clippy::type_complexity below + type Gatherer = fn(&GlobalConfig, Vec) -> Vec<(Box, Vec)>; + let gather_requirements: Vec = vec![ + #[cfg(windows)] + gather_rcc_binary_permissions, + #[cfg(windows)] + gather_rcc_profile_permissions, + ]; + + let mut failures = Vec::new(); + for gather in gather_requirements.iter() { + plans = { + let plan_count = plans.len(); + let setup_steps = gather(config, plans); + assert_eq!( + plan_count, + setup_steps.iter().map(|s| s.1.len()).sum::() + ); + let (surviving_plans, current_errors) = run_steps(setup_steps); + failures.extend(current_errors); + surviving_plans + }; + } + sort_plans_by_grouping(&mut plans); + (plans, failures) +} + +#[cfg(windows)] +struct StepFilePermissions { + target: Utf8PathBuf, + session: Session, + icacls_permissions: String, +} + +#[cfg(windows)] +impl SetupStep for StepFilePermissions { + fn setup(&self) -> Result<(), api::Error> { + if let Session::User(user_session) = &self.session { + log::info!( + "Granting user `{user}` {permissions} access to {target}.", + user = &user_session.user_name, + permissions = &self.icacls_permissions, + target = &self.target, + ); + run_icacls_command([ + self.target.as_str(), + "/grant", + &format!("{}:{}", &user_session.user_name, self.icacls_permissions), + ]) + .map_err(|err| { + api::Error::new( + format!( + "Adjusting permissions of {} for user `{}` failed", + self.target, &user_session.user_name + ), + err, + ) + })?; + } + Ok(()) + } +} + +#[cfg(windows)] +fn gather_rcc_binary_permissions(config: &GlobalConfig, plans: Vec) -> Vec { + let (rcc_plans, system_plans): (Vec, Vec) = plans + .into_iter() + .partition(|plan| matches!(plan.environment, Environment::Rcc(_))); + let mut steps: Vec = Vec::new(); + for (session, plans_in_session) in plans_by_sessions(rcc_plans) { + steps.push(( + Box::new(StepFilePermissions { + target: config.rcc_config.binary_path.clone(), + session, + icacls_permissions: "(RX)".to_string(), + }), + plans_in_session, + )); + } + steps.push(skip(system_plans)); + steps +} + +#[cfg(windows)] +fn gather_rcc_profile_permissions(config: &GlobalConfig, plans: Vec) -> Vec { + let (rcc_plans, system_plans): (Vec, Vec) = plans + .into_iter() + .partition(|plan| matches!(plan.environment, Environment::Rcc(_))); + let mut steps: Vec = Vec::new(); + + match &config.rcc_config.profile_config { + RCCProfileConfig::Default => steps.push(skip(rcc_plans)), + RCCProfileConfig::Custom(custom_profile) => { + for (session, plans_in_session) in plans_by_sessions(rcc_plans) { + steps.push(( + Box::new(StepFilePermissions { + target: custom_profile.path.clone(), + session, + icacls_permissions: "(R)".to_string(), + }), + plans_in_session, + )); + } + } + } + steps.push(skip(system_plans)); + steps +} + fn rcc_setup( global_config: &GlobalConfig, rcc_plans: Vec, diff --git a/src/bin/scheduler/setup/windows_permissions.rs b/src/bin/scheduler/setup/windows_permissions.rs index ccf830c6..d40206db 100644 --- a/src/bin/scheduler/setup/windows_permissions.rs +++ b/src/bin/scheduler/setup/windows_permissions.rs @@ -1,60 +1,10 @@ #![cfg(windows)] -use super::plans_by_sessions; use anyhow::{bail, Context}; -use log::{debug, error}; -use std::process::Command; - -use crate::internal_config::Plan; use camino::Utf8Path; -use robotmk::config::{RCCConfig, RCCProfileConfig}; -use robotmk::results::SetupFailure; -use robotmk::session::Session; - -pub fn grant_permissions_to_all_plan_users( - path: &Utf8Path, - plans: Vec, - permissions: &str, - additional_icacls_args: &[&str], - description_for_failure_reporting: &str, -) -> (Vec, Vec) { - let mut surviving_plans = vec![]; - let mut failures = vec![]; - - for (session, plans_in_session) in plans_by_sessions(plans) { - if let Session::User(user_session) = session { - let icacls_permission_arg = format!("{}:{}", user_session.user_name, permissions); - let mut icacls_args = vec![path.as_str(), "/grant", &icacls_permission_arg]; - icacls_args.extend(additional_icacls_args); - - match run_icacls_command(icacls_args).context(format!( - "Adjusting permissions of {path} for user `{}` failed", - user_session.user_name - )) { - Ok(_) => surviving_plans.extend(plans_in_session), - Err(error) => { - for plan in plans_in_session { - error!( - "Plan {}: Failed to adjust permissions of \ - {description_for_failure_reporting} for plan user. Plan won't be scheduled. - Error: {error:?}", - plan.id - ); - failures.push(SetupFailure { - plan_id: plan.id.clone(), - summary: format!( - "Failed to adjust permissions of {description_for_failure_reporting} for plan user" - ), - details: format!("{error:?}"), - }); - } - } - } - } else { - surviving_plans.extend(plans_in_session); - } - } +use std::process::Command; - (surviving_plans, failures) +pub fn run_icacls_command<'a>(arguments: impl IntoIterator) -> anyhow::Result<()> { + run_command("icacls.exe", arguments) } pub fn grant_full_access(user: &str, target_path: &Utf8Path) -> anyhow::Result<()> { @@ -78,48 +28,6 @@ pub fn reset_access(target_path: &Utf8Path) -> anyhow::Result<()> { }) } -pub fn adjust_rcc_file_permissions( - rcc_config: &RCCConfig, - rcc_plans: Vec, -) -> (Vec, Vec) { - debug!( - "Granting all plan users read and execute access to {}", - rcc_config.binary_path - ); - let (mut surviving_rcc_plans, rcc_binary_permissions_failures) = - grant_permissions_to_all_plan_users( - &rcc_config.binary_path, - rcc_plans, - "(RX)", - &[], - "RCC binary", - ); - - let mut rcc_profile_file_permissions_failures = vec![]; - if let RCCProfileConfig::Custom(custom_rcc_profile_config) = &rcc_config.profile_config { - debug!( - "Granting all plan users read access to {}", - custom_rcc_profile_config.path - ); - (surviving_rcc_plans, rcc_profile_file_permissions_failures) = - grant_permissions_to_all_plan_users( - &custom_rcc_profile_config.path, - surviving_rcc_plans, - "(R)", - &[], - "RCC profile file", - ); - } - - ( - surviving_rcc_plans, - rcc_binary_permissions_failures - .into_iter() - .chain(rcc_profile_file_permissions_failures) - .collect(), - ) -} - pub fn transfer_directory_ownership_to_admin_group_recursive( target_path: &Utf8Path, ) -> anyhow::Result<()> { @@ -149,10 +57,6 @@ fn run_command<'a>( Ok(()) } -fn run_icacls_command<'a>(arguments: impl IntoIterator) -> anyhow::Result<()> { - run_command("icacls.exe", arguments) -} - fn run_takeown_command<'a>(arguments: impl IntoIterator) -> anyhow::Result<()> { run_command("takeown.exe", arguments) }