Skip to main content

unitycatalog_server/services/
mod.rs

1use std::sync::Arc;
2
3use crate::Result;
4use crate::policy::{Decision, Permission, Policy, ProvidesPolicy};
5use crate::store::{ProvidesResourceStore, ResourceStore};
6use unitycatalog_common::models::ResourceIdent;
7use unitycatalog_delta_api::coordinator::{
8    CommitCoordinator, InMemoryCommitCoordinator, ProvidesCommitCoordinator,
9};
10
11pub mod credential_vending;
12mod delta_backend;
13pub mod location;
14pub mod location_policy;
15pub(crate) mod object_store;
16// The storage byte-proxy local arm impl. Gated on `bin`, where the
17// `unitycatalog-storage-proxy` dependency (and the serve stack that mounts it) is
18// available.
19#[cfg(feature = "bin")]
20mod storage_proxy_backend;
21
22pub use location_policy::LocalStoragePolicy;
23
24/// Access to the server's [`LocalStoragePolicy`].
25///
26/// Implemented by the server handler so request handlers (defined as blanket
27/// impls over a generic `T`) can reach the server-wide allowlist that governs
28/// which host paths may back a `file://` storage location. Mirrors
29/// [`ProvidesCommitCoordinator`].
30pub trait ProvidesLocalStoragePolicy {
31    fn local_storage_policy(&self) -> &LocalStoragePolicy;
32}
33
34/// Access to the server's metastore-level managed storage root.
35///
36/// This is the default managed storage location for the metastore as a whole.
37/// A managed catalog created without an explicit `storage_root` inherits this
38/// root, mirroring the Unity Catalog metastore → catalog → schema hierarchy
39/// ("if the metastore has no managed storage set, you must set one at the
40/// catalog level"). `None` means no metastore default is configured, in which
41/// case every managed catalog must carry its own `storage_root`.
42pub trait ProvidesManagedStorageRoot {
43    fn managed_storage_root(&self) -> Option<&str>;
44}
45
46#[derive(Clone)]
47pub struct ServerHandler<Cx> {
48    handler: Arc<ServerHandlerInner<Cx>>,
49}
50
51impl<Cx: Send + Sync + 'static> ServerHandler<Cx> {
52    pub fn try_new_tokio(
53        policy: Arc<dyn Policy<Cx>>,
54        store: Arc<dyn ResourceStore>,
55    ) -> Result<Self> {
56        Self::try_new_tokio_with_coordinator(
57            policy,
58            store,
59            Arc::new(InMemoryCommitCoordinator::default()),
60        )
61    }
62
63    /// Construct a handler backed by a specific [`CommitCoordinator`].
64    ///
65    /// Use this to wire a persistent coordinator (e.g. the Postgres-backed
66    /// `GraphStore`) instead of the default in-memory one.
67    pub fn try_new_tokio_with_coordinator(
68        policy: Arc<dyn Policy<Cx>>,
69        store: Arc<dyn ResourceStore>,
70        commit_coordinator: Arc<dyn CommitCoordinator>,
71    ) -> Result<Self> {
72        let handler = Arc::new(
73            ServerHandlerInner::new(policy.clone(), store.clone())
74                .with_commit_coordinator(commit_coordinator),
75        );
76        Ok(Self { handler })
77    }
78}
79
80impl<Cx: Send + Sync + 'static> ServerHandler<Cx> {
81    /// Set the allowlist governing `file://` storage locations.
82    ///
83    /// Rebuilds the inner handler with the policy attached. Call at construction
84    /// time, before the handler is cloned/shared. When unset, all local storage
85    /// is denied.
86    pub fn with_local_storage_policy(mut self, policy: impl Into<Arc<LocalStoragePolicy>>) -> Self {
87        // Rebuild the inner handler with the policy attached. All inner fields
88        // are `Arc`s, so this is a cheap reconstruction (the derived `Clone`
89        // would require `Cx: Clone`, which we don't impose).
90        let prev = &self.handler;
91        let inner = ServerHandlerInner {
92            policy: prev.policy.clone(),
93            store: prev.store.clone(),
94            commit_coordinator: prev.commit_coordinator.clone(),
95            local_storage_policy: policy.into(),
96            managed_storage_root: prev.managed_storage_root.clone(),
97        };
98        self.handler = Arc::new(inner);
99        self
100    }
101
102    /// Set the metastore-level managed storage root.
103    ///
104    /// Rebuilds the inner handler with the root attached. Call at construction
105    /// time, before the handler is cloned/shared. When unset, managed catalogs
106    /// must each supply their own `storage_root`.
107    pub fn with_managed_storage_root(mut self, root: Option<impl Into<Arc<str>>>) -> Self {
108        let prev = &self.handler;
109        let inner = ServerHandlerInner {
110            policy: prev.policy.clone(),
111            store: prev.store.clone(),
112            commit_coordinator: prev.commit_coordinator.clone(),
113            local_storage_policy: prev.local_storage_policy.clone(),
114            managed_storage_root: root.map(Into::into),
115        };
116        self.handler = Arc::new(inner);
117        self
118    }
119}
120
121#[derive(Clone)]
122pub struct ServerHandlerInner<Cx> {
123    policy: Arc<dyn Policy<Cx>>,
124    store: Arc<dyn ResourceStore>,
125    /// Delta catalog-managed commit coordinator (in-memory by default).
126    commit_coordinator: Arc<dyn CommitCoordinator>,
127    /// Allowlist governing which host paths may back a `file://` storage
128    /// location. Deny-all by default (see [`LocalStoragePolicy`]).
129    local_storage_policy: Arc<LocalStoragePolicy>,
130    /// Metastore-level managed storage root inherited by managed catalogs that
131    /// omit `storage_root`. `None` ⇒ no metastore default (see
132    /// [`ProvidesManagedStorageRoot`]).
133    managed_storage_root: Option<Arc<str>>,
134}
135
136impl<Cx: Send + Sync + 'static> ServerHandlerInner<Cx> {
137    pub fn new(policy: Arc<dyn Policy<Cx>>, store: Arc<dyn ResourceStore>) -> Self {
138        Self {
139            policy,
140            store,
141            commit_coordinator: Arc::new(InMemoryCommitCoordinator::default()),
142            // Deny all local (file://) storage until a policy is configured.
143            local_storage_policy: Arc::new(LocalStoragePolicy::deny_all()),
144            // No metastore-level managed storage root by default.
145            managed_storage_root: None,
146        }
147    }
148
149    /// Set the allowlist governing `file://` storage locations.
150    ///
151    /// When unset, the handler denies every local storage path.
152    pub fn with_local_storage_policy(mut self, policy: impl Into<Arc<LocalStoragePolicy>>) -> Self {
153        self.local_storage_policy = policy.into();
154        self
155    }
156
157    /// Set the metastore-level managed storage root.
158    ///
159    /// When unset, managed catalogs must each supply their own `storage_root`.
160    pub fn with_managed_storage_root(mut self, root: Option<impl Into<Arc<str>>>) -> Self {
161        self.managed_storage_root = root.map(Into::into);
162        self
163    }
164
165    /// Override the Delta commit coordinator (e.g. a Postgres-backed one, or a
166    /// custom unbackfilled cap).
167    pub fn with_commit_coordinator(mut self, coordinator: Arc<dyn CommitCoordinator>) -> Self {
168        self.commit_coordinator = coordinator;
169        self
170    }
171}
172
173impl<Cx: Send + Sync + 'static> ProvidesPolicy<Cx> for ServerHandlerInner<Cx> {
174    fn policy(&self) -> &Arc<dyn Policy<Cx>> {
175        &self.policy
176    }
177}
178
179impl<Cx: Send + Sync + 'static> ProvidesPolicy<Cx> for ServerHandler<Cx> {
180    fn policy(&self) -> &Arc<dyn Policy<Cx>> {
181        &self.handler.policy
182    }
183}
184
185#[async_trait::async_trait]
186impl<Cx: Send + Sync + 'static> Policy<Cx> for ServerHandlerInner<Cx> {
187    async fn authorize(
188        &self,
189        resource: &ResourceIdent,
190        permission: &Permission,
191        context: &Cx,
192    ) -> Result<Decision> {
193        self.policy().authorize(resource, permission, context).await
194    }
195
196    async fn authorize_many(
197        &self,
198        resources: &[ResourceIdent],
199        permission: &Permission,
200        context: &Cx,
201    ) -> Result<Vec<Decision>> {
202        self.policy()
203            .authorize_many(resources, permission, context)
204            .await
205    }
206}
207
208#[async_trait::async_trait]
209impl<Cx: Send + Sync + 'static> Policy<Cx> for ServerHandler<Cx> {
210    async fn authorize(
211        &self,
212        resource: &ResourceIdent,
213        permission: &Permission,
214        context: &Cx,
215    ) -> Result<Decision> {
216        self.handler
217            .policy
218            .authorize(resource, permission, context)
219            .await
220    }
221
222    async fn authorize_many(
223        &self,
224        resources: &[ResourceIdent],
225        permission: &Permission,
226        context: &Cx,
227    ) -> Result<Vec<Decision>> {
228        self.handler
229            .policy
230            .authorize_many(resources, permission, context)
231            .await
232    }
233}
234
235impl<Cx: Send + Sync + 'static> ProvidesResourceStore for ServerHandlerInner<Cx> {
236    fn store(&self) -> &dyn ResourceStore {
237        self.store.as_ref()
238    }
239}
240
241impl<Cx: Send + Sync + 'static> ProvidesResourceStore for ServerHandler<Cx> {
242    fn store(&self) -> &dyn ResourceStore {
243        self.handler.store.as_ref()
244    }
245}
246
247impl<Cx: Send + Sync + 'static> ProvidesCommitCoordinator for ServerHandlerInner<Cx> {
248    fn commit_coordinator(&self) -> &dyn CommitCoordinator {
249        self.commit_coordinator.as_ref()
250    }
251}
252
253impl<Cx: Send + Sync + 'static> ProvidesCommitCoordinator for ServerHandler<Cx> {
254    fn commit_coordinator(&self) -> &dyn CommitCoordinator {
255        self.handler.commit_coordinator.as_ref()
256    }
257}
258
259impl<Cx: Send + Sync + 'static> ProvidesLocalStoragePolicy for ServerHandlerInner<Cx> {
260    fn local_storage_policy(&self) -> &LocalStoragePolicy {
261        self.local_storage_policy.as_ref()
262    }
263}
264
265impl<Cx: Send + Sync + 'static> ProvidesLocalStoragePolicy for ServerHandler<Cx> {
266    fn local_storage_policy(&self) -> &LocalStoragePolicy {
267        self.handler.local_storage_policy.as_ref()
268    }
269}
270
271impl<Cx: Send + Sync + 'static> ProvidesManagedStorageRoot for ServerHandlerInner<Cx> {
272    fn managed_storage_root(&self) -> Option<&str> {
273        self.managed_storage_root.as_deref()
274    }
275}
276
277impl<Cx: Send + Sync + 'static> ProvidesManagedStorageRoot for ServerHandler<Cx> {
278    fn managed_storage_root(&self) -> Option<&str> {
279        self.handler.managed_storage_root.as_deref()
280    }
281}