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::ResultBoxError; use crate::data::user::UserID; use crate::helpers::{account_helper, api_helper}; /// Http request handler /// /// @author Pierre Hubert pub struct HttpRequestHandler { request: web::HttpRequest, body: HashMap, response: Option, headers: HashMap, client: Option, curr_user_id: Option, } impl HttpRequestHandler { /// Construct a new request handler pub fn new(req: HttpRequest, body: HashMap) -> HttpRequestHandler { HttpRequestHandler { request: req, body, response: None, headers: HashMap::new(), client: None, curr_user_id: 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 { 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 { let api_name = self.post_string("serviceName")?; let api_token = self.post_string("serviceToken")?; let client = self.ok_or_bad_request( api_helper::get_client(&api_name, &api_token), "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) -> RequestResult { let token = self.post_string("userToken1")?; // Find user match account_helper::get_user_by_login_token(&token, self.api_client()) { Ok(id) => { self.curr_user_id = Some(id); 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(&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 = 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_id_opt_ref(&self) -> Option<&UserID> { self.curr_user_id.as_ref() } }