routing/bedrock/
with_rights.rs1use crate::error::RoutingError;
6use crate::rights::Rights;
7use async_trait::async_trait;
8use capability_source::CapabilitySource;
9use fidl_fuchsia_component_runtime::RouteRequest;
10use fidl_fuchsia_io as fio;
11use moniker::ExtendedMoniker;
12use router_error::RouterError;
13use runtime_capabilities::{CapabilityBound, Routable, Router, WeakInstanceToken};
14
15struct RightsRouter<T: CapabilityBound> {
16 router: Router<T>,
17 rights: Rights,
18 moniker: ExtendedMoniker,
19}
20
21impl<T: CapabilityBound> RightsRouter<T> {
22 fn check_and_compute_rights(
23 &self,
24 mut request: RouteRequest,
25 ) -> Result<RouteRequest, RouterError> {
26 if request == RouteRequest::default() {
27 return Err(RouterError::InvalidArgs);
28 }
29 let RightsRouter { router: _, rights, moniker } = self;
30 let inherit = request.inherit_rights.ok_or(RouterError::InvalidArgs)?;
31 let request_rights: Rights = match request.directory_rights {
32 Some(request_rights) => request_rights.into(),
33 None => {
34 if inherit {
35 request.directory_rights = Some(fio::Flags::from(*rights));
36 *rights
37 } else {
38 Err(RouterError::InvalidArgs)?
39 }
40 }
41 };
42 if let Some(intermediate_rights) = request.directory_intermediate_rights {
45 Rights::from(intermediate_rights)
46 .validate_next(&rights, moniker.clone().into())
47 .map_err(|e| router_error::RouterError::from(RoutingError::from(e)))?;
48 };
49 request.directory_intermediate_rights = Some(fio::Flags::from(*rights));
50 match request_rights.validate_next(&rights, moniker.clone().into()) {
53 Ok(()) => Ok(request),
54 Err(e) => Err(RoutingError::from(e).into()),
55 }
56 }
57}
58
59#[async_trait]
60impl<T: CapabilityBound> Routable<T> for RightsRouter<T> {
61 async fn route(
62 &self,
63 request: RouteRequest,
64 target: WeakInstanceToken,
65 ) -> Result<Option<T>, RouterError> {
66 let request = self.check_and_compute_rights(request)?;
67 self.router.route(request, target).await
68 }
69
70 async fn route_debug(
71 &self,
72 request: RouteRequest,
73 target: WeakInstanceToken,
74 ) -> Result<CapabilitySource, RouterError> {
75 let request = self.check_and_compute_rights(request)?;
76 self.router.route_debug(request, target).await
77 }
78}
79
80pub trait WithRights {
81 fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self;
84}
85
86impl<T: CapabilityBound> WithRights for Router<T> {
87 fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self {
88 Router::<T>::new(RightsRouter { rights, router: self, moniker: moniker.into() })
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use assert_matches::assert_matches;
96 use fidl_fuchsia_io as fio;
97 use router_error::RouterError;
98 use runtime_capabilities::{Data, WeakInstanceToken};
99 use std::sync::Arc;
100
101 #[derive(Debug)]
102 struct FakeComponentToken {}
103
104 impl FakeComponentToken {
105 fn new() -> WeakInstanceToken {
106 WeakInstanceToken { inner: Arc::new(FakeComponentToken {}) }
107 }
108 }
109
110 impl runtime_capabilities::WeakInstanceTokenAny for FakeComponentToken {
111 fn as_any(&self) -> &dyn std::any::Any {
112 self
113 }
114 }
115
116 #[fuchsia::test]
117 async fn rights_good() {
118 let source = Data::String("hello".into());
119 let base = Router::<Data>::new_ok(source);
120 let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::RW_STAR_DIR.into());
121 let request = RouteRequest {
122 directory_rights: Some(fio::PERM_READABLE),
123 inherit_rights: Some(false),
124 ..Default::default()
125 };
126 let capability = proxy.route(request, FakeComponentToken::new()).await.unwrap();
127 let capability = match capability {
128 Some(d) => d,
129 c => panic!("Bad enum {:#?}", c),
130 };
131 assert_eq!(capability, Data::String("hello".into()));
132 }
133
134 #[fuchsia::test]
135 async fn rights_bad() {
136 let source = Data::String("hello".into());
137 let base = Router::<Data>::new_ok(source);
138 let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::R_STAR_DIR.into());
139 let request = RouteRequest {
140 directory_rights: Some(fio::PERM_READABLE | fio::PERM_WRITABLE),
141 inherit_rights: Some(false),
142 ..Default::default()
143 };
144 let error = proxy.route(request, FakeComponentToken::new()).await.unwrap_err();
145 assert_matches!(
146 error,
147 RouterError::NotFound(err)
148 if matches!(
149 err.as_any().downcast_ref::<RoutingError>(),
150 Some(RoutingError::RightsRoutingError(
151 crate::error::RightsRoutingError::Invalid { moniker: ExtendedMoniker::ComponentManager, requested, provided }
152 )) if *requested == <fio::Operations as Into<Rights>>::into(fio::RW_STAR_DIR) && *provided == <fio::Operations as Into<Rights>>::into(fio::R_STAR_DIR)
153 )
154 );
155 }
156
157 #[fuchsia::test]
158 async fn invalid_intermediate_rights() {
159 let source = Data::String("hello".into());
160 let base = Router::<Data>::new_ok(source)
161 .with_rights(ExtendedMoniker::ComponentManager, fio::R_STAR_DIR.into());
162 let intermediate =
163 base.with_rights(ExtendedMoniker::ComponentManager, fio::RW_STAR_DIR.into());
164 let request = RouteRequest {
165 directory_rights: Some(fio::PERM_READABLE),
166 inherit_rights: Some(false),
167 ..Default::default()
168 };
169 let error = intermediate.route(request, FakeComponentToken::new()).await.unwrap_err();
170 assert_matches!(
171 error,
172 RouterError::NotFound(err)
173 if matches!(
174 err.as_any().downcast_ref::<RoutingError>(),
175 Some(RoutingError::RightsRoutingError(
176 crate::error::RightsRoutingError::Invalid { moniker: ExtendedMoniker::ComponentManager, requested, provided }
177 )) if *requested == <fio::Operations as Into<Rights>>::into(fio::RW_STAR_DIR) && *provided == <fio::Operations as Into<Rights>>::into(fio::R_STAR_DIR)
178 )
179 );
180 }
181}