Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
middleware.rs 4.24 KiB
Newer Older
  • Learn to ignore specific revisions
  • Pierre Jarriges's avatar
    Pierre Jarriges committed
    use crate::{
        model::{AdminAuthCredentials, Administrator},
        AppState,
    };
    
    use actix_web::{cookie::SameSite, http::Cookie, web::Form, HttpMessage, HttpRequest};
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    use wither::{bson::doc, prelude::Model};
    
    
    /// Returns a Secure actix_web::http::Cookie.
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    pub fn get_auth_cookie(name: &'static str, value: String) -> Cookie<'static> {
        Cookie::build(name, value)
            .secure(true)
            .http_only(true)
    
            .same_site(SameSite::Strict)
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
            .path("/")
            .finish()
    }
    
    /// This is not a real middleware as it is meant to be executed only after having processed the request and not before.
    /// It must be registered in the actix App instance with app_data.
    /// ```
    /// App::new()
    ///     .app_data(Data::new(AuthenticatedAdminMiddleware::new("some-auth-cookie-name")))
    /// ```
    /// If a service need to perform an authentication before doing anything, this "pseudo-middleware" should be run before anything else in the function.
    /// Example:
    /// ```
    /// #[post("/some-url")]
    /// pub async fn some_service(
    ///     app_state: Data<AppState>,
    ///     middleware: Data<AuthenticatedAdminMiddleware<'_>>,
    ///     req: HttpRequest,
    /// ) -> impl Responder {
    ///     if middleware.exec(&app_state, &req, None).await.is_err() {
    ///         return HttpResponse::Unauthorized().finish();
    ///     }
    ///     ... Authenticated action ....
    /// }
    /// ```
    #[derive(Debug, Clone)]
    pub struct AuthenticatedAdminMiddleware<'a> {
        /// The name of the authentication cookie
        pub cookie_name: &'a str,
    }
    
    impl<'a> AuthenticatedAdminMiddleware<'a> {
        pub fn new(cookie_name: &'a str) -> Self {
            AuthenticatedAdminMiddleware { cookie_name }
        }
    
        /// Performs Administrator authentication from form data with username and password
        /// Returns an authentication Cookie instance if the authentication succeeds, or an error.
        async fn try_auth_from_form_data(
            &self,
            app_state: &AppState,
            form_data: Form<AdminAuthCredentials>,
        ) -> Result<Cookie<'static>, ()> {
            match Administrator::authenticated(app_state, form_data.into_inner()).await {
                Ok(ref mut admin) => {
                    let auth_token = app_state.encryption.random_ascii_lc_string(256);
                    admin.auth_token = Some(app_state.encryption.encrypt(&auth_token));
    
                    if admin
                        .save(&app_state.db, Some(doc!("_id": admin.id().unwrap())))
                        .await
                        .is_err()
                    {
                        println!("Failed to update admin auth_token");
                        return Err(());
                    }
    
                    let cookie = get_auth_cookie("kuadrado-admin-auth", auth_token.to_owned());
    
                    return Ok(cookie);
                }
                Err(_) => return Err(()),
            }
        }
    
        /// Performs Administrator authentication from the authentication cookie value
        async fn try_auth_from_auth_cookie(
            &self,
            app_state: &AppState,
            cookie: &Cookie<'static>,
        ) -> Result<Cookie<'static>, ()> {
            match Administrator::authenticated_with_cookie(app_state, &cookie).await {
                Ok(_) => return Ok(cookie.clone()),
                Err(_) => return Err(()),
            }
        }
    
        /// The function that must be called in order to execute the verification.
        /// Example :
        /// ```
        /// #[post("/some-url")]
        /// pub async fn some_service(
        ///     app_state: Data<AppState>,
        ///     middleware: Data<AuthenticatedAdminMiddleware<'_>>,
        ///     req: HttpRequest,
        /// ) -> impl Responder {
        ///     if middleware.exec(&app_state, &req, None).await.is_err() {
        ///         return HttpResponse::Unauthorized().finish();
        ///     }
        ///     ... Authenticated actions ....
        /// }
        /// ```
        pub async fn exec(
            &self,
            app_state: &AppState,
            req: &HttpRequest,
            auth_form_data: Option<Form<AdminAuthCredentials>>,
        ) -> Result<Cookie<'static>, ()> {
            let auth_cookie = req.cookie(self.cookie_name);
            if let Some(form_data) = auth_form_data {
                return self.try_auth_from_form_data(app_state, form_data).await;
            } else if let Some(cookie) = auth_cookie {
                return self.try_auth_from_auth_cookie(app_state, &cookie).await;
            } else {
                return Err(());
            }
        }
    }