routing/bedrock/
with_rights.rs

1// Copyright 2024 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::bedrock::request_metadata::{InheritRights, IntermediateRights, Metadata};
6use crate::error::RoutingError;
7use crate::rights::{Rights, RightsWalker};
8use crate::walk_state::WalkStateUnit;
9use async_trait::async_trait;
10use moniker::ExtendedMoniker;
11use router_error::RouterError;
12use sandbox::{CapabilityBound, Request, Routable, Router, RouterResponse, WeakInstanceToken};
13
14struct RightsRouter<T: CapabilityBound> {
15    router: Router<T>,
16    rights: Rights,
17    moniker: ExtendedMoniker,
18}
19
20#[async_trait]
21impl<T: CapabilityBound> Routable<T> for RightsRouter<T> {
22    async fn route(
23        &self,
24        request: Option<Request>,
25        debug: bool,
26        target: WeakInstanceToken,
27    ) -> Result<RouterResponse<T>, router_error::RouterError> {
28        let request = request.ok_or(RouterError::InvalidArgs)?;
29        let RightsRouter { router, rights, moniker } = self;
30        let InheritRights(inherit) =
31            request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
32        let request_rights: Rights = match request.metadata.get_metadata() {
33            Some(request_rights) => request_rights,
34            None => {
35                if inherit {
36                    request.metadata.set_metadata(*rights);
37                    *rights
38                } else {
39                    Err(RouterError::InvalidArgs)?
40                }
41            }
42        };
43        let intermediate_rights: Option<IntermediateRights> = request.metadata.get_metadata();
44        let request_rights = RightsWalker::new(request_rights, moniker.clone());
45        let router_rights = RightsWalker::new(*rights, moniker.clone());
46        // The rights of the previous step (if any) of the route must be
47        // compatible with this step of the route.
48        if let Some(IntermediateRights(intermediate_rights)) = intermediate_rights {
49            let intermediate_rights_walker =
50                RightsWalker::new(intermediate_rights, moniker.clone());
51            intermediate_rights_walker
52                .validate_next(&router_rights)
53                .map_err(|e| router_error::RouterError::from(RoutingError::from(e)))?;
54        };
55        request.metadata.set_metadata(IntermediateRights(*rights));
56        // The rights of the request must be compatible with the
57        // rights of this step of the route.
58        match request_rights.validate_next(&router_rights) {
59            Ok(()) => router.route(Some(request), debug, target).await,
60            Err(e) => Err(RoutingError::from(e).into()),
61        }
62    }
63}
64
65pub trait WithRights {
66    /// Returns a router that ensures the capability request does not request
67    /// greater rights than provided at this stage of the route.
68    fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self;
69}
70
71impl<T: CapabilityBound> WithRights for Router<T> {
72    fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self {
73        Router::<T>::new(RightsRouter { rights, router: self, moniker: moniker.into() })
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use assert_matches::assert_matches;
81    use fidl_fuchsia_io as fio;
82    use router_error::{DowncastErrorForTest, RouterError};
83    use sandbox::{Data, Dict, WeakInstanceToken};
84    use std::sync::Arc;
85
86    #[derive(Debug)]
87    struct FakeComponentToken {}
88
89    impl FakeComponentToken {
90        fn new() -> WeakInstanceToken {
91            WeakInstanceToken { inner: Arc::new(FakeComponentToken {}) }
92        }
93    }
94
95    impl sandbox::WeakInstanceTokenAny for FakeComponentToken {
96        fn as_any(&self) -> &dyn std::any::Any {
97            self
98        }
99    }
100
101    #[fuchsia::test]
102    async fn rights_good() {
103        let source = Data::String("hello".into());
104        let base = Router::<Data>::new_ok(source);
105        let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::RW_STAR_DIR.into());
106        let metadata = Dict::new();
107        metadata.set_metadata(InheritRights(false));
108        metadata.set_metadata(Into::<Rights>::into(fio::R_STAR_DIR));
109        let capability = proxy
110            .route(Some(Request { metadata }), false, FakeComponentToken::new())
111            .await
112            .unwrap();
113        let capability = match capability {
114            RouterResponse::<Data>::Capability(d) => d,
115            c => panic!("Bad enum {:#?}", c),
116        };
117        assert_eq!(capability, Data::String("hello".into()));
118    }
119
120    #[fuchsia::test]
121    async fn rights_bad() {
122        let source = Data::String("hello".into());
123        let base = Router::<Data>::new_ok(source);
124        let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::R_STAR_DIR.into());
125        let metadata = Dict::new();
126        metadata.set_metadata(InheritRights(false));
127        metadata.set_metadata(Into::<Rights>::into(fio::RW_STAR_DIR));
128        let error = proxy
129            .route(Some(Request { metadata }), false, FakeComponentToken::new())
130            .await
131            .unwrap_err();
132        assert_matches!(
133            error,
134            RouterError::NotFound(err)
135            if matches!(
136                err.downcast_for_test::<RoutingError>(),
137                RoutingError::RightsRoutingError(
138                    crate::error::RightsRoutingError::Invalid { moniker: ExtendedMoniker::ComponentManager, requested, provided }
139                ) if *requested == <fio::Operations as Into<Rights>>::into(fio::RW_STAR_DIR) && *provided == <fio::Operations as Into<Rights>>::into(fio::R_STAR_DIR)
140            )
141        );
142    }
143
144    #[fuchsia::test]
145    async fn invalid_intermediate_rights() {
146        let source = Data::String("hello".into());
147        let base = Router::<Data>::new_ok(source)
148            .with_rights(ExtendedMoniker::ComponentManager, fio::R_STAR_DIR.into());
149        let intermediate =
150            base.with_rights(ExtendedMoniker::ComponentManager, fio::RW_STAR_DIR.into());
151        let metadata = Dict::new();
152        metadata.set_metadata(InheritRights(false));
153        metadata.set_metadata(Into::<Rights>::into(fio::R_STAR_DIR));
154        let error = intermediate
155            .route(Some(Request { metadata }), false, FakeComponentToken::new())
156            .await
157            .unwrap_err();
158        assert_matches!(
159            error,
160            RouterError::NotFound(err)
161            if matches!(
162                err.downcast_for_test::<RoutingError>(),
163                RoutingError::RightsRoutingError(
164                    crate::error::RightsRoutingError::Invalid { moniker: ExtendedMoniker::ComponentManager, requested, provided }
165                ) if *requested == <fio::Operations as Into<Rights>>::into(fio::RW_STAR_DIR) && *provided == <fio::Operations as Into<Rights>>::into(fio::R_STAR_DIR)
166            )
167        );
168    }
169}