A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
4.9 KiB

8 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io::Error as IoError;
  4. use command_runner::CommandRunner;
  5. use symbols::Symbol;
  6. #[derive(Debug, PartialEq)]
  7. pub enum UserAdderError<E: Error> {
  8. AlreadyExists,
  9. UnknownError,
  10. ImplError(E)
  11. }
  12. impl<E: Error> Error for UserAdderError<E> {
  13. fn description(&self) -> &str {
  14. match self {
  15. &UserAdderError::AlreadyExists => "User already exists",
  16. &UserAdderError::UnknownError => "Unknown error",
  17. &UserAdderError::ImplError(_) => "User adding error"
  18. }
  19. }
  20. fn cause(&self) -> Option<&Error> {
  21. match self {
  22. &UserAdderError::ImplError(ref e) => Some(e),
  23. _ => None
  24. }
  25. }
  26. }
  27. impl<E: Error> fmt::Display for UserAdderError<E> {
  28. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  29. match self.cause() {
  30. Some(e) => write!(f, "{} (cause: {})", self.description(), e),
  31. None => write!(f, "{}", self.description())
  32. }
  33. }
  34. }
  35. pub trait UserAdder {
  36. type SubE: Error;
  37. fn add_user(&self, user_name: &str) -> Result<(), UserAdderError<Self::SubE>>;
  38. }
  39. #[derive(Debug, PartialEq)]
  40. pub enum UserError<E: Error> {
  41. GenericError,
  42. ExecError(E)
  43. }
  44. impl<E: Error> Error for UserError<E> {
  45. fn description(&self) -> &str {
  46. match self {
  47. &UserError::GenericError => "Could not find out if user exists",
  48. &UserError::ExecError(_) => "Error executing symbol"
  49. }
  50. }
  51. fn cause(&self) -> Option<&Error> {
  52. match self {
  53. &UserError::ExecError(ref e) => Some(e),
  54. _ => None
  55. }
  56. }
  57. }
  58. impl<E: Error> fmt::Display for UserError<E> {
  59. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  60. match self.cause() {
  61. Some(e) => write!(f, "{} (cause: {})", self.description(), e),
  62. None => write!(f, "{}", self.description())
  63. }
  64. }
  65. }
  66. pub struct User<'a, E, A> where E: Error + Sized, A: 'a + UserAdder<SubE=E> {
  67. user_name: &'a str,
  68. command_runner: &'a CommandRunner,
  69. user_adder: &'a A
  70. }
  71. impl<'a, E: Error + Sized, A: 'a + UserAdder<SubE=E>> User<'a, E, A> {
  72. pub fn new(user_name: &'a str, command_runner: &'a CommandRunner, user_adder: &'a A) -> User<'a, E, A> {
  73. User {
  74. user_name: user_name,
  75. command_runner: command_runner,
  76. user_adder: user_adder
  77. }
  78. }
  79. }
  80. impl<'a, E: Error, A: UserAdder<SubE=E>> fmt::Display for User<'a, E, A> {
  81. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  82. write!(f, "User {}", self.user_name)
  83. }
  84. }
  85. impl<'a, E: Error, A: UserAdder<SubE=E>> Symbol for User<'a, E, A> {
  86. type Error = UserError<UserAdderError<E>>;
  87. fn target_reached(&self) -> Result<bool, Self::Error> {
  88. let output = self.command_runner.run_with_args("getent", &["passwd", self.user_name]);
  89. match output {
  90. Ok(output) => match output.status.code() {
  91. Some(2) => Ok(false),
  92. Some(0) => Ok(true),
  93. _ => Err(UserError::GenericError)
  94. },
  95. Err(_) => Err(UserError::GenericError)
  96. }
  97. }
  98. fn execute(&self) -> Result<(), Self::Error> {
  99. self.user_adder.add_user(self.user_name).map_err(|e| UserError::ExecError(e))
  100. }
  101. }
  102. pub struct SystemUserAdder<'a> {
  103. command_runner: &'a CommandRunner
  104. }
  105. impl<'a> SystemUserAdder<'a> {
  106. pub fn new(command_runner: &'a CommandRunner) -> SystemUserAdder<'a> {
  107. SystemUserAdder { command_runner: command_runner }
  108. }
  109. }
  110. impl<'a> UserAdder for SystemUserAdder<'a> {
  111. type SubE = IoError;
  112. fn add_user(&self, user_name: &str) -> Result<(), UserAdderError<IoError>> {
  113. let output = self.command_runner.run_with_args("adduser", &["--system", "--disabled-login", "--disabled-password", user_name]);
  114. match output {
  115. Ok(output) => match output.status.code() {
  116. Some(0) => Ok(()),
  117. Some(1) => Err(UserAdderError::AlreadyExists),
  118. Some(_) => Err(UserAdderError::UnknownError),
  119. None => Err(UserAdderError::UnknownError),
  120. },
  121. Err(e) => Err(UserAdderError::ImplError(e))
  122. }
  123. }
  124. }
  125. #[cfg(test)]
  126. mod test {
  127. use std::error::Error;
  128. use std::fmt;
  129. use command_runner::StdCommandRunner;
  130. use symbols::Symbol;
  131. use symbols::user::User;
  132. use symbols::user::UserAdder;
  133. use symbols::user::UserAdderError;
  134. #[derive(Debug, PartialEq)]
  135. struct DummyError;
  136. impl Error for DummyError {
  137. fn description(&self) -> &str {
  138. "DummyError"
  139. }
  140. }
  141. impl fmt::Display for DummyError {
  142. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  143. write!(f, "DummyError")
  144. }
  145. }
  146. struct DummyUserAdder;
  147. impl UserAdder for DummyUserAdder {
  148. type SubE = DummyError;
  149. fn add_user(&self, user_name: &str) -> Result<(), UserAdderError<Self::SubE>> {
  150. Ok(())
  151. }
  152. }
  153. #[test]
  154. fn test_target_reached_nonexisting() {
  155. let symbol = User { user_name: "nonexisting", command_runner: &StdCommandRunner, user_adder: &DummyUserAdder };
  156. assert_eq!(symbol.target_reached(), Ok(false));
  157. }
  158. #[test]
  159. fn test_target_reached_root() {
  160. let symbol = User { user_name: "root", command_runner: &StdCommandRunner, user_adder: &DummyUserAdder };
  161. assert_eq!(symbol.target_reached(), Ok(true));
  162. }
  163. }