Unverified Commit 9649fe89 authored by Nugine's avatar Nugine
Browse files

s3s: auth: check access

parent d49f0097
Loading
Loading
Loading
Loading
+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
    }
}
+24 −1
Original line number Diff line number Diff line
@@ -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")),
        }
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
+2 −6
Original line number Diff line number Diff line
@@ -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)