Loading crates/s3s/src/auth/context.rs 0 → 100644 +60 −0 Original line number Diff line number Diff line use super::Credentials; use crate::path::S3Path; use hyper::http::Extensions; use hyper::HeaderMap; use hyper::Method; use hyper::Uri; pub struct S3AuthContext<'a> { pub(crate) credentials: Option<&'a Credentials>, pub(crate) s3_path: &'a S3Path, pub(crate) method: &'a Method, pub(crate) uri: &'a Uri, pub(crate) headers: &'a HeaderMap, pub(crate) extensions: &'a mut Extensions, } impl<'a> S3AuthContext<'a> { /// Returns the credentials of current request. /// /// `None` means anonymous request. #[must_use] pub fn credentials(&self) -> Option<&Credentials> { self.credentials } /// Returns the S3 path of current request. /// /// An S3 path can be root, bucket, or object. #[must_use] pub fn s3_path(&self) -> &S3Path { self.s3_path } #[must_use] pub fn method(&self) -> &Method { self.method } #[must_use] pub fn uri(&self) -> &Uri { self.uri } #[must_use] pub fn headers(&self) -> &HeaderMap { self.headers } /// Returns the extensions of current request. /// /// It is used to pass custom data between middlewares. #[must_use] pub fn extensions_mut(&mut self) -> &mut Extensions { self.extensions } } crates/s3s/src/auth/mod.rs +24 −1 Original line number Diff line number Diff line Loading @@ -9,11 +9,34 @@ pub use self::simple_auth::SimpleAuth; mod credentials; pub use self::credentials::Credentials; mod context; pub use self::context::S3AuthContext; use crate::error::S3Result; /// S3 Authentication Provider #[async_trait::async_trait] pub trait S3Auth: Send + Sync + 'static { /// lookup `secret_key` by `access_key` /// Gets the corresponding secret key of the access key. /// /// 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.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")), } } } crates/s3s/src/ops/mod.rs +13 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ mod get_object; mod tests; use crate::auth::S3Auth; use crate::auth::S3AuthContext; use crate::error::*; use crate::header; use crate::http; Loading Loading @@ -245,6 +246,18 @@ async fn prepare(req: &mut Request, auth: Option<&dyn S3Auth>, base_domain: Opti req.s3ext.credentials = credentials; } if let Some(auth) = auth { let mut cx = S3AuthContext { credentials: req.s3ext.credentials.as_ref(), s3_path, method: &req.method, uri: &req.uri, headers: &req.headers, extensions: &mut req.extensions, }; auth.check_access(&mut cx).await?; } if body_changed { // invalidate the original content length if let Some(val) = req.headers.get_mut(header::CONTENT_LENGTH) { Loading crates/s3s/src/ops/signature.rs +2 −6 Original line number Diff line number Diff line Loading @@ -81,16 +81,12 @@ impl SignatureContext<'_> { pub async fn check(&mut self) -> S3Result<Option<Credentials>> { if let Some(result) = self.v2_check().await { debug!("checked signature v2"); return result.map(Some); return Ok(Some(result?)); } if let Some(result) = self.v4_check().await { debug!("checked signature v4"); return result.map(Some); } if self.auth.is_some() { return Err(s3_error!(AccessDenied, "Signature is required")); return Ok(Some(result?)); } Ok(None) Loading Loading
crates/s3s/src/auth/context.rs 0 → 100644 +60 −0 Original line number Diff line number Diff line use super::Credentials; use crate::path::S3Path; use hyper::http::Extensions; use hyper::HeaderMap; use hyper::Method; use hyper::Uri; pub struct S3AuthContext<'a> { pub(crate) credentials: Option<&'a Credentials>, pub(crate) s3_path: &'a S3Path, pub(crate) method: &'a Method, pub(crate) uri: &'a Uri, pub(crate) headers: &'a HeaderMap, pub(crate) extensions: &'a mut Extensions, } impl<'a> S3AuthContext<'a> { /// Returns the credentials of current request. /// /// `None` means anonymous request. #[must_use] pub fn credentials(&self) -> Option<&Credentials> { self.credentials } /// Returns the S3 path of current request. /// /// An S3 path can be root, bucket, or object. #[must_use] pub fn s3_path(&self) -> &S3Path { self.s3_path } #[must_use] pub fn method(&self) -> &Method { self.method } #[must_use] pub fn uri(&self) -> &Uri { self.uri } #[must_use] pub fn headers(&self) -> &HeaderMap { self.headers } /// Returns the extensions of current request. /// /// It is used to pass custom data between middlewares. #[must_use] pub fn extensions_mut(&mut self) -> &mut Extensions { self.extensions } }
crates/s3s/src/auth/mod.rs +24 −1 Original line number Diff line number Diff line Loading @@ -9,11 +9,34 @@ pub use self::simple_auth::SimpleAuth; mod credentials; pub use self::credentials::Credentials; mod context; pub use self::context::S3AuthContext; use crate::error::S3Result; /// S3 Authentication Provider #[async_trait::async_trait] pub trait S3Auth: Send + Sync + 'static { /// lookup `secret_key` by `access_key` /// Gets the corresponding secret key of the access key. /// /// 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.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")), } } }
crates/s3s/src/ops/mod.rs +13 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ mod get_object; mod tests; use crate::auth::S3Auth; use crate::auth::S3AuthContext; use crate::error::*; use crate::header; use crate::http; Loading Loading @@ -245,6 +246,18 @@ async fn prepare(req: &mut Request, auth: Option<&dyn S3Auth>, base_domain: Opti req.s3ext.credentials = credentials; } if let Some(auth) = auth { let mut cx = S3AuthContext { credentials: req.s3ext.credentials.as_ref(), s3_path, method: &req.method, uri: &req.uri, headers: &req.headers, extensions: &mut req.extensions, }; auth.check_access(&mut cx).await?; } if body_changed { // invalidate the original content length if let Some(val) = req.headers.get_mut(header::CONTENT_LENGTH) { Loading
crates/s3s/src/ops/signature.rs +2 −6 Original line number Diff line number Diff line Loading @@ -81,16 +81,12 @@ impl SignatureContext<'_> { pub async fn check(&mut self) -> S3Result<Option<Credentials>> { if let Some(result) = self.v2_check().await { debug!("checked signature v2"); return result.map(Some); return Ok(Some(result?)); } if let Some(result) = self.v4_check().await { debug!("checked signature v4"); return result.map(Some); } if self.auth.is_some() { return Err(s3_error!(AccessDenied, "Signature is required")); return Ok(Some(result?)); } Ok(None) Loading