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}