Skip to content

Commit

Permalink
change DSL to emit warnings about unused variables by using getters
Browse files Browse the repository at this point in the history
  • Loading branch information
squell committed Dec 16, 2024
1 parent 58a5375 commit d38696e
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 71 deletions.
72 changes: 37 additions & 35 deletions src/defaults/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ pub enum SettingKind {
}

mod settings_dsl;
use settings_dsl::{defaults, ifdef, modifier_of, negate_of, type_of, value_of};
use settings_dsl::{
defaults, ifdef, initializer_of, modifier_of, negator_of, referent_of, result_of, storage_of,
};

defaults! {
always_query_group_plugin = false

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test-msrv

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test-msrv

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test-minimal

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / build-and-test-minimal

multiple methods are never used

Check failure on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

multiple methods are never used

Check warning on line 24 in src/defaults/mod.rs

View workflow job for this annotation

GitHub Actions / miri

multiple methods are never used
Expand Down Expand Up @@ -68,35 +70,35 @@ mod test {
#[test]
fn check() {
let mut def = Settings::default();
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, true };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 3 };
assert_eq! { def.secure_path, None };
assert_eq! { def.env_check, ["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"].iter().map(|s| s.to_string()).collect() };
assert_eq! { def.verifypw, enums::verifypw::all };
assert_eq! { def.always_query_group_plugin(), false };
assert_eq! { def.always_set_home(), false };
assert_eq! { def.env_reset(), true };
assert_eq! { def.mail_badpass(), true };
assert_eq! { def.match_group_by_gid(), false };
assert_eq! { def.use_pty(), true };
assert_eq! { def.visiblepw(), false };
assert_eq! { def.env_editor(), true };
assert_eq! { def.passwd_tries(), 3 };
assert_eq! { def.secure_path(), None };
assert_eq! { def.env_check(), &["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"].iter().map(|s| s.to_string()).collect() };
assert_eq! { def.verifypw(), enums::verifypw::all };

negate("env_check").unwrap()(&mut def);
negate("env_reset").unwrap()(&mut def);
negate("secure_path").unwrap()(&mut def);
negate("verifypw").unwrap()(&mut def);
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, false };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 3 };
assert_eq! { def.secure_path, None };
assert_eq! { def.always_query_group_plugin(), false };
assert_eq! { def.always_set_home(), false };
assert_eq! { def.env_reset(), false };
assert_eq! { def.mail_badpass(), true };
assert_eq! { def.match_group_by_gid(), false };
assert_eq! { def.use_pty(), true };
assert_eq! { def.visiblepw(), false };
assert_eq! { def.env_editor(), true };
assert_eq! { def.passwd_tries(), 3 };
assert_eq! { def.secure_path(), None };
assert! { def.env_check.is_empty() };
assert_eq! { def.verifypw, enums::verifypw::never };
assert_eq! { def.verifypw(), enums::verifypw::never };

