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