distant_protocol/common/
permissions.rs

1use bitflags::bitflags;
2use serde::{Deserialize, Serialize};
3
4use crate::utils;
5
6#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(default, deny_unknown_fields, rename_all = "snake_case")]
8pub struct SetPermissionsOptions {
9    /// Whether or not to exclude symlinks from traversal entirely, meaning that permissions will
10    /// not be set on symlinks (usually resolving the symlink and setting the permission of the
11    /// referenced file or directory) that are explicitly provided or show up during recursion.
12    #[serde(skip_serializing_if = "utils::is_false")]
13    pub exclude_symlinks: bool,
14
15    /// Whether or not to traverse symlinks when recursively setting permissions. Note that this
16    /// does NOT influence setting permissions when encountering a symlink as most platforms will
17    /// resolve the symlink before setting permissions.
18    #[serde(skip_serializing_if = "utils::is_false")]
19    pub follow_symlinks: bool,
20
21    /// Whether or not to set the permissions of the file hierarchies rooted in the paths, instead
22    /// of just the paths themselves.
23    #[serde(skip_serializing_if = "utils::is_false")]
24    pub recursive: bool,
25}
26
27/// Represents permissions to apply to some path on a remote machine
28///
29/// When used to set permissions on a file, directory, or symlink,
30/// only fields that are set (not `None`) will be applied.
31///
32/// On `Unix` platforms, this translates directly into the mode that
33/// you would find with `chmod`. On all other platforms, this uses the
34/// write flags to determine whether or not to set the readonly status.
35#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
36pub struct Permissions {
37    /// Represents whether or not owner can read from the file
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub owner_read: Option<bool>,
40
41    /// Represents whether or not owner can write to the file
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub owner_write: Option<bool>,
44
45    /// Represents whether or not owner can execute the file
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub owner_exec: Option<bool>,
48
49    /// Represents whether or not associated group can read from the file
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub group_read: Option<bool>,
52
53    /// Represents whether or not associated group can write to the file
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub group_write: Option<bool>,
56
57    /// Represents whether or not associated group can execute the file
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub group_exec: Option<bool>,
60
61    /// Represents whether or not other can read from the file
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub other_read: Option<bool>,
64
65    /// Represents whether or not other can write to the file
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub other_write: Option<bool>,
68
69    /// Represents whether or not other can execute the file
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub other_exec: Option<bool>,
72}
73
74impl Permissions {
75    /// Creates a set of [`Permissions`] that indicate readonly status.
76    ///
77    /// ```
78    /// use distant_protocol::Permissions;
79    ///
80    /// let permissions = Permissions::readonly();
81    /// assert_eq!(permissions.is_readonly(), Some(true));
82    /// assert_eq!(permissions.is_writable(), Some(false));
83    /// ```
84    pub fn readonly() -> Self {
85        Self {
86            owner_write: Some(false),
87            group_write: Some(false),
88            other_write: Some(false),
89
90            owner_read: Some(true),
91            group_read: Some(true),
92            other_read: Some(true),
93
94            owner_exec: None,
95            group_exec: None,
96            other_exec: None,
97        }
98    }
99
100    /// Creates a set of [`Permissions`] that indicate globally writable status.
101    ///
102    /// ```
103    /// use distant_protocol::Permissions;
104    ///
105    /// let permissions = Permissions::writable();
106    /// assert_eq!(permissions.is_readonly(), Some(false));
107    /// assert_eq!(permissions.is_writable(), Some(true));
108    /// ```
109    pub fn writable() -> Self {
110        Self {
111            owner_write: Some(true),
112            group_write: Some(true),
113            other_write: Some(true),
114
115            owner_read: Some(true),
116            group_read: Some(true),
117            other_read: Some(true),
118
119            owner_exec: None,
120            group_exec: None,
121            other_exec: None,
122        }
123    }
124
125    /// Returns true if the permission set has a value specified for each permission (no `None`
126    /// settings).
127    ///
128    /// ```
129    /// use distant_protocol::Permissions;
130    ///
131    /// let permissions = Permissions {
132    ///     owner_write: Some(true),
133    ///     group_write: Some(false),
134    ///     other_write: Some(true),
135    ///     owner_read: Some(false),
136    ///     group_read: Some(true),
137    ///     other_read: Some(false),
138    ///     owner_exec: Some(true),
139    ///     group_exec: Some(false),
140    ///     other_exec: Some(true),
141    /// };
142    /// assert!(permissions.is_complete());
143    /// ```
144    pub fn is_complete(&self) -> bool {
145        self.owner_read.is_some()
146            && self.owner_write.is_some()
147            && self.owner_exec.is_some()
148            && self.group_read.is_some()
149            && self.group_write.is_some()
150            && self.group_exec.is_some()
151            && self.other_read.is_some()
152            && self.other_write.is_some()
153            && self.other_exec.is_some()
154    }
155
156    /// Returns `true` if permissions represent readonly, `false` if permissions represent
157    /// writable, and `None` if no permissions have been set to indicate either status.
158    ///
159    /// ```
160    /// use distant_protocol::Permissions;
161    ///
162    /// assert_eq!(
163    ///     Permissions { owner_write: Some(true), ..Default::default() }.is_readonly(),
164    ///     Some(false)
165    /// );
166    ///
167    /// assert_eq!(
168    ///     Permissions { owner_write: Some(false), ..Default::default() }.is_readonly(),
169    ///     Some(true)
170    /// );
171    ///
172    /// assert_eq!(
173    ///     Permissions { ..Default::default() }.is_writable(),
174    ///     None
175    /// );
176    /// ```
177    #[inline]
178    pub fn is_readonly(&self) -> Option<bool> {
179        // Negate the writable status to indicate whether or not readonly
180        self.is_writable().map(|x| !x)
181    }
182
183    /// Returns `true` if permissions represent ability to write, `false` if permissions represent
184    /// inability to write, and `None` if no permissions have been set to indicate either status.
185    ///
186    /// ```
187    /// use distant_protocol::Permissions;
188    ///
189    /// assert_eq!(
190    ///     Permissions { owner_write: Some(true), ..Default::default() }.is_writable(),
191    ///     Some(true)
192    /// );
193    ///
194    /// assert_eq!(
195    ///     Permissions { owner_write: Some(false), ..Default::default() }.is_writable(),
196    ///     Some(false)
197    /// );
198    ///
199    /// assert_eq!(
200    ///     Permissions { ..Default::default() }.is_writable(),
201    ///     None
202    /// );
203    /// ```
204    #[inline]
205    pub fn is_writable(&self) -> Option<bool> {
206        match (self.owner_write, self.group_write, self.other_write) {
207            (None, None, None) => None,
208            (owner, group, other) => {
209                Some(owner.unwrap_or(false) || group.unwrap_or(false) || other.unwrap_or(false))
210            }
211        }
212    }
213
214    /// Applies `other` settings to `self`, overwriting any of the permissions in `self` with `other`.
215    ///
216    /// ```
217    /// use distant_protocol::Permissions;
218    ///
219    /// let mut a = Permissions {
220    ///     owner_read: Some(true),
221    ///     owner_write: Some(false),
222    ///     owner_exec: None,
223    ///     ..Default::default()
224    /// };
225    ///
226    /// let b = Permissions {
227    ///     owner_read: Some(false),
228    ///     owner_write: None,
229    ///     owner_exec: Some(true),
230    ///     ..Default::default()
231    /// };
232    ///
233    /// a.apply_from(&b);
234    ///
235    /// assert_eq!(a, Permissions {
236    ///     owner_read: Some(false),
237    ///     owner_write: Some(false),
238    ///     owner_exec: Some(true),
239    ///     ..Default::default()
240    /// });
241    /// ```
242    #[inline]
243    pub fn apply_from(&mut self, other: &Self) {
244        macro_rules! apply {
245            ($key:ident) => {{
246                if let Some(value) = other.$key {
247                    self.$key = Some(value);
248                }
249            }};
250        }
251
252        apply!(owner_read);
253        apply!(owner_write);
254        apply!(owner_exec);
255        apply!(group_read);
256        apply!(group_write);
257        apply!(group_exec);
258        apply!(other_read);
259        apply!(other_write);
260        apply!(other_exec);
261    }
262
263    /// Applies `self` settings to `other`, overwriting any of the permissions in `other` with
264    /// `self`.
265    ///
266    /// ```
267    /// use distant_protocol::Permissions;
268    ///
269    /// let a = Permissions {
270    ///     owner_read: Some(true),
271    ///     owner_write: Some(false),
272    ///     owner_exec: None,
273    ///     ..Default::default()
274    /// };
275    ///
276    /// let mut b = Permissions {
277    ///     owner_read: Some(false),
278    ///     owner_write: None,
279    ///     owner_exec: Some(true),
280    ///     ..Default::default()
281    /// };
282    ///
283    /// a.apply_to(&mut b);
284    ///
285    /// assert_eq!(b, Permissions {
286    ///     owner_read: Some(true),
287    ///     owner_write: Some(false),
288    ///     owner_exec: Some(true),
289    ///     ..Default::default()
290    /// });
291    /// ```
292    #[inline]
293    pub fn apply_to(&self, other: &mut Self) {
294        Self::apply_from(other, self)
295    }
296
297    /// Converts a Unix `mode` into the permission set.
298    pub fn from_unix_mode(mode: u32) -> Self {
299        let flags = UnixFilePermissionFlags::from_bits_truncate(mode);
300        Self {
301            owner_read: Some(flags.contains(UnixFilePermissionFlags::OWNER_READ)),
302            owner_write: Some(flags.contains(UnixFilePermissionFlags::OWNER_WRITE)),
303            owner_exec: Some(flags.contains(UnixFilePermissionFlags::OWNER_EXEC)),
304            group_read: Some(flags.contains(UnixFilePermissionFlags::GROUP_READ)),
305            group_write: Some(flags.contains(UnixFilePermissionFlags::GROUP_WRITE)),
306            group_exec: Some(flags.contains(UnixFilePermissionFlags::GROUP_EXEC)),
307            other_read: Some(flags.contains(UnixFilePermissionFlags::OTHER_READ)),
308            other_write: Some(flags.contains(UnixFilePermissionFlags::OTHER_WRITE)),
309            other_exec: Some(flags.contains(UnixFilePermissionFlags::OTHER_EXEC)),
310        }
311    }
312
313    /// Converts to a Unix `mode` from a permission set. For any missing setting, a 0 bit is used.
314    ///
315    /// ```
316    /// use distant_protocol::Permissions;
317    ///
318    /// assert_eq!(Permissions {
319    ///     owner_read: Some(true),
320    ///     owner_write: Some(true),
321    ///     owner_exec: Some(true),
322    ///     group_read: Some(true),
323    ///     group_write: Some(true),
324    ///     group_exec: Some(true),
325    ///     other_read: Some(true),
326    ///     other_write: Some(true),
327    ///     other_exec: Some(true),
328    /// }.to_unix_mode(), 0o777);
329    ///
330    /// assert_eq!(Permissions {
331    ///     owner_read: Some(true),
332    ///     owner_write: Some(false),
333    ///     owner_exec: Some(false),
334    ///     group_read: Some(true),
335    ///     group_write: Some(false),
336    ///     group_exec: Some(false),
337    ///     other_read: Some(true),
338    ///     other_write: Some(false),
339    ///     other_exec: Some(false),
340    /// }.to_unix_mode(), 0o444);
341    ///
342    /// assert_eq!(Permissions {
343    ///     owner_exec: Some(true),
344    ///     group_exec: Some(true),
345    ///     other_exec: Some(true),
346    ///     ..Default::default()
347    /// }.to_unix_mode(), 0o111);
348    /// ```
349    pub fn to_unix_mode(&self) -> u32 {
350        let mut flags = UnixFilePermissionFlags::empty();
351
352        macro_rules! is_true {
353            ($opt:expr) => {{
354                $opt.is_some() && $opt.unwrap()
355            }};
356        }
357
358        if is_true!(self.owner_read) {
359            flags.insert(UnixFilePermissionFlags::OWNER_READ);
360        }
361        if is_true!(self.owner_write) {
362            flags.insert(UnixFilePermissionFlags::OWNER_WRITE);
363        }
364        if is_true!(self.owner_exec) {
365            flags.insert(UnixFilePermissionFlags::OWNER_EXEC);
366        }
367
368        if is_true!(self.group_read) {
369            flags.insert(UnixFilePermissionFlags::GROUP_READ);
370        }
371        if is_true!(self.group_write) {
372            flags.insert(UnixFilePermissionFlags::GROUP_WRITE);
373        }
374        if is_true!(self.group_exec) {
375            flags.insert(UnixFilePermissionFlags::GROUP_EXEC);
376        }
377
378        if is_true!(self.other_read) {
379            flags.insert(UnixFilePermissionFlags::OTHER_READ);
380        }
381        if is_true!(self.other_write) {
382            flags.insert(UnixFilePermissionFlags::OTHER_WRITE);
383        }
384        if is_true!(self.other_exec) {
385            flags.insert(UnixFilePermissionFlags::OTHER_EXEC);
386        }
387
388        flags.bits()
389    }
390}
391
392#[cfg(unix)]
393impl From<std::fs::Permissions> for Permissions {
394    /// Converts [`std::fs::Permissions`] into [`Permissions`] using
395    /// [`std::os::unix::fs::PermissionsExt::mode`] to supply the bitset.
396    fn from(permissions: std::fs::Permissions) -> Self {
397        use std::os::unix::prelude::*;
398        Self::from_unix_mode(permissions.mode())
399    }
400}
401
402#[cfg(not(unix))]
403impl From<std::fs::Permissions> for Permissions {
404    /// Converts [`std::fs::Permissions`] into [`Permissions`] using the `readonly` flag.
405    ///
406    /// This will not set executable flags, but will set all read and write flags with write flags
407    /// being `false` if `readonly`, otherwise set to `true`.
408    fn from(permissions: std::fs::Permissions) -> Self {
409        if permissions.readonly() {
410            Self::readonly()
411        } else {
412            Self::writable()
413        }
414    }
415}
416
417#[cfg(unix)]
418impl From<Permissions> for std::fs::Permissions {
419    /// Converts [`Permissions`] into [`std::fs::Permissions`] using
420    /// [`std::os::unix::fs::PermissionsExt::from_mode`].
421    fn from(permissions: Permissions) -> Self {
422        use std::os::unix::prelude::*;
423        std::fs::Permissions::from_mode(permissions.to_unix_mode())
424    }
425}
426
427bitflags! {
428    struct UnixFilePermissionFlags: u32 {
429        const OWNER_READ = 0o400;
430        const OWNER_WRITE = 0o200;
431        const OWNER_EXEC = 0o100;
432        const GROUP_READ = 0o40;
433        const GROUP_WRITE = 0o20;
434        const GROUP_EXEC = 0o10;
435        const OTHER_READ = 0o4;
436        const OTHER_WRITE = 0o2;
437        const OTHER_EXEC = 0o1;
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444
445    #[test]
446    fn should_be_able_to_serialize_minimal_permissions_to_json() {
447        let permissions = Permissions {
448            owner_read: None,
449            owner_write: None,
450            owner_exec: None,
451            group_read: None,
452            group_write: None,
453            group_exec: None,
454            other_read: None,
455            other_write: None,
456            other_exec: None,
457        };
458
459        let value = serde_json::to_value(permissions).unwrap();
460        assert_eq!(value, serde_json::json!({}));
461    }
462
463    #[test]
464    fn should_be_able_to_serialize_full_permissions_to_json() {
465        let permissions = Permissions {
466            owner_read: Some(true),
467            owner_write: Some(false),
468            owner_exec: Some(true),
469            group_read: Some(false),
470            group_write: Some(true),
471            group_exec: Some(false),
472            other_read: Some(true),
473            other_write: Some(false),
474            other_exec: Some(true),
475        };
476
477        let value = serde_json::to_value(permissions).unwrap();
478        assert_eq!(
479            value,
480            serde_json::json!({
481                "owner_read": true,
482                "owner_write": false,
483                "owner_exec": true,
484                "group_read": false,
485                "group_write": true,
486                "group_exec": false,
487                "other_read": true,
488                "other_write": false,
489                "other_exec": true,
490            })
491        );
492    }
493
494    #[test]
495    fn should_be_able_to_deserialize_minimal_permissions_from_json() {
496        let value = serde_json::json!({});
497
498        let permissions: Permissions = serde_json::from_value(value).unwrap();
499        assert_eq!(
500            permissions,
501            Permissions {
502                owner_read: None,
503                owner_write: None,
504                owner_exec: None,
505                group_read: None,
506                group_write: None,
507                group_exec: None,
508                other_read: None,
509                other_write: None,
510                other_exec: None,
511            }
512        );
513    }
514
515    #[test]
516    fn should_be_able_to_deserialize_full_permissions_from_json() {
517        let value = serde_json::json!({
518            "owner_read": true,
519            "owner_write": false,
520            "owner_exec": true,
521            "group_read": false,
522            "group_write": true,
523            "group_exec": false,
524            "other_read": true,
525            "other_write": false,
526            "other_exec": true,
527        });
528
529        let permissions: Permissions = serde_json::from_value(value).unwrap();
530        assert_eq!(
531            permissions,
532            Permissions {
533                owner_read: Some(true),
534                owner_write: Some(false),
535                owner_exec: Some(true),
536                group_read: Some(false),
537                group_write: Some(true),
538                group_exec: Some(false),
539                other_read: Some(true),
540                other_write: Some(false),
541                other_exec: Some(true),
542            }
543        );
544    }
545
546    #[test]
547    fn should_be_able_to_serialize_minimal_permissions_to_msgpack() {
548        let permissions = Permissions {
549            owner_read: None,
550            owner_write: None,
551            owner_exec: None,
552            group_read: None,
553            group_write: None,
554            group_exec: None,
555            other_read: None,
556            other_write: None,
557            other_exec: None,
558        };
559
560        // NOTE: We don't actually check the output here because it's an implementation detail
561        // and could change as we change how serialization is done. This is merely to verify
562        // that we can serialize since there are times when serde fails to serialize at
563        // runtime.
564        let _ = rmp_serde::encode::to_vec_named(&permissions).unwrap();
565    }
566
567    #[test]
568    fn should_be_able_to_serialize_full_permissions_to_msgpack() {
569        let permissions = Permissions {
570            owner_read: Some(true),
571            owner_write: Some(false),
572            owner_exec: Some(true),
573            group_read: Some(true),
574            group_write: Some(false),
575            group_exec: Some(true),
576            other_read: Some(true),
577            other_write: Some(false),
578            other_exec: Some(true),
579        };
580
581        // NOTE: We don't actually check the output here because it's an implementation detail
582        // and could change as we change how serialization is done. This is merely to verify
583        // that we can serialize since there are times when serde fails to serialize at
584        // runtime.
585        let _ = rmp_serde::encode::to_vec_named(&permissions).unwrap();
586    }
587
588    #[test]
589    fn should_be_able_to_deserialize_minimal_permissions_from_msgpack() {
590        // NOTE: It may seem odd that we are serializing just to deserialize, but this is to
591        // verify that we are not corrupting or preventing issues when serializing on a
592        // client/server and then trying to deserialize on the other side. This has happened
593        // enough times with minor changes that we need tests to verify.
594        let buf = rmp_serde::encode::to_vec_named(&Permissions {
595            owner_read: None,
596            owner_write: None,
597            owner_exec: None,
598            group_read: None,
599            group_write: None,
600            group_exec: None,
601            other_read: None,
602            other_write: None,
603            other_exec: None,
604        })
605        .unwrap();
606
607        let permissions: Permissions = rmp_serde::decode::from_slice(&buf).unwrap();
608        assert_eq!(
609            permissions,
610            Permissions {
611                owner_read: None,
612                owner_write: None,
613                owner_exec: None,
614                group_read: None,
615                group_write: None,
616                group_exec: None,
617                other_read: None,
618                other_write: None,
619                other_exec: None,
620            }
621        );
622    }
623
624    #[test]
625    fn should_be_able_to_deserialize_full_permissions_from_msgpack() {
626        // NOTE: It may seem odd that we are serializing just to deserialize, but this is to
627        // verify that we are not corrupting or preventing issues when serializing on a
628        // client/server and then trying to deserialize on the other side. This has happened
629        // enough times with minor changes that we need tests to verify.
630        let buf = rmp_serde::encode::to_vec_named(&Permissions {
631            owner_read: Some(true),
632            owner_write: Some(false),
633            owner_exec: Some(true),
634            group_read: Some(true),
635            group_write: Some(false),
636            group_exec: Some(true),
637            other_read: Some(true),
638            other_write: Some(false),
639            other_exec: Some(true),
640        })
641        .unwrap();
642
643        let permissions: Permissions = rmp_serde::decode::from_slice(&buf).unwrap();
644        assert_eq!(
645            permissions,
646            Permissions {
647                owner_read: Some(true),
648                owner_write: Some(false),
649                owner_exec: Some(true),
650                group_read: Some(true),
651                group_write: Some(false),
652                group_exec: Some(true),
653                other_read: Some(true),
654                other_write: Some(false),
655                other_exec: Some(true),
656            }
657        );
658    }
659}