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}