Skip to content

Commit

Permalink
refactor(s3s): split access from auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Nugine committed Oct 10, 2024
1 parent da6b55f commit f59a330
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::Credentials;

use crate::auth::Credentials;
use crate::path::S3Path;
use crate::S3Operation;

Expand All @@ -8,7 +7,7 @@ use hyper::HeaderMap;
use hyper::Method;
use hyper::Uri;

pub struct S3AuthContext<'a> {
pub struct S3AccessContext<'a> {
pub(crate) credentials: Option<&'a Credentials>,
pub(crate) s3_path: &'a S3Path,
pub(crate) s3_op: &'a S3Operation,
Expand All @@ -20,7 +19,7 @@ pub struct S3AuthContext<'a> {
pub(crate) extensions: &'a mut Extensions,
}

impl S3AuthContext<'_> {
impl S3AccessContext<'_> {
/// Returns the credentials of current request.
///
/// `None` means anonymous request.
Expand Down
32 changes: 32 additions & 0 deletions crates/s3s/src/access/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
mod context;
pub use self::context::S3AccessContext;

use crate::error::S3Result;

#[async_trait::async_trait]
pub trait S3Access: Send + Sync + 'static {
/// Checks whether the current request have accesses to the resources.
///
/// This method is called before deserializing the operation input.
///
/// By default, this method rejects all anonymous requests
/// and returns [`AccessDenied`](crate::S3ErrorCode::AccessDenied) error.
///
/// An access control provider can override this method to implement custom logic.
///
/// Common fields in the context:
/// + [`cx.credentials()`](S3AccessContext::credentials)
/// + [`cx.s3_path()`](S3AccessContext::s3_path)
/// + [`cx.s3_op().name()`](crate::S3Operation::name)
/// + [`cx.extensions_mut()`](S3AccessContext::extensions_mut)
async fn check(&self, cx: &mut S3AccessContext<'_>) -> S3Result<()> {
default_check(cx)
}
}

pub(crate) fn default_check(cx: &mut S3AccessContext<'_>) -> S3Result<()> {
match cx.credentials() {
Some(_) => Ok(()),
None => Err(s3_error!(AccessDenied, "Signature is required")),
}
}
22 changes: 0 additions & 22 deletions crates/s3s/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ pub use self::secret_key::{Credentials, SecretKey};
mod simple_auth;
pub use self::simple_auth::SimpleAuth;

mod context;
pub use self::context::S3AuthContext;

use crate::error::S3Result;

/// S3 Authentication Provider
Expand All @@ -18,23 +15,4 @@ pub trait S3Auth: Send + Sync + 'static {
///
/// This method is usually implemented as a database query.
async fn get_secret_key(&self, access_key: &str) -> S3Result<SecretKey>;

/// Checks if the current request can access the resource.
///
/// By default, this method rejects all anonymous requests
/// and returns [`AccessDenied`](crate::S3ErrorCode::AccessDenied) error.
///
/// An authentication provider can override this method to implement custom access control.
///
/// Common fields in the context:
/// + [`cx.credentials()`](S3AuthContext::credentials)
/// + [`cx.s3_path()`](S3AuthContext::s3_path)
/// + [`cx.s3_op().name()`](crate::S3Operation::name)
/// + [`cx.extensions_mut()`](S3AuthContext::extensions_mut)
async fn check_access(&self, cx: &mut S3AuthContext<'_>) -> S3Result<()> {
match cx.credentials() {
Some(_) => Ok(()),
None => Err(s3_error!(AccessDenied, "Signature is required")),
}
}
}
1 change: 1 addition & 0 deletions crates/s3s/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mod sig_v2;
mod sig_v4;
mod xml;

pub mod access;
pub mod auth;
pub mod checksum;
pub mod dto;
Expand Down
12 changes: 8 additions & 4 deletions crates/s3s/src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ mod get_object;
#[cfg(test)]
mod tests;

use crate::access::{S3Access, S3AccessContext};
use crate::auth::S3Auth;
use crate::auth::S3AuthContext;
use crate::error::*;
use crate::header;
use crate::host::S3Host;
Expand Down Expand Up @@ -49,6 +49,7 @@ pub struct CallContext<'a> {
pub s3: &'a Arc<dyn S3>,
pub host: Option<&'a dyn S3Host>,
pub auth: Option<&'a dyn S3Auth>,
pub access: Option<&'a dyn S3Access>,
}

fn build_s3_request<T>(input: T, req: &mut Request) -> S3Request<T> {
Expand Down Expand Up @@ -320,8 +321,8 @@ async fn prepare(req: &mut Request, ccx: &CallContext<'_>) -> S3Result<&'static

debug!(op = %op.name(), ?s3_path, "resolved route");

if let Some(auth) = ccx.auth {
let mut cx = S3AuthContext {
{
let mut acx = S3AccessContext {
credentials: req.s3ext.credentials.as_ref(),
s3_path,
s3_op: &crate::S3Operation { name: op.name() },
Expand All @@ -330,7 +331,10 @@ async fn prepare(req: &mut Request, ccx: &CallContext<'_>) -> S3Result<&'static
headers: &req.headers,
extensions: &mut req.extensions,
};
auth.check_access(&mut cx).await?;
match ccx.access {
Some(access) => access.check(&mut acx).await?,
None => crate::access::default_check(&mut acx)?,
}
}

debug!(op = %op.name(), ?s3_path, "checked access");
Expand Down
12 changes: 11 additions & 1 deletion crates/s3s/src/service.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::access::S3Access;
use crate::auth::S3Auth;
use crate::error::{S3Error, S3Result};
use crate::host::S3Host;
Expand All @@ -17,15 +18,17 @@ pub struct S3ServiceBuilder {
s3: Arc<dyn S3>,
host: Option<Box<dyn S3Host>>,
auth: Option<Box<dyn S3Auth>>,
access: Option<Box<dyn S3Access>>,
}

impl S3ServiceBuilder {
#[must_use]
pub fn new(s3: impl S3) -> Self {
Self {
s3: Arc::new(s3),
auth: None,
host: None,
auth: None,
access: None,
}
}

Expand All @@ -37,12 +40,17 @@ impl S3ServiceBuilder {
self.auth = Some(Box::new(auth));
}

pub fn set_access(&mut self, access: impl S3Access) {
self.access = Some(Box::new(access));
}

#[must_use]
pub fn build(self) -> S3Service {
S3Service {
s3: self.s3,
host: self.host,
auth: self.auth,
access: self.access,
}
}
}
Expand All @@ -51,6 +59,7 @@ pub struct S3Service {
s3: Arc<dyn S3>,
host: Option<Box<dyn S3Host>>,
auth: Option<Box<dyn S3Auth>>,
access: Option<Box<dyn S3Access>>,
}

impl S3Service {
Expand All @@ -68,6 +77,7 @@ impl S3Service {
s3: &self.s3,
host: self.host.as_deref(),
auth: self.auth.as_deref(),
access: self.access.as_deref(),
};
let result = crate::ops::call(&mut req, &ccx).await.map(Into::into);

Expand Down

0 comments on commit f59a330

Please sign in to comment.