Skip to main content

isb_server/server/mcp/
downscope.rs

1//! An org-bound endpoint (`/orgs/<org>/...`) scopes a superadmin or platform
2//! admin down to an admin of that org ([`Principal::downscoped_to`]).
3
4use std::sync::Arc;
5
6use serde_json::Value;
7
8use super::{Caller, Endpoint, Request, Response, Tool};
9
10impl Caller {
11    /// The org an org-bound endpoint scoped this caller down to, if it did.
12    pub fn downscope(&self) -> Option<&crate::org::OrgId> {
13        self.principal().and_then(|p| p.downscoped.as_ref())
14    }
15
16    /// This caller on an org-bound endpoint (`/orgs/<org>/...`): a
17    /// superadmin over HTTP or a platform admin acts as an admin of `org`
18    /// only ([`crate::auth::Principal::downscoped_to`]). The unix socket and
19    /// everyone else are unchanged.
20    pub fn downscoped_to(self, org: &crate::org::OrgId) -> Caller {
21        match self {
22            Caller::Superadmin(s) => Caller::User {
23                principal: Arc::new(s.principal.downscoped_to(org)),
24            },
25            Caller::User { principal } => Caller::User {
26                principal: if principal.platform_admin {
27                    Arc::new(principal.downscoped_to(org))
28                } else {
29                    principal
30                },
31            },
32            c => c,
33        }
34    }
35}
36
37impl Endpoint {
38    /// What `tools/list` shows `caller`: the tools this listener's policy
39    /// allows, less what the `listed` hook hides on this endpoint.
40    pub(super) fn listed_tools(
41        &self,
42        caller: &Caller,
43        scope: Option<&crate::org::OrgId>,
44    ) -> Vec<Value> {
45        let hide = |t: &Tool| {
46            self.hooks
47                .listed
48                .as_ref()
49                .is_some_and(|l| !l(caller, &t.name, scope))
50        };
51        let all = self.registry.tools().iter();
52        all.filter(|t| self.policy.allows(&t.name) && !hide(t))
53            .map(Tool::describe)
54            .collect()
55    }
56
57    /// [`Self::authenticate`], then scoped down to the org of an org-bound
58    /// endpoint (`None`: the unbound endpoints, whose callers keep their reach).
59    pub(super) fn authenticate_in(
60        &self,
61        req: &Request,
62        scope: Option<&crate::org::OrgId>,
63    ) -> std::result::Result<Caller, Response> {
64        let caller = self.authenticate(req)?;
65        Ok(match scope {
66            Some(org) => caller.downscoped_to(org),
67            None => caller,
68        })
69    }
70}