objectstore_server/auth/
service.rs

1use objectstore_service::id::{ObjectContext, ObjectId};
2use objectstore_service::{PayloadStream, StorageService};
3use objectstore_types::{Metadata, Permission};
4
5use crate::auth::AuthContext;
6use crate::endpoints::common::ApiResult;
7
8/// Wrapper around [`StorageService`] that ensures each operation is authorized.
9///
10/// Authorization is performed according to the request's authorization details, see also
11/// [`AuthContext`]. When [`crate::config::AuthZ::enforce`] is false, authorization failures are
12/// logged but any unauthorized operations are still allowed to proceed.
13///
14/// Objectstore API endpoints can use `AuthAwareService` simply by adding it to their handler
15/// function's argument list like so:
16///
17/// ```
18/// use axum::http::StatusCode;
19/// use objectstore_server::auth::AuthAwareService;
20///
21/// async fn my_endpoint(service: AuthAwareService) -> Result<StatusCode, StatusCode> {
22///     service.delete_object(todo!("pass some ID"))
23///         .await
24///         .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
25///
26///     Ok(StatusCode::NO_CONTENT)
27/// }
28/// ```
29#[derive(Debug)]
30pub struct AuthAwareService {
31    service: StorageService,
32    context: Option<AuthContext>,
33}
34
35impl AuthAwareService {
36    /// Creates a new `AuthAwareService` using the given service and auth context.
37    ///
38    /// If no auth context is provided, authorization is disabled and all operations will be
39    /// permitted.
40    pub fn new(service: StorageService, context: Option<AuthContext>) -> Self {
41        Self { service, context }
42    }
43
44    fn assert_authorized(&self, perm: Permission, context: &ObjectContext) -> ApiResult<()> {
45        if let Some(auth) = &self.context {
46            auth.assert_authorized(perm, context)?;
47        }
48
49        Ok(())
50    }
51
52    /// Auth-aware wrapper around [`StorageService::insert_object`].
53    pub async fn insert_object(
54        &self,
55        context: ObjectContext,
56        key: Option<String>,
57        metadata: &Metadata,
58        stream: PayloadStream,
59    ) -> ApiResult<ObjectId> {
60        self.assert_authorized(Permission::ObjectWrite, &context)?;
61        Ok(self
62            .service
63            .insert_object(context, key, metadata, stream)
64            .await?)
65    }
66
67    /// Auth-aware wrapper around [`StorageService::get_object`].
68    pub async fn get_object(&self, id: &ObjectId) -> ApiResult<Option<(Metadata, PayloadStream)>> {
69        self.assert_authorized(Permission::ObjectRead, id.context())?;
70        Ok(self.service.get_object(id).await?)
71    }
72
73    /// Auth-aware wrapper around [`StorageService::delete_object`].
74    pub async fn delete_object(&self, id: &ObjectId) -> ApiResult<()> {
75        self.assert_authorized(Permission::ObjectDelete, id.context())?;
76        Ok(self.service.delete_object(id).await?)
77    }
78}