let SettingKind::Flag(f) = set("env_reset").unwrap() else {
panic!()
Expand All @@ -114,18 +116,18 @@ mod test {
panic!()
};
f("any").unwrap()(&mut def);
assert_eq! { def.always_query_group_plugin, false };
assert_eq! { def.always_set_home, false };
assert_eq! { def.env_reset, true };
assert_eq! { def.mail_badpass, true };
assert_eq! { def.match_group_by_gid, false };
assert_eq! { def.use_pty, true };
assert_eq! { def.visiblepw, false };
assert_eq! { def.env_editor, true };
assert_eq! { def.passwd_tries, 5 };
assert_eq! { def.secure_path, Some("/bin".into()) };
assert_eq! { def.always_query_group_plugin(), false };
assert_eq! { def.always_set_home(), false };
assert_eq! { def.env_reset(), true };
assert_eq! { def.mail_badpass(), true };
assert_eq! { def.match_group_by_gid(), false };
assert_eq! { def.use_pty(), true };
assert_eq! { def.visiblepw(), false };
assert_eq! { def.env_editor(), true };
assert_eq! { def.passwd_tries(), 5 };
assert_eq! { def.secure_path(), Some("/bin") };
assert! { def.env_check.is_empty() };
assert_eq! { def.verifypw, enums::verifypw::any };
assert_eq! { def.verifypw(), enums::verifypw::any };

assert!(set("notanoption").is_none());
assert!(f("notanoption").is_none());
Expand Down
60 changes: 49 additions & 11 deletions src/defaults/settings_dsl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
macro_rules! type_of {
macro_rules! storage_of {
($id:ident, true) => { bool };
($id:ident, false) => { bool };
($id:ident, [ $($value: expr),* ]) => { std::collections::HashSet<String> };
Expand All @@ -7,7 +7,16 @@ macro_rules! type_of {
($id:ident, $_: expr) => { Option<Box<str>> };
}

macro_rules! value_of {
macro_rules! referent_of {
($id:ident, true) => { bool };
($id:ident, false) => { bool };
($id:ident, [ $($value: expr),* ]) => { &std::collections::HashSet<String> };
($id:ident, $(=int $check: expr;)+ $_: expr) => { i64 };
($id:ident, $(=enum $k: ident;)+ $_: ident) => { $crate::defaults::enums::$id };
($id:ident, $_: expr) => { Option<&str> };
}

macro_rules! initializer_of {
($id:ident, true) => { true };
($id:ident, false) => { false };
($id:ident, [ $($value: expr),* ]) => { [$($value),*].into_iter().map(|s: &str| s.to_string()).collect::<std::collections::HashSet<_>>() };
Expand All @@ -18,7 +27,7 @@ macro_rules! value_of {
($id:ident, $($_: tt)*) => { ifdef![] };
}

macro_rules! negate_of {
macro_rules! negator_of {
($id:ident, true) => {
false
};
Expand All @@ -45,6 +54,24 @@ macro_rules! negate_of {
};
}

macro_rules! result_of {
($id:expr, true) => {
$id
};
($id:expr, false) => {
$id
};
($id:expr, [ $($value: expr),* ]) => {
&$id
};
($id:expr, $(=value $k: expr;)+ $_: expr) => {
$id
};
($id:expr, $_: expr) => {
$id.as_deref()
};
}

macro_rules! modifier_of {
($id:ident, true) => {
$crate::defaults::SettingKind::Flag(Box::new(move |obj: &mut Settings| obj.$id = true))
Expand Down Expand Up @@ -130,22 +157,31 @@ macro_rules! defaults {
#[allow(non_camel_case_types)]
mod enums {
$($(
#[derive(Clone,Debug)]
#[derive(Clone,Copy,Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub enum $name { $($key),* }
)?)*
}

#[derive(Clone)]
pub struct Settings {
$(pub $name: type_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),*
$($name: storage_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),*
}

// we add setters to make sure the settings-object is read only, and to generate 'unused variable' warnings
impl Settings {
$(
pub fn $name(&self) -> referent_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value) {
result_of!(self.$name, $(=value $fn;)?$(=value $first;)?$($(=value $key;)*)? $value)
}
)*
}

impl Default for Settings {
#[allow(unused_parens)]
fn default() -> Self {
Self {
$($name: value_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),*
$($name: initializer_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)),*
}
}
}
Expand All @@ -157,8 +193,8 @@ macro_rules! defaults {
$(
stringify!($name) if ifdef!($($negate)?; true; false) || matches!(stringify!($value), "true" | "false") || stringify!($value).starts_with('[') => {
let _value = ifdef!($($negate)?;
value_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $($negate)?);
negate_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)
initializer_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $($negate)?);
negator_of!($name, $(=int $fn;)?$(=int $first;)?$($(=enum $key;)*)? $value)
);
Some(Box::new(move |obj: &mut Settings| obj.$name = _value))
},
Expand All @@ -180,7 +216,9 @@ macro_rules! defaults {

pub(super) use defaults;
pub(super) use ifdef;
pub(super) use initializer_of;
pub(super) use modifier_of;
pub(super) use negate_of;
pub(super) use type_of;
pub(super) use value_of;
pub(super) use negator_of;
pub(super) use referent_of;
pub(super) use result_of;
pub(super) use storage_of;
2 changes: 1 addition & 1 deletion src/sudoers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl Sudoers {
}

pub(crate) fn solve_editor_path(&self) -> Option<PathBuf> {
if self.settings.env_editor {
if self.settings.env_editor() {
for key in ["SUDO_EDITOR", "VISUAL", "EDITOR"] {
if let Some(var) = std::env::var_os(key) {
let path = Path::new(&var);
Expand Down
18 changes: 9 additions & 9 deletions src/sudoers/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ pub enum DirChange<'a> {
impl Policy for Judgement {
fn authorization(&self) -> Authorization {
if let Some(tag) = &self.flags {
let allowed_attempts = self.settings.passwd_tries.try_into().unwrap();
let valid_seconds = self.settings.timestamp_timeout;
let allowed_attempts = self.settings.passwd_tries().try_into().unwrap();
let valid_seconds = self.settings.timestamp_timeout();
Authorization::Allowed(AuthorizationAllowed {
must_authenticate: tag.needs_passwd(),
allowed_attempts,
Expand All @@ -66,11 +66,11 @@ impl Policy for Judgement {
}

fn env_keep(&self) -> &HashSet<String> {
&self.settings.env_keep
self.settings.env_keep()
}

fn env_check(&self) -> &HashSet<String> {
&self.settings.env_check
self.settings.env_check()
}

fn chdir(&self) -> DirChange {
Expand All @@ -82,11 +82,11 @@ impl Policy for Judgement {
}

fn secure_path(&self) -> Option<String> {
self.settings.secure_path.as_ref().map(|s| s.to_string())
self.settings.secure_path().as_ref().map(|s| s.to_string())
}

fn use_pty(&self) -> bool {
self.settings.use_pty
self.settings.use_pty()
}
}

Expand All @@ -97,14 +97,14 @@ pub trait PreJudgementPolicy {

impl PreJudgementPolicy for Sudoers {
fn secure_path(&self) -> Option<String> {
self.settings.secure_path.as_ref().map(|s| s.to_string())
self.settings.secure_path().as_ref().map(|s| s.to_string())
}

fn validate_authorization(&self) -> Authorization {
Authorization::Allowed(AuthorizationAllowed {
must_authenticate: true,
allowed_attempts: self.settings.passwd_tries.try_into().unwrap(),
prior_validity: Duration::seconds(self.settings.timestamp_timeout),
allowed_attempts: self.settings.passwd_tries().try_into().unwrap(),
prior_validity: Duration::seconds(self.settings.timestamp_timeout()),
})
}
}
Expand Down
33 changes: 18 additions & 15 deletions src/sudoers/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,11 @@ fn default_bool_test() {
"Defaults !env_editor"
],
);
assert!(settings.env_reset);
assert!(!settings.use_pty);
assert!(settings.env_keep.is_empty());
assert_eq!(settings.secure_path, None);
assert!(!settings.env_editor);
assert!(settings.env_reset());
assert!(!settings.use_pty());
assert!(settings.env_keep().is_empty());
assert_eq!(settings.secure_path(), None);
assert!(!settings.env_editor());
}

#[test]
Expand All @@ -279,18 +279,18 @@ fn default_set_test() {
],
);
assert_eq!(
settings.env_keep,
["FOO", "BAR"].into_iter().map(|x| x.to_string()).collect()
settings.env_keep(),
&["FOO", "BAR"].into_iter().map(|x| x.to_string()).collect()
);
assert_eq!(
settings.env_check,
["FOO", "XYZZY"]
settings.env_check(),
&["FOO", "XYZZY"]
.into_iter()
.map(|x| x.to_string())
.collect()
);
assert_eq!(settings.secure_path.as_deref(), Some("/etc"));
assert_eq!(settings.passwd_tries, 5);
assert_eq!(settings.secure_path(), Some("/etc"));
assert_eq!(settings.passwd_tries(), 5);

assert!(parse_string::<Sudo>("Defaults verifypw = \"sometimes\"").is_err());
assert!(parse_string::<Sudo>("Defaults verifypw = sometimes").is_err());
Expand All @@ -305,10 +305,13 @@ fn default_multi_test() {
"Defaults env_reset, !use_pty, secure_path=/etc, env_keep = \"FOO BAR\", env_keep -= BAR"
],
);
assert!(settings.env_reset);
assert!(!settings.use_pty);
assert_eq!(settings.secure_path.as_deref(), Some("/etc"));
assert_eq!(settings.env_keep, ["FOO".to_string()].into_iter().collect());
assert!(settings.env_reset());
assert!(!settings.use_pty());
assert_eq!(settings.secure_path(), Some("/etc"));
assert_eq!(
settings.env_keep(),
&["FOO".to_string()].into_iter().collect()
);
}

#[test]
Expand Down

0 comments on commit d38696e

Please sign in to comment.