mirror of
				https://gitlab.com/comunic/comunicapiv3
				synced 2025-11-04 09:34:04 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
use std::collections::HashMap;
 | 
						|
use std::str::FromStr;
 | 
						|
 | 
						|
use actix_web::{HttpRequest, HttpResponse, web};
 | 
						|
use actix_web::dev::HttpResponseBuilder;
 | 
						|
use actix_web::http::{HeaderName, HeaderValue, StatusCode};
 | 
						|
use serde::Serialize;
 | 
						|
 | 
						|
use crate::api_data::http_error::HttpError;
 | 
						|
use crate::controllers::routes::RequestResult;
 | 
						|
use crate::data::api_client::APIClient;
 | 
						|
use crate::data::base_request_handler::{BaseRequestHandler, RequestValue};
 | 
						|
use crate::data::config::conf;
 | 
						|
use crate::data::error::{Res, ResultBoxError};
 | 
						|
use crate::data::user_token::UserAccessToken;
 | 
						|
use crate::helpers::{account_helper, api_helper};
 | 
						|
 | 
						|
/// Http request handler
 | 
						|
///
 | 
						|
/// @author Pierre Hubert
 | 
						|
 | 
						|
pub struct HttpRequestHandler {
 | 
						|
    request: web::HttpRequest,
 | 
						|
    body: HashMap<String, RequestValue>,
 | 
						|
    response: Option<web::HttpResponse>,
 | 
						|
    headers: HashMap<String, String>,
 | 
						|
    client: Option<APIClient>,
 | 
						|
    curr_user_token: Option<UserAccessToken>,
 | 
						|
}
 | 
						|
 | 
						|
