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