1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2025-04-19 02:50:54 +00:00
comunicapiv3/src/data/http_request_handler.rs

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()
}
}