1use crate::error::{RightsRoutingError, RoutingError};
6use fidl_fuchsia_component_runtime::RouteRequest;
7use fidl_fuchsia_io as fio;
8use moniker::ExtendedMoniker;
9use router_error::RouterError;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
12use std::fmt;
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy)]
16pub struct Rights(fio::Operations);
17
18impl Rights {
19 pub fn validate_next(
23 &self,
24 next_rights: &Self,
25 moniker: ExtendedMoniker,
26 ) -> Result<(), RightsRoutingError> {
27 if next_rights.0.contains(self.0) {
28 Ok(())
29 } else {
30 Err(RightsRoutingError::Invalid { moniker, requested: *self, provided: *next_rights })
31 }
32 }
33}
34
35impl From<fio::Operations> for Rights {
37 fn from(rights: fio::Operations) -> Self {
38 Rights(rights)
39 }
40}
41
42impl From<Rights> for fio::Flags {
43 fn from(rights: Rights) -> Self {
44 fio::Flags::from_bits_retain(rights.0.bits())
45 }
46}
47
48impl Into<u64> for Rights {
49 fn into(self) -> u64 {
50 self.0.bits()
51 }
52}
53
54impl Into<fio::Operations> for Rights {
55 fn into(self) -> fio::Operations {
56 let Self(ops) = self;
57 ops
58 }
59}
60
61impl From<fio::Flags> for Rights {
62 fn from(flags: fio::Flags) -> Self {
63 Self(
64 fio::Operations::from_bits(flags.bits())
65 .expect("operations is bit-compatible with flags"),
66 )
67 }
68}
69
70impl fmt::Display for Rights {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let Self(rights) = &self;
73 match *rights {
74 fio::R_STAR_DIR => write!(f, "r*"),
75 fio::W_STAR_DIR => write!(f, "w*"),
76 fio::X_STAR_DIR => write!(f, "x*"),
77 fio::RW_STAR_DIR => write!(f, "rw*"),
78 fio::RX_STAR_DIR => write!(f, "rx*"),
79 ops => write!(f, "{:?}", ops),
80 }
81 }
82}
83
84#[cfg(feature = "serde")]
85impl Serialize for Rights {
86 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87 where
88 S: Serializer,
89 {
90 let Self(rights) = self;
91 rights.bits().serialize(serializer)
92 }
93}
94
95#[cfg(feature = "serde")]
96impl<'de> Deserialize<'de> for Rights {
97 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98 where
99 D: Deserializer<'de>,
100 {
101 let bits: u64 = Deserialize::deserialize(deserializer)?;
102 let rights = fio::Operations::from_bits(bits)
103 .ok_or_else(|| serde::de::Error::custom("invalid value for fuchsia.io/Operations"))?;
104 Ok(Self(rights))
105 }
106}
107
108pub fn validate_rights(
109 moniker: ExtendedMoniker,
110 allowed_rights: fio::Operations,
111 request: &mut RouteRequest,
112) -> Result<(), RouterError> {
113 let allowed_rights = Rights::from(allowed_rights);
114 let inherit = request.inherit_rights.ok_or_else(|| RoutingError::RouteRequestMissingField {
115 moniker: moniker.clone(),
116 missing_field: "inherit_rights".to_string(),
117 })?;
118 let request_rights: Rights = match request.directory_rights {
119 Some(request_rights) => request_rights.into(),
120 None => {
121 if inherit {
122 request.directory_rights = Some(fio::Flags::from(allowed_rights));
123 allowed_rights
124 } else {
125 Err(RoutingError::RouteRequestMissingField {
126 moniker: moniker.clone(),
127 missing_field: "directory_rights".to_string(),
128 })?
129 }
130 }
131 };
132 request_rights.validate_next(&allowed_rights, moniker.clone()).map_err(RoutingError::from)?;
133 if let Some(intermediate_rights) = request.directory_intermediate_rights {
134 Rights::from(intermediate_rights)
135 .validate_next(&allowed_rights, moniker)
136 .map_err(|e| router_error::RouterError::from(RoutingError::from(e)))?;
137 };
138 Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use assert_matches::assert_matches;
145
146 #[test]
147 fn validate_next() {
148 assert_matches!(
149 Rights(fio::Operations::empty())
150 .validate_next(&Rights(fio::R_STAR_DIR,), ExtendedMoniker::ComponentManager),
151 Ok(())
152 );
153 assert_matches!(
154 Rights(fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES,)
155 .validate_next(&Rights(fio::R_STAR_DIR), ExtendedMoniker::ComponentManager),
156 Ok(())
157 );
158 let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
159 assert_eq!(
160 Rights(fio::R_STAR_DIR)
161 .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
162 Err(RightsRoutingError::Invalid {
163 moniker: ExtendedMoniker::ComponentManager,
164 requested: Rights::from(fio::R_STAR_DIR),
165 provided: Rights::from(provided),
166 })
167 );
168 let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
169 assert_eq!(
170 Rights(fio::Operations::WRITE_BYTES)
171 .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
172 Err(RightsRoutingError::Invalid {
173 moniker: ExtendedMoniker::ComponentManager,
174 requested: Rights::from(fio::Operations::WRITE_BYTES),
175 provided: Rights::from(provided),
176 })
177 );
178 }
179}