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.

perm.rs 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. use err::{Error, Result};
  2. use keyring::Path;
  3. use ser::{de_opt_dur, ser_opt_dur};
  4. use std::borrow::Cow;
  5. use std::fmt::{self, Display};
  6. use std::time::{Duration, Instant};
  7. const DEFAULT_TIMEOUT_SECS: u64 = 120;
  8. #[allow(missing_docs)]
  9. pub trait AccessControl {
  10. fn allowed(&self, access: &Access) -> bool;
  11. fn request(&self, access: &Access) -> Result<()> {
  12. if self.allowed(access) {
  13. Ok(())
  14. } else {
  15. Err(Error::permission_denied(access))
  16. }
  17. }
  18. }
  19. /// A set of unlock operations defining the permissions to a keyring state.
  20. #[allow(missing_docs)]
  21. pub struct Permissions(Vec<Unlock>);
  22. impl Permissions {
  23. #[allow(missing_docs)]
  24. pub fn new() -> Self {
  25. Permissions(Vec::new())
  26. }
  27. /// Removes dead unlocks from the permission set.
  28. pub fn purge(&mut self) -> bool {
  29. let t = Instant::now();
  30. self.0.retain(|u| !u.expired(t));
  31. self.0.len() == 0
  32. }
  33. /// Whether any of the permissions may require write access.
  34. pub fn requires_write(&self) -> bool {
  35. self.0.iter().any(|u| u.write)
  36. }
  37. /// Whether any of the permissions may require read access.
  38. pub fn requires_read(&self) -> bool {
  39. self.0.len() > 0
  40. }
  41. /// Revokes a precise access. Only unlocks matching in scope and type (read or write) exactly
  42. /// are revoked.
  43. pub fn lock(&mut self, access: Access) {
  44. self.0.retain(|u| {
  45. &u.scope != &access.scope || &u.write != &access.write
  46. })
  47. }
  48. /// Adds an unlock.
  49. pub fn unlock(&mut self, spec: UnlockSpec) {
  50. self.0.push(spec.into())
  51. }
  52. /// Revokes any permissions that may require write access.
  53. pub fn coerce_read(&mut self) {
  54. self.0.retain(|u| !u.write);
  55. }
  56. }
  57. impl AccessControl for Permissions {
  58. fn allowed(&self, access: &Access) -> bool {
  59. let t = Instant::now();
  60. for perm in &self.0 {
  61. if perm.allowed(access) && !perm.expired(t) {
  62. return true;
  63. }
  64. }
  65. false
  66. }
  67. }
  68. /// Specifies a keyring unlock operation.
  69. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
  70. pub struct UnlockSpec {
  71. /// The scope of the unlock, i.e. which paths are affected.
  72. pub scope: Scope<'static>,
  73. /// Whether the unlock permits writing.
  74. pub write: bool,
  75. /// How long the unlock will remain before it expires, or `None` if it does not expire.
  76. #[serde(serialize_with = "ser_opt_dur", deserialize_with = "de_opt_dur")]
  77. pub expires: Option<Duration>,
  78. }
  79. impl UnlockSpec {
  80. /// Creates a new unlock specification with the default expiry time.
  81. ///
  82. /// The default expiry time is 2 minutes.
  83. ///
  84. /// # Example
  85. ///
  86. /// ```
  87. /// use goblin_core::{UnlockSpec, Scope};
  88. /// use std::time::Duration;
  89. /// assert_eq!(
  90. /// UnlockSpec {
  91. /// scope: Scope::Global,
  92. /// write: false,
  93. /// expires: Some(Duration::from_secs(120)),
  94. /// },
  95. /// UnlockSpec::new(Scope::Global, false)
  96. /// );
  97. /// ```
  98. pub fn new(scope: Scope<'static>, write: bool) -> Self {
  99. UnlockSpec {
  100. scope: scope,
  101. write: write,
  102. expires: Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)),
  103. }
  104. }
  105. /// Creates a new unlock specification for a path unlock with the given path, and the default
  106. /// expiry time.
  107. pub fn path(path: Path, write: bool) -> Self {
  108. UnlockSpec::new(Scope::Path(Cow::Owned(path)), write)
  109. }
  110. }
  111. impl Into<Unlock> for UnlockSpec {
  112. fn into(self) -> Unlock {
  113. Unlock {
  114. scope: self.scope,
  115. write: self.write,
  116. expires: self.expires.map(|d| Instant::now() + d),
  117. }
  118. }
  119. }
  120. /// Describes an access request to the keyring.
  121. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
  122. pub struct Access<'a> {
  123. /// The scope of the acces, i.e. which paths need to be accessible.
  124. pub scope: Scope<'a>,
  125. /// Whether write access is required.
  126. pub write: bool,
  127. }
  128. impl<'a> Access<'a> {
  129. /// Shorthand constructor.
  130. pub fn new(scope: Scope<'a>, write: bool) -> Self {
  131. Access {
  132. scope: scope,
  133. write: write,
  134. }
  135. }
  136. /// Shorthand constructor for a path access.
  137. pub fn path(path: &'a Path, write: bool) -> Self {
  138. Access::new(Scope::Path(Cow::Borrowed(path)), write)
  139. }
  140. }
  141. struct Unlock {
  142. scope: Scope<'static>,
  143. write: bool,
  144. expires: Option<Instant>,
  145. }
  146. impl Unlock {
  147. fn expired(&self, t: Instant) -> bool {
  148. match &self.expires {
  149. &Some(t2) => t > t2,
  150. &None => false,
  151. }
  152. }
  153. }
  154. impl AccessControl for Unlock {
  155. fn allowed(&self, access: &Access) -> bool {
  156. (self.write || !access.write) && self.scope.allowed(access)
  157. }
  158. }
  159. /// Describes a set of paths which may be accessed.
  160. #[derive(Serialize, Deserialize, Debug, Eq)]
  161. #[serde(tag = "type", content = "path", rename_all = "snake_case")]
  162. pub enum Scope<'a> {
  163. /// Describes all paths beginning with a prefix.
  164. Prefix(Cow<'a, Path>),
  165. /// Describes a single path.
  166. Path(Cow<'a, Path>),
  167. /// Captures all paths, and adminstrative operations requiring access to the entire vault.
  168. Global,
  169. }
  170. impl<'a> AccessControl for Scope<'a> {
  171. fn allowed(&self, access: &Access) -> bool {
  172. match (self, &access.scope) {
  173. (&Scope::Global, _) => true,
  174. (&Scope::Prefix(ref pref), &Scope::Prefix(ref pref2)) => pref2.has_prefix(pref),
  175. (&Scope::Prefix(ref pref), &Scope::Path(ref path)) => path.has_prefix(pref),
  176. (&Scope::Path(ref path), &Scope::Path(ref path2)) => path == path2,
  177. _ => false,
  178. }
  179. }
  180. }
  181. impl<'a> Display for Scope<'a> {
  182. fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
  183. match self {
  184. &Scope::Prefix(ref pref) => fmt.write_fmt(format_args!("prefix {:?}", ***pref)),
  185. &Scope::Path(ref path) => fmt.write_fmt(format_args!("path {:?}", ***path)),
  186. &Scope::Global => fmt.write_str("entire vault"),
  187. }
  188. }
  189. }
  190. impl<'a, 'b> PartialEq<Scope<'a>> for Scope<'b> {
  191. fn eq(&self, other: &Scope<'a>) -> bool {
  192. match (self, other) {
  193. (&Scope::Prefix(ref a), &Scope::Prefix(ref b)) => a == b,
  194. (&Scope::Path(ref a), &Scope::Path(ref b)) => a == b,
  195. (&Scope::Global, &Scope::Global) => true,
  196. _ => false,
  197. }
  198. }
  199. }