use std::{
collections::HashMap,
fs,
io::BufReader,
path::{Path, PathBuf},
};
use api::{Api, ApiEvent};
use bon::Builder;
use de::{ConfigFinderDeserializer, DConfigFinder, DLinkedCommand, DLinkedRole, DLinkedTask};
use log::debug;
use options::BorrowedOptStack;
use rootasrole_core::{
Cred,
database::{
actor::{DGroupType, DGroups},
options::{SAuthentication, SBounding, SPrivileged, STimeout, SUMask, WorkdirBehavior},
score::{CmdMin, CmdOrder, HardenedBool, Score, hardened_bool_from_bool},
},
util::{StorageMethod, WORKDIR_BEHAVIOR, all_paths_from_env, read_with_privileges},
};
use serde::de::DeserializeSeed;
use crate::{
Cli,
error::{SrError, SrResult},
finder::{
de::{cred::CredOwnedData, settings::read_storage},
options::DWorkdirSet,
},
};
pub mod api;
mod cmd;
pub mod de;
mod options;
#[derive(Debug, Default, Clone, Builder)]
pub struct BestExecSettings {
#[builder(default)]
pub score: Score,
#[builder(default)]
pub final_path: PathBuf,
#[builder(default)]
pub cred: CredOwnedData,
pub task: Option<String>,
#[builder(default)]
pub role: String,
#[builder(default)]
pub env: HashMap<String, String>,
#[builder(default)]
pub env_path: Vec<String>,
pub workdir: Option<PathBuf>,
#[builder(default)]
pub bounding: SBounding,
#[builder(default)]
pub timeout: STimeout,
#[builder(default)]
pub auth: SAuthentication,
#[builder(default)]
pub root: SPrivileged,
#[builder(default)]
pub umask: SUMask,
}
pub fn find_best_exec_settings<'de: 'a, 'a, P>(
cli: &'a Cli,
cred: &'a Cred,
rar_cfg_path: P,
rar_cfg_data_path: P,
rar_cfg_type: StorageMethod,
env_vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
env_path: &[&str],
) -> SrResult<BestExecSettings>
where
P: AsRef<Path>,
{
let env_vars: Vec<(String, String)> = env_vars
.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect();
let settings_file = read_storage(rar_cfg_path, rar_cfg_type)?;
let file_path = settings_file
.data
.data
.settings
.unwrap_or_default()
.path
.unwrap_or_else(|| rar_cfg_data_path.as_ref().to_path_buf());
let parse_file = |file_path: &Path| -> SrResult<BestExecSettings> {
let config_finder_deserializer = ConfigFinderDeserializer {
cli,
cred,
env_path,
};
let file = read_with_privileges(file_path)?;
let reader = BufReader::new(file);
match settings_file.data.data.method {
StorageMethod::CBOR => {
let mut io_reader = cbor4ii::core::utils::IoReader::new(reader);
Ok(BestExecSettings::retrieve_settings(
cli,
cred,
&config_finder_deserializer
.deserialize(&mut cbor4ii::serde::Deserializer::new(&mut io_reader))
.map_err(|e| {
debug!("Error deserializing CBOR: {e}");
SrError::ConfigurationError
})?,
env_vars.iter().cloned(),
env_path,
)?)
}
StorageMethod::JSON => {
let io_reader = serde_json::de::IoRead::new(reader);
Ok(BestExecSettings::retrieve_settings(
cli,
cred,
&config_finder_deserializer
.deserialize(&mut serde_json::Deserializer::new(io_reader))
.map_err(|e| {
debug!("Error deserializing JSON: {e}");
SrError::ConfigurationError
})?,
env_vars.iter().cloned(),
env_path,
)?)
}
}
};
if file_path.is_dir() {
let mut entries: Vec<PathBuf> = fs::read_dir(&file_path)
.map_err(|e| {
debug!(
"Failed to read policy directory {}: {e}",
file_path.display()
);
SrError::ConfigurationError
})?
.flatten()
.map(|entry| entry.path())
.filter(|path| path.is_file())
.collect();
entries.sort();
let mut best: Option<BestExecSettings> = None;
let mut parsed_any = false;
for entry in entries {
let result = parse_file(&entry);
match result {
Ok(settings) => {
parsed_any = true;
if best.as_ref().is_none_or(|best_settings| {
settings.score.better_fully(&best_settings.score)
}) {
best = Some(settings);
}
}
Err(SrError::PermissionDenied) => {
parsed_any = true;
}
Err(err) => {
debug!("Skipping policy file {}: {err}", entry.display());
}
}
}
return best.ok_or({
if parsed_any {
SrError::PermissionDenied
} else {
SrError::ConfigurationError
}
});
}
parse_file(&file_path)
}
impl BestExecSettings {
fn retrieve_settings<'a>(
cli: &'a Cli,
cred: &'a Cred,
data: &'a DConfigFinder<'a>,
env_vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
env_path: &[&str],
) -> SrResult<Self> {
let mut result = Self::default();
let mut matching = false;
let mut opt_stack = BorrowedOptStack::new(data.options.clone());
for role in data.roles() {
matching |= result.role_settings(cli, cred, &role, &mut opt_stack, env_path)?;
Api::notify(ApiEvent::BestRoleSettingsFound(
cli,
cred,
&role,
&mut opt_stack,
&env_path,
&mut result,
&mut matching,
))?;
}
if !matching {
return Err(SrError::PermissionDenied);
}
result.env = opt_stack
.calc_temp_env(opt_stack.calc_override_behavior(), cli.opt_filter.as_ref())
.calc_final_env(
env_vars,
opt_stack.calc_path(env_path),
cred,
result.cred.setuid.as_ref(),
format!(
"{}{}",
cli.cmd_path.display(),
if cli.cmd_args.is_empty() {
String::new()
} else {
format!(" {}", cli.cmd_args.join(" "))
}
),
)?;
result.auth = opt_stack.calc_authentication();
result.bounding = opt_stack.calc_bounding();
result.timeout = opt_stack.calc_timeout();
result.root = opt_stack.calc_privileged();
result.umask = opt_stack.calc_umask();
result.workdir = opt_stack.calc_workdir();
Ok(result)
}
pub fn role_settings<'c, 'a>(
&mut self,
cli: &'c Cli,
cred: &'c Cred,
data: &DLinkedRole<'c, 'a>,
opt_stack: &mut BorrowedOptStack<'a>,
env_path: &[&str],
) -> SrResult<bool> {
debug!("role_settings: {:?}", data.role().role);
if !self.actors_settings(data)? {
return Ok(false);
}
let mut res = false;
for task in data.tasks() {
res |= self.task_settings(cli, cred, &task, opt_stack, env_path)?;
}
Ok(res)
}
pub fn actors_settings(&mut self, data: &DLinkedRole<'_, '_>) -> SrResult<bool> {
let mut res = !data.role().user_min.is_no_match();
Api::notify(ApiEvent::ActorMatching(data, self, &mut res))?;
Ok(res)
}
#[allow(clippy::too_many_lines)]
pub fn task_settings<'t, 'a>(
&mut self,
cli: &'t Cli,
cred: &'t Cred,
data: &DLinkedTask<'t, '_, 'a>,
opt_stack: &mut BorrowedOptStack<'a>,
env_path: &[&str],
) -> SrResult<bool> {
debug!("task_settings: {:?}", data.id);
let temp_opt_stack = BorrowedOptStack::from_task(data);
let mut found = false;
let mut f_env_path = None;
if cli
.opt_filter
.as_ref()
.is_some_and(|f| f.env_behavior.is_some() && !temp_opt_stack.calc_override_behavior())
{
debug!(
"task_settings: deny task due to inherited from role or config env_override requirement"
);
return Ok(false);
}
let result = temp_opt_stack.get_workdir_temp();
if cli
.opt_filter
.as_ref()
.and_then(|f| f.workdir.as_ref())
.is_some_and(|w| Self::allowed_workdir(&result, w.as_str()).is_false())
{
debug!(
"task_settings: deny task due to the user wanted to execute to a specific folder, which do not match to settings"
);
return Ok(false);
}
if Self::allowed_workdir(&result, cred.curdir.as_os_str().to_string_lossy().as_ref())
.is_false()
{
debug!(
"task_settings: deny task due to the current directory do not match to allowed settings"
);
return Ok(false);
}
if cli.info && temp_opt_stack.calc_info().is_hide() {
debug!("task_settings: deny task due to inherited from role or config info hide");
return Ok(false);
}
if let Some(commands) = data.commands() {
let t_env_path = opt_stack.calc_path(env_path);
for command in commands.del() {
if self.command_settings(
&t_env_path
.iter()
.map(std::string::String::as_str)
.collect::<Vec<_>>(),
cli,
&command,
)? {
return Ok(false);
}
}
if commands.default_behavior.is_some_and(|b| b.is_all()) {
debug!("default behavior is all");
let t_env_path = opt_stack.calc_path(env_path);
found = true;
debug!("{}", &cli.cmd_path.display());
if let Ok(path) = cli.cmd_path.canonicalize() {
self.final_path = path;
} else {
self.final_path.clone_from(
all_paths_from_env(
&t_env_path
.iter()
.map(std::string::String::as_str)
.collect::<Vec<_>>(),
&cli.cmd_path,
)
.first()
.ok_or(SrError::ExecutionFailed)?,
);
}
self.score.cmd_min = CmdMin::builder()
.matching()
.order(CmdOrder::FullWildcardPath | CmdOrder::RegexArgs)
.build();
} else {
for command in commands.add() {
if self.command_settings(
&t_env_path
.iter()
.map(std::string::String::as_str)
.collect::<Vec<_>>(),
cli,
&command,
)? {
found = true;
break;
}
}
}
f_env_path = Some(t_env_path);
} else if let Some(final_path) = &data.final_path {
debug!("final_path already found: {}", final_path.display());
found = self.update_command_score(final_path.clone(), data.score.cmd_min);
}
let mut score = data.score(self.score.cmd_min, temp_opt_stack.calc_security_min());
Api::notify(ApiEvent::BestTaskSettingsFound(
cli, data, opt_stack, self, &mut score,
))?;
if found && score.better_fully(&self.score) {
debug!("found better task settings");
self.role = data.role().role().role.to_string();
self.task = Some(data.id.to_string());
self.env_path = f_env_path
.unwrap_or_else(|| opt_stack.calc_path(env_path))
.iter()
.map(std::string::ToString::to_string)
.collect();
self.score = score;
self.cred.setuid = data.cred.setuid.clone().and_then(|u| u.fetch_user());
self.cred.setgroups = data.cred.setgroups.clone().map(|g| match g {
DGroups::Single(g) => vec![g.fetch_group()].into_iter().flatten().collect(),
DGroups::Multiple(g) => g.iter().filter_map(DGroupType::fetch_group).collect(),
});
self.cred.caps = data.cred.caps;
opt_stack.set_role(data);
opt_stack.set_task(data);
debug!("resulting settings: {self:?}");
}
Ok(found)
}
pub fn command_settings<'d>(
&mut self,
env_path: &[&str],
cli: &'d Cli,
data: &DLinkedCommand<'d, '_, '_, '_, '_>,
) -> SrResult<bool> {
debug!("env_path: {env_path:?}");
Ok(match &**data {
de::DCommand::Simple(role_cmd) => {
let mut final_path = None;
let cmd_min = cmd::evaluate_command_match(
env_path,
&cli.cmd_path,
&cli.cmd_args,
role_cmd,
self.score.cmd_min,
&mut final_path,
);
final_path.is_some_and(|final_path| self.update_command_score(final_path, cmd_min))
}
de::DCommand::Complex(value) => {
let mut cmd_min = CmdMin::empty();
let mut final_path = None;
Api::notify(ApiEvent::ProcessComplexCommand(
value,
env_path,
&cli.cmd_path,
&cli.cmd_args,
&mut cmd_min,
&mut final_path,
))?;
final_path.is_some_and(|final_path| self.update_command_score(final_path, cmd_min))
}
})
}
fn allowed_workdir(result: &DWorkdirSet<'_>, workdir: &str) -> HardenedBool {
match result.default_behavior {
WorkdirBehavior::Allowlist => {
let is_in_add = result.add.iter().any(|path| path.as_ref() == workdir);
let is_in_sub = result.sub.iter().any(|path| path.as_ref() == workdir);
hardened_bool_from_bool(is_in_add && !is_in_sub)
}
WorkdirBehavior::Blacklist => {
hardened_bool_from_bool(!result.sub.iter().any(|path| path.as_ref() == workdir))
}
WorkdirBehavior::Inherit => {
hardened_bool_from_bool(WORKDIR_BEHAVIOR.is_blacklist())
}
}
}
fn update_command_score(&mut self, final_path: PathBuf, res: CmdMin) -> bool {
debug!(
"update_command_score: current score {:?}, new score {:?}",
self.score.cmd_min, res
);
if res.better(self.score.cmd_min) {
debug!("better");
self.score.cmd_min = res;
self.final_path = final_path;
true
} else {
debug!("not better");
false
}
}
}
#[cfg(test)]
mod tests {
use super::de::{DCommand, DCommandList, DRoleFinder, DTaskFinder, IdTask};
use super::*;
use capctl::CapSet;
use rootasrole_core::database::FilterMatcher;
use rootasrole_core::database::options::{EnvBehavior, Level, SInfo};
use rootasrole_core::database::score::{ActorMatchMin, CmdMin, Score};
use rootasrole_core::database::structs::SetBehavior;
use serde_json::Value;
use std::path::PathBuf;
use crate::Cli;
use crate::finder::de::cred::CredData;
use crate::finder::options::{DEnvOptions, Opt};
use rootasrole_core::Cred;
use test_log::test;
fn dummy_cli() -> Cli {
Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.build()
}
fn dummy_cred() -> Cred {
Cred::builder()
.curdir()
.unwrap()
.groups()
.unwrap()
.user()
.unwrap()
.build()
}
fn dummy_dconfigfinder<'a>() -> DConfigFinder<'a> {
DConfigFinder::builder()
.roles(vec![
DRoleFinder::builder()
.user_min(ActorMatchMin::UserMatch)
.role("test")
.tasks(vec![
DTaskFinder::builder()
.id(IdTask::Number(0))
.cred(CredData::builder().caps(!CapSet::empty()).build())
.commands(
DCommandList::builder(SetBehavior::None)
.add(vec![DCommand::simple("/usr/bin/ls -l")])
.build(),
)
.options(Opt::builder(Level::Task).execinfo(SInfo::Hide).build())
.build(),
DTaskFinder::builder()
.id(IdTask::Number(1))
.cred(CredData::builder().caps(CapSet::empty()).build())
.commands(
DCommandList::builder(SetBehavior::None)
.add(vec![
DCommand::simple("/usr/bin/ls ^.*$"),
DCommand::complex(Value::Object(
std::iter::once((
"key".to_string(),
Value::String("value".into()),
))
.collect::<serde_json::Map<String, Value>>(),
)),
])
.build(),
)
.options(
Opt::builder(Level::Task)
.execinfo(SInfo::Show)
.env(
DEnvOptions::builder(EnvBehavior::Delete)
.override_behavior(true)
.build(),
)
.build(),
)
.build(),
])
.build(),
DRoleFinder::builder()
.user_min(ActorMatchMin::UserMatch)
.role("test2")
.tasks(vec![
DTaskFinder::builder()
.id(IdTask::Number(0))
.cred(CredData::builder().caps(!CapSet::empty()).build())
.commands(
DCommandList::builder(SetBehavior::None)
.add(vec![DCommand::simple("/usr/bin/ls -l")])
.build(),
)
.options(
Opt::builder(Level::Task)
.execinfo(SInfo::Show)
.env(
DEnvOptions::builder(EnvBehavior::Delete)
.override_behavior(true)
.build(),
)
.build(),
)
.build(),
DTaskFinder::builder()
.id(IdTask::Number(1))
.cred(CredData::builder().caps(CapSet::empty()).build())
.commands(
DCommandList::builder(SetBehavior::None)
.add(vec![DCommand::simple("/usr/bin/ls ^.*$")])
.build(),
)
.options(Opt::builder(Level::Task).execinfo(SInfo::Hide).build())
.build(),
])
.build(),
])
.build()
}
#[test]
fn test_retrieve_settings_no_matching_role() {
let cli = Cli::builder().cmd_path("/usr/bin/cat".to_string()).build();
let cred = dummy_cred();
let data = dummy_dconfigfinder();
let env_vars = vec![("KEY", "VALUE")];
let env_path = &["/bin"];
let result = BestExecSettings::retrieve_settings(&cli, &cred, &data, env_vars, env_path);
assert!(result.is_err());
}
#[test]
fn test_retrieve_settings_with_matching_role() {
let cli = dummy_cli();
let cred = dummy_cred();
let data = dummy_dconfigfinder();
let env_vars = vec![("KEY", "VALUE")];
let env_path = &["/UNWANTED"];
let result = BestExecSettings::retrieve_settings(&cli, &cred, &data, env_vars, env_path);
assert!(result.is_ok());
let settings = result.unwrap();
assert_eq!(settings.final_path, PathBuf::from("/usr/bin/ls"));
assert_eq!(settings.role, "test");
assert_eq!(settings.task, Some("0".to_string()));
assert!(settings.cred.setuid.is_none());
assert!(settings.cred.setgroups.is_none());
assert!(settings.cred.caps.is_some());
assert!(!settings.env.is_empty());
assert!(!settings.env_path.is_empty());
assert!(settings.env_path.iter().all(|p| p != "/UNWANTED"));
}
#[test]
fn test_role_settings_calls_actors_and_tasks() {
let mut best = BestExecSettings::default();
let cli = dummy_cli();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let data = binding.roles().next().unwrap();
let mut opt_stack = BorrowedOptStack::new(None);
let env_path = &["/bin"];
let result = best.role_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok());
}
#[test]
fn test_actors_settings_returns_bool() {
let mut best = BestExecSettings::default();
let binding = dummy_dconfigfinder();
let data = binding.roles().next().unwrap();
let result = best.actors_settings(&data);
assert!(result.is_ok());
assert!(result.is_ok());
}
#[test]
fn test_task_settings_sets_fields_on_found() {
let mut best = BestExecSettings::default();
let cli = dummy_cli();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let data = binding.tasks().next().unwrap();
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/bin"];
let result = best.task_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok_and(|r| r));
assert!(*best.final_path == *"/usr/bin/ls");
assert!(best.role == "test");
assert!(best.task == Some("0".to_string()));
assert!(best.cred.caps.is_some());
assert!(best.score.cmd_min == CmdMin::MATCH);
}
#[cfg(feature = "pcre2")]
#[test]
fn test_command_settings_simple_and_complex() {
let mut best = BestExecSettings::default();
let cli = dummy_cli();
let env_path = &["/usr/bin"];
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let binding = binding.tasks().nth(1).unwrap();
let binding = binding.commands().unwrap();
let data = binding.add().next().unwrap();
let result = best.command_settings(env_path, &cli, &data);
assert!(result.as_ref().is_ok_and(|b| *b));
let data = binding.add().nth(1).unwrap();
let result = best.command_settings(env_path, &cli, &data);
assert!(
result.as_ref().is_ok_and(|b| !*b),
"Failed to process complex command : {}",
result.unwrap_err()
);
}
#[test]
fn test_update_command_score_better() {
let mut settings = BestExecSettings {
score: Score {
cmd_min: CmdMin::builder()
.matching()
.order(CmdOrder::RegexArgs)
.build(),
..Default::default()
},
final_path: PathBuf::from("/old/path"),
..Default::default()
};
let new_cmd_min = CmdMin::MATCH;
let new_path = PathBuf::from("/new/path");
let updated = settings.update_command_score(new_path.clone(), new_cmd_min);
assert!(updated);
assert_eq!(settings.score.cmd_min, new_cmd_min);
assert_eq!(settings.final_path, new_path);
}
#[test]
fn test_update_command_score_not_better() {
let mut settings = BestExecSettings {
score: Score {
cmd_min: CmdMin::MATCH,
..Default::default()
},
final_path: PathBuf::from("/old/path"),
..Default::default()
};
let worse_cmd_min = CmdMin::builder()
.matching()
.order(CmdOrder::RegexArgs)
.build();
let new_path = PathBuf::from("/new/path");
let updated = settings.update_command_score(new_path, worse_cmd_min);
assert!(!updated);
assert_eq!(settings.final_path, PathBuf::from("/old/path"));
}
#[test]
fn test_info_denied_due_to_inherited_hide_one() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.info()
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let binding = binding.tasks().next().unwrap();
let data = binding;
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.task_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_info_denied_due_to_inherited_hide_two() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.info()
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let binding = binding.tasks().nth(1).unwrap();
let data = binding;
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.task_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_info_denied_due_to_inherited_hide_three() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.info()
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let data = binding.tasks().nth(1).unwrap();
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.role_settings(&cli, &cred, &binding, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
assert!(*best.final_path == *"/usr/bin/ls");
assert!(best.role == "test", "role was {}", best.role);
assert!(best.task == Some("1".to_string()));
}
#[test]
fn test_info_denied_due_to_inherited_hide_four() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.info()
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().nth(1).unwrap();
let data = binding.tasks().next().unwrap();
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.role_settings(&cli, &cred, &binding, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
assert!(*best.final_path == *"/usr/bin/ls");
assert!(best.role == "test2", "role was {}", best.role);
assert!(best.task == Some("0".to_string()));
}
#[test]
fn test_info_denied_due_to_inherited_env_override_one() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.opt_filter(
FilterMatcher::builder()
.env_behavior(EnvBehavior::Keep)
.build(),
)
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let binding = binding.tasks().next().unwrap();
let data = binding;
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.task_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_info_denied_due_to_inherited_env_override_two() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.opt_filter(
FilterMatcher::builder()
.env_behavior(EnvBehavior::Keep)
.build(),
)
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let binding = binding.tasks().nth(1).unwrap();
let data = binding;
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.task_settings(&cli, &cred, &data, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_info_denied_due_to_inherited_env_override_three() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.opt_filter(
FilterMatcher::builder()
.env_behavior(EnvBehavior::Keep)
.build(),
)
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().next().unwrap();
let data = binding.tasks().nth(1).unwrap();
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.role_settings(&cli, &cred, &binding, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
assert!(*best.final_path == *"/usr/bin/ls");
assert!(best.role == "test", "role was {}", best.role);
assert!(best.task == Some("1".to_string()));
}
#[test]
fn test_info_denied_due_to_inherited_env_override_four() {
let mut best = BestExecSettings::default();
let cli = Cli::builder()
.cmd_path("/usr/bin/ls".to_string())
.cmd_args(vec!["-l".to_string()])
.opt_filter(
FilterMatcher::builder()
.env_behavior(EnvBehavior::Keep)
.build(),
)
.build();
let cred = dummy_cred();
let binding = dummy_dconfigfinder();
let binding = binding.roles().nth(1).unwrap();
let data = binding.tasks().next().unwrap();
let mut opt_stack = BorrowedOptStack::new(data.role().config().options.clone());
let env_path = &["/usr/bin"];
let result = best.role_settings(&cli, &cred, &binding, &mut opt_stack, env_path);
assert!(result.is_ok());
assert!(result.unwrap());
assert!(*best.final_path == *"/usr/bin/ls");
assert!(best.role == "test2", "role was {}", best.role);
assert!(best.task == Some("0".to_string()));
}
}