Skip to main content

routing/
rights.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::RightsRoutingError;
6use fidl_fuchsia_io as fio;
7use moniker::ExtendedMoniker;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
10use std::fmt;
11
12/// Opaque rights type to define new traits like PartialOrd on.
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14pub struct Rights(fio::Operations);
15
16impl Rights {
17    /// Ensures the next walk state of rights satisfies a monotonic increasing sequence. Used to
18    /// verify the expectation that no right requested from a use, offer, or expose is missing as
19    /// capability routing walks from the capability's consumer to its provider.
20    pub fn validate_next(
21        &self,
22        next_rights: &Self,
23        moniker: ExtendedMoniker,
24    ) -> Result<(), RightsRoutingError> {
25        if next_rights.0.contains(self.0) {
26            Ok(())
27        } else {
28            Err(RightsRoutingError::Invalid { moniker, requested: *self, provided: *next_rights })
29        }
30    }
31}
32
33/// Allows creating rights from fio::Operations.
34impl From<fio::Operations> for Rights {
35    fn from(rights: fio::Operations) -> Self {
36        Rights(rights)
37    }
38}
39
40impl From<Rights> for fio::Flags {
41    fn from(rights: Rights) -> Self {
42        fio::Flags::from_bits_retain(rights.0.bits())
43    }
44}
45
46impl Into<u64> for Rights {
47    fn into(self) -> u64 {
48        self.0.bits()
49    }
50}
51
52impl Into<fio::Operations> for Rights {
53    fn into(self) -> fio::Operations {
54        let Self(ops) = self;
55        ops
56    }
57}
58
59impl From<fio::Flags> for Rights {
60    fn from(flags: fio::Flags) -> Self {
61        Self(
62            fio::Operations::from_bits(flags.bits())
63                .expect("operations is bit-compatible with flags"),
64        )
65    }
66}
67
68impl fmt::Display for Rights {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        let Self(rights) = &self;
71        match *rights {
72            fio::R_STAR_DIR => write!(f, "r*"),
73            fio::W_STAR_DIR => write!(f, "w*"),
74            fio::X_STAR_DIR => write!(f, "x*"),
75            fio::RW_STAR_DIR => write!(f, "rw*"),
76            fio::RX_STAR_DIR => write!(f, "rx*"),
77            ops => write!(f, "{:?}", ops),
78        }
79    }
80}
81
82#[cfg(feature = "serde")]
83impl Serialize for Rights {
84    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85    where
86        S: Serializer,
87    {
88        let Self(rights) = self;
89        rights.bits().serialize(serializer)
90    }
91}
92
93#[cfg(feature = "serde")]
94impl<'de> Deserialize<'de> for Rights {
95    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96    where
97        D: Deserializer<'de>,
98    {
99        let bits: u64 = Deserialize::deserialize(deserializer)?;
100        let rights = fio::Operations::from_bits(bits)
101            .ok_or_else(|| serde::de::Error::custom("invalid value for fuchsia.io/Operations"))?;
102        Ok(Self(rights))
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use assert_matches::assert_matches;
110
111    #[test]
112    fn validate_next() {
113        assert_matches!(
114            Rights(fio::Operations::empty())
115                .validate_next(&Rights(fio::R_STAR_DIR,), ExtendedMoniker::ComponentManager),
116            Ok(())
117        );
118        assert_matches!(
119            Rights(fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES,)
120                .validate_next(&Rights(fio::R_STAR_DIR), ExtendedMoniker::ComponentManager),
121            Ok(())
122        );
123        let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
124        assert_eq!(
125            Rights(fio::R_STAR_DIR)
126                .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
127            Err(RightsRoutingError::Invalid {
128                moniker: ExtendedMoniker::ComponentManager,
129                requested: Rights::from(fio::R_STAR_DIR),
130                provided: Rights::from(provided),
131            })
132        );
133        let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
134        assert_eq!(
135            Rights(fio::Operations::WRITE_BYTES)
136                .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
137            Err(RightsRoutingError::Invalid {
138                moniker: ExtendedMoniker::ComponentManager,
139                requested: Rights::from(fio::Operations::WRITE_BYTES),
140                provided: Rights::from(provided),
141            })
142        );
143    }
144}