impl HttpRequestHandler {
 | 
						|
    /// Construct a new request handler
 | 
						|
    pub fn new(req: HttpRequest, body: HashMap<String, RequestValue>) -> HttpRequestHandler {
 | 
						|
        HttpRequestHandler {
 | 
						|
            request: req,
 | 
						|
            body,
 | 
						|
            response: None,
 | 
						|
            headers: HashMap::new(),
 | 
						|
            client: None,
 | 
						|
            curr_user_token: None,
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// Check if a response has been set for this request
 | 
						|
    pub fn has_response(&self) -> bool {
 | 
						|
        self.response.is_some()
 | 
						|
    }
 | 
						|
 | 
						|
    /// Get the response status code, eg. 200 or 404
 | 
						|
    pub fn response_status_code(&self) -> u16 {
 | 
						|
        self.response.as_ref().unwrap().status().as_u16()
 | 
						|
    }
 | 
						|
 | 
						|
    /// Take the response from this struct
 | 
						|
    pub fn response(self) -> ResultBoxError<HttpResponse> {
 | 
						|
        let mut response = self.response.unwrap();
 | 
						|
 | 
						|
        // Put additional headers if required
 | 
						|
        for (k, v) in &self.headers {
 | 
						|
            response.headers_mut().insert(HeaderName::from_str(k)?,
 | 
						|
                                          HeaderValue::from_str(v)?,
 | 
						|
            );
 | 
						|
        }
 | 
						|
 | 
						|
        Ok(response)
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /// Get the path of the request
 | 
						|
    pub fn request_path(&self) -> String {
 | 
						|
        self.request.path().to_string()
 | 
						|
    }
 | 
						|
 | 
						|
    /// Get information about the client which made the request
 | 
						|
    pub fn api_client(&self) -> &APIClient {
 | 
						|
        self.client.as_ref().unwrap()
 | 
						|
    }
 | 
						|
 | 
						|
    /// Check API client tokens
 | 
						|
    pub fn check_client_token(&mut self) -> RequestResult {
 | 
						|
        // TODO : remove fallback
 | 
						|
        let client_name = self.post_string_with_fallback("client", "serviceName")?;
 | 
						|
 | 
						|
        let client = self.ok_or_bad_request(
 | 
						|
            api_helper::get_client(&client_name),
 | 
						|
            "Client not recognized!",
 | 
						|
        )?;
 | 
						|
 | 
						|
 | 
						|
        if let Some(domain) = &client.domain {
 | 
						|
            let allowed_origin = match conf().force_https {
 | 
						|
                true => format!("https://{}", domain),
 | 
						|
                false => format!("http://{}", domain)
 | 
						|
            };
 | 
						|
 | 
						|
            match self.request.headers().get("Referer") {
 | 
						|
                None => self.bad_request("Unknown origin!".to_string())?,
 | 
						|
                Some(s) => {
 | 
						|
                    if !s.to_str()?.starts_with(&allowed_origin) {
 | 
						|
                        self.bad_request("Use of this client is prohibited from this domain!".to_string())?;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            self.headers.insert("Access-Control-Allow-Origin".to_string(), allowed_origin);
 | 
						|
        }
 | 
						|
 | 
						|
        self.client = Some(client);
 | 
						|
 | 
						|
        Ok(())
 | 
						|
    }
 | 
						|
 | 
						|
    /// Check login token
 | 
						|
    pub fn check_user_token(&mut self) -> Res {
 | 
						|
        // TODO : remove fallback
 | 
						|
        let token = self.post_string_with_fallback("token", "userToken1")?;
 | 
						|
 | 
						|
        // Find user
 | 
						|
        match account_helper::find_user_by_login_token(&token, self.api_client()) {
 | 
						|
            Ok(token) => {
 | 
						|
                if token.need_refresh() {
 | 
						|
                    account_helper::refresh_access_token(&token)?;
 | 
						|
                }
 | 
						|
 | 
						|
                self.curr_user_token = Some(token);
 | 
						|
 | 
						|
                Ok(())
 | 
						|
            }
 | 
						|
            Err(e) => {
 | 
						|
                println!("Error marking login tokens as invalid: {}", e);
 | 
						|
                self.response = Some(
 | 
						|
                    actix_web::HttpResponse::
 | 
						|
                    build(actix_web::http::StatusCode::from_u16(412)?)
 | 
						|
                        .json(HttpError::new(412, "Please check your login tokens!")));
 | 
						|
                Err(e)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl BaseRequestHandler for HttpRequestHandler {
 | 
						|
    /// Get request parameter
 | 
						|
    fn post_parameter_opt(&self, name: &str) -> Option<&RequestValue> {
 | 
						|
        self.body.get(name)
 | 
						|
    }
 | 
						|
 | 
						|
    /// Set request response
 | 
						|
    fn set_response<T: Serialize>(&mut self, data: T) -> RequestResult {
 | 
						|
        self.response = Some(HttpResponse::Ok().json(data));
 | 
						|
        Ok(())
 | 
						|
    }
 | 
						|
 | 
						|
    /// Set request error
 | 
						|
    fn set_error(&mut self, error: HttpError) {
 | 
						|
        self.response = Some(HttpResponseBuilder::new(StatusCode::from_u16(error.error.code).unwrap())
 | 
						|
            .json(error));
 | 
						|
    }
 | 
						|
 | 
						|
    /// Get the remote IP address
 | 
						|
    fn remote_ip(&self) -> String {
 | 
						|
        let mut ip = self.request.peer_addr().unwrap().ip().to_string();
 | 
						|
 | 
						|
        // We check if the request comes from a trusted reverse proxy
 | 
						|
        if let Some(proxy) = conf().proxy.as_ref() {
 | 
						|
            if ip.eq(proxy) {
 | 
						|
                if let Some(header) = self.request.headers().get("X-Forwarded-For") {
 | 
						|
                    let header: Vec<String> = header
 | 
						|
                        .to_str()
 | 
						|
                        .unwrap()
 | 
						|
                        .to_string()
 | 
						|
                        .split(",")
 | 
						|
                        .map(|f| f.to_string())
 | 
						|
                        .collect();
 | 
						|
 | 
						|
                    if header.len() > 0 {
 | 
						|
                        ip = header[0].to_string();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        ip
 | 
						|
    }
 | 
						|
 | 
						|
    fn user_access_token(&self) -> Option<&UserAccessToken> {
 | 
						|
        self.curr_user_token.as_ref()
 | 
						|
    }
 | 
						|
} |