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