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