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
view_resource.rs 6.09 KiB
Newer Older
Pierre Jarriges's avatar
Pierre Jarriges committed
use crate::{
    middleware::AuthenticatedAdminMiddleware,
    model::AdminAuthCredentials,
    // view_resource::{ViewResource, ViewResourceDescriptor},
    AppState,
};
use actix_web::{web::Form, HttpMessage, HttpRequest, HttpResponse};
use std::{env::var as env_var, fs::read_to_string as file_to_string, path::PathBuf};

#[derive(Debug, Clone)]
/// Loads a static resource data allowing it to be served by the get_view service.
/// It holds a name, allowing the resource to be retrived by name,
/// a content which can be any text content stored in a string (like an html document),
/// a path to the directory of the actual static resource, and a boolean which indicates wether
/// or not an authentication verification should be applied.
pub struct ViewResource {
    pub name: String,
    pub string_contents: String,
    pub dir_path: PathBuf,
    pub apply_auth_middleware: bool,
}

#[derive(Debug, Clone)]
/// Defines the values that will be used to construct a ViewResource.
/// It must be passed to the AppViewResourceManager for resource registration
pub struct ViewResourceDescriptor<'a> {
    pub path_str: &'a str,
    pub index_file_name: &'a str,
    pub resource_name: &'a str,
    pub apply_auth_middleware: bool,
}

#[derive(Debug, Clone)]
/// A structure reponsible of registering and retrieving static resources.
pub struct ViewResourceManager {
    resources: Vec<ViewResource>,
}

impl ViewResourceManager {
    pub fn new() -> Self {
        ViewResourceManager { resources: vec![] }
    }

    /// Calls the constructor and registers the resources described as argument before returning the instance
    pub fn with_views(resource_descriptors: Vec<ViewResourceDescriptor>) -> Self {
        let mut instance = Self::new();
        instance.register_batch(resource_descriptors);
        instance
    }

    /// Registers a new static resource in the instance.
    /// The path provided in the argument must point to an existing file
    pub fn register(&mut self, desc: ViewResourceDescriptor) {
        let static_dir = std::path::PathBuf::from(
            env_var("RESOURCES_DIR").expect("RESOURCES_DIR is not defined"),
        )
        .join("public/views");

        let dir_path = static_dir.join(desc.path_str);

        let path: PathBuf = format!("{}/{}", dir_path.to_str().unwrap(), desc.index_file_name)
            .parse()
            .expect(&format!(
                "Failed to pare resource index file path {:?}",
                desc.index_file_name
            ));

        let string_contents = file_to_string(path).unwrap();

Pierre Jarriges's avatar
Pierre Jarriges committed
        self.resources.push(ViewResource {
Pierre Jarriges's avatar
Pierre Jarriges committed
            name: desc.resource_name.to_string(),
            dir_path,
            string_contents,
            apply_auth_middleware: desc.apply_auth_middleware,
        });
    }

    /// Registers a collection of multiple resources.
    pub fn register_batch(&mut self, resource_descriptors: Vec<ViewResourceDescriptor>) {
        for desc in resource_descriptors.iter() {
            self.register(desc.clone());
        }
    }

    /// Retrieves a resource by name and returns a reference to it or None.
    pub fn get_resource(&self, name: &str) -> Option<&ViewResource> {
        self.resources.iter().find(|res| res.name == name)
    }

    /// Retrieves a resource by name and returns it as an http response.
    /// This can be returned as it by a service.
    pub async fn get_resource_as_http_response<'a>(
        &self,
        app_state: &AppState,
        auth_middleware: &AuthenticatedAdminMiddleware<'a>,
        req: &HttpRequest,
        auth_data: Option<Form<AdminAuthCredentials>>,
        resource_name: &str,
    ) -> HttpResponse {
        match self.get_resource(resource_name) {
            Some(res) => {
                if res.apply_auth_middleware {
                    let auth_cookie = auth_middleware.exec(app_state, req, auth_data).await;
                    if auth_cookie.is_err() {
                        let unauthorized_view = match self.get_resource("unauthorized") {
                            Some(res_404) => res_404.string_contents.to_string(),
                            None => {
                                println!("WARNING: missing Unauthorized view resource");

                                "
                                    <h1>Unauthorized</h1>
                                    <p>You must login as an administrator to access this page</p>
                                    <a href='/v/admin-login'>Login page</a>
                                "
                                .to_string()
                            }
                        };

                        let mut response_builder = HttpResponse::Unauthorized();

                        return match req.cookie(auth_middleware.cookie_name) {
                            Some(cookie) => {
                                // Invalidate auth_cookie if auth failed in any way
                                response_builder
                                    .del_cookie(&cookie)
                                    .content_type("text/html")
                                    .body(unauthorized_view)
                            }
                            None => response_builder
                                .content_type("text/html")
                                .body(unauthorized_view),
                        };
                    } else {
                        return HttpResponse::Ok()
                            .content_type("text/html")
                            .cookie(auth_cookie.unwrap())
                            .body(&res.string_contents);
                    }
                }

                HttpResponse::Ok()
                    .content_type("text/html")
                    .body(&res.string_contents)
            }
            None => match self.get_resource("404") {
                Some(res_404) => HttpResponse::NotFound()
                    .content_type("text/html")
                    .body(&res_404.string_contents),
                None => {
                    println!("WARNING: missing 404 view resource");
                    HttpResponse::NotFound().finish()
                }
            },
        }
    }
}