/ crates / fs-mistrust / src / disable.rs
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  }