disable.rs
1 //! Functionality for disabling `fs-mistrust` checks based on configuration or 2 //! environment variables. 3 4 use std::env::{self, VarError}; 5 6 /// Convenience type to indicate whether permission checks are disabled. 7 /// 8 /// Used to avoid accidents with boolean meanings. 9 #[derive(Copy, Clone, Eq, PartialEq, Debug)] 10 pub(crate) enum Status { 11 /// We should indeed run permission checks, and treat some users as untrusted. 12 CheckPermissions, 13 /// We should treat every user as trusted, and therefore disable (most) 14 /// permissions checks. 15 DisableChecks, 16 } 17 18 impl Status { 19 /// Return true if this `Status` tells us to disable checks. 20 pub(crate) fn disabled(self) -> bool { 21 self == Status::DisableChecks 22 } 23 } 24 25 /// An environment variable which, if set, will cause a us to trust all users 26 /// (and therefore, in effect, to disable all permissions checks.) 27 pub const GLOBAL_DISABLE_VAR: &str = "FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS"; 28 29 /// Value to configure when permission checks should be disabled. This type is 30 /// set in the builder, and converted to a bool in the `Mistrust`. 31 #[derive(Clone, Default, Debug, Eq, PartialEq)] 32 pub(crate) enum Disable { 33 /// Check a caller-provided environment variable, and honor it if it is set. 34 /// If it is not set, fall back to checking 35 /// `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS`. 36 OnUserEnvVar(String), 37 /// Disable permissions checks if the value of 38 /// `$FS_MISTRUST_DISABLE_PERMISSIONS_CHECKS` is something other than "false", 39 /// "0", "no", etc.. 40 /// 41 /// This is the default. 42 #[default] 43 OnGlobalEnvVar, 44 /// Perform permissions checks regardless of any values in the environment. 45 Never, 46 } 47 48 /// Convert the result of `std::env::var` to a boolean, if the variable is set. 49 /// 50 /// Names that seem to say "don't disable" are treated as `Some(false)`. Any 51 /// other value is treated as `Some(true)`. (That is, we err on the side of 52 /// assuming that if you set a disable variable, you meant to disable.) 53 /// 54 /// Absent environment vars, or those set to the empty string, are treated as 55 /// None. 56 #[allow(clippy::match_like_matches_macro)] 57 fn from_env_var_value(input: std::result::Result<String, VarError>) -> Option<Status> { 58 // WARNING: This behaviour of the environment variable parsing/evaluation is considered 59 // stable and should not be modified unless necessary. 60 // This behaviour is part of the public interface of applications which use fs-mistrust, 61 // so changing the behaviour of this function may result in a breaking change for applications. 62 63 let mut s = match input { 64 Ok(s) => s, 65 Err(VarError::NotPresent) => return None, 66 Err(VarError::NotUnicode(_)) => return Some(Status::DisableChecks), 67 }; 68 69 s.make_ascii_lowercase(); 70 let s = s.trim(); 71 72 match s { 73 "" => None, 74 "0" | "no" | "never" | "false" | "n" => Some(Status::CheckPermissions), 75 _ => Some(Status::DisableChecks), 76 } 77 } 78 79 /// As `from_env_value`, but takes the name of the variable. 80 fn from_env_var(varname: &str) -> Option<Status> { 81 from_env_var_value(env::var(varname)) 82 } 83 84 impl Disable { 85 /// Return true if, based on this [`Disable`] setting, and on the 86 /// environment, we should disable permissions checking. 87 pub(crate) fn should_disable_checks(&self) -> Status { 88 match self { 89 Disable::OnUserEnvVar(varname) => from_env_var(varname) 90 .or_else(|| from_env_var(GLOBAL_DISABLE_VAR)) 91 .unwrap_or(Status::CheckPermissions), 92 Disable::OnGlobalEnvVar => { 93 from_env_var(GLOBAL_DISABLE_VAR).unwrap_or(Status::CheckPermissions) 94 } 95 Disable::Never => Status::CheckPermissions, 96 } 97 } 98 } 99 100 #[cfg(test)] 101 mod test { 102 // @@ begin test lint list maintained by maint/add_warning @@ 103 #![allow(clippy::bool_assert_comparison)] 104 #![allow(clippy::clone_on_copy)] 105 #![allow(clippy::dbg_macro)] 106 #![allow(clippy::mixed_attributes_style)] 107 #![allow(clippy::print_stderr)] 108 #![allow(clippy::print_stdout)] 109 #![allow(clippy::single_char_pattern)] 110 #![allow(clippy::unwrap_used)] 111 #![allow(clippy::unchecked_duration_subtraction)] 112 #![allow(clippy::useless_vec)] 113 #![allow(clippy::needless_pass_by_value)] 114 //! <!-- @@ end test lint list maintained by maint/add_warning @@ --> 115 use super::*; 116 #[test] 117 fn from_val() { 118 for word in ["yes", "1", "true", "certainly", "whatever"] { 119 assert_eq!( 120 from_env_var_value(Ok(word.into())), 121 Some(Status::DisableChecks) 122 ); 123 } 124 125 for word in ["no", "0", "false", "NO", "Never", "n"] { 126 assert_eq!( 127 from_env_var_value(Ok(word.into())), 128 Some(Status::CheckPermissions) 129 ); 130 } 131 132 assert_eq!(from_env_var_value(Ok("".into())), None); 133 assert_eq!(from_env_var_value(Ok(" ".into())), None); 134 135 assert_eq!(from_env_var_value(Err(VarError::NotPresent)), None); 136 assert_eq!( 137 from_env_var_value(Err(VarError::NotUnicode("".into()))), 138 Some(Status::DisableChecks) 139 ); 140 141 // see https://gitlab.torproject.org/tpo/core/arti/-/issues/1782 142 assert_eq!( 143 from_env_var_value(Ok(" false ".to_string())), 144 Some(Status::CheckPermissions), 145 ); 146 } 147